Wednesday, October 19, 2011

Who is there?

Today I needed to take a database offline. Generally we send notification to all requesting to log off. But today I thought to do something differently. Before I send notification to everyone I wanted to see if anyone is using that database at all.

There are a system stored procedure sp_who which returns information regarding current sessions on given database.








There is an extended version of this stored procedure is also available.

Friday, September 23, 2011

Exception log simplified

One of very common problem is to keep log of exception/error which happens in production environment for debugging and quality improvement purpose.

There are many great logging tools/framework available in the market with many enhanced functionality. In most cases these detailed functionalities are not required.

Few weeks back, when this problem presented itself (to log exception detail to database or file) I thought let’s make things simpler.

Problem Description: Quickly log exception details without any 3rd party framework.

Conditions:

  1. Where to log exceptions details was controlled by system parameter and can be managed from UI.
  2. Exception log method should have ability to log more details if required and supplied.
Solution
  1. Method to get exception details for log
  2. Decide which logging method(s) to call
  3. Implementation of various logging methods


public static void ProcessError(Exception exception, string otherDetails = "")
{
 StackTrace stackTrace = new StackTrace();
 string methodName = stackTrace.GetFrame(1).GetMethod().Name;
 string className = stackTrace.GetFrame(1).GetMethod().DeclaringType.Name;
 string classDetail = string.Format("{0}.{1}", className, methodName);
 string exceptionDetail = string.Format("Error occurred in {0}. {1}Message:{2}", 
  classDetail,
  Environment.NewLine,
  exception.Message
 );

 if (!string.IsNullOrWhiteSpace(otherDetails))
 {
  exceptionDetail = string.Format("{0}{1}Other Details: {2}.",
   exceptionDetail,
   Environment.NewLine,
   otherDetails
  );
 }
 // send this detail for saving
 LogError(exceptionDetail);
}

Here the trick is to get Calling method and class name for logging. We used following lines to do that job

// to get name of calling method
string methodName = stackTrace.GetFrame(1).GetMethod().Name; 

// to get name of calling class
string className = stackTrace.GetFrame(1).GetMethod().DeclaringType.Name; 

Now lets decide where to save this detail



public enum LogMethods
{
 Database,
 File,
 Email
};


...
...


public static void LogError(string exceptionDetail)
{
 switch (errorLogMethod)
 {
  case LogMethods.Database:
   LogErrorInDatabase(exceptionDetail);
   break;
  case LogMethods.File:
   LogErrorInFile(exceptionDetail);
   break;
  case LogMethods.Email:
   SendErrorLog(exceptionDetail);
   break;
  case default:
   SendErrorLog(exceptionDetail);
   break;
 }
}

Now last step, implement save methods


Here is an example of saving detail in database. Similarly other methods can be implemented.


public static void logErrorMessageInDB(string exceptionDetail, string methodName)
{
 try
 {
  MyDataContext myDC = DataContext;

  Log lerror = new Log
  {
   FullDescription = exceptionDetail,
   MethodName = methodName,
   CreateDataTime = DateTime.Now
  };

  iDC.Log.InsertOnSubmit(lerror);
  iDC.SubmitChanges();
 }
 catch (Exception exp)
 {
  /*
   LogErrorInFile(exp);
   // OR
   LogErrorInEventLog(exp);
  */
 }
}

Tuesday, June 21, 2011

Publish Infopath (2010) Form on Sharepoint Server (2010)


From last few months I am working with InfoPath forms; especially for web hosted
forms. I needed to create a page to host InfoPath form. Here is a quick tutorial
to do this

Assumptions:

  1. User has required rights to perform this task
  2. Server is SharePoint 2010 Server.

We need to do few tasks

  1. Publish InfoPath form.
  2. Upload and Publish that form on SharePoint Server.
  3. Create a page to host this InfoPath form.

So let’s get started.

Publish InfoPath form.

1. 1. In InfoPath click on File Menu and select Publish.



1. 2. From various options on right side of page, Select “SharePoint Server” (Publish
form to a SharePoint Library). This will open Publishing Wizard dialog.



1. 3. In Publishing Wizard dialog type URL of SharePoint server where you want to
host this InfoPath form and click Next.



1. 4. Select “Administrator-approved form template (advanced)” option from list
and click next.



1. 5. Select location where you want to publish your InfoPath form; (Please make
sure that SharePoint admin can access this location) and hit Next.



1. 6. If you want to add new columns in SharePoint list add that here, otherwise
click Next.



1. 7. Please review information and click “Publish” if everything seems OK.



1. 8. After you click on publish it may take few minutes to update screen, Once
published you will see confirmation screen indicating that publication was successful.
Click on Close to finish “Publishing Wizard”



Upload and Publish that form on SharePoint Server

Now Our InfoPath form is ready for publication on SharePoint server. First we need
to upload InfoPath form on server. For that …

2. 1. Open SharePoint website on web browser and select “Site Settings” from Site
Actions menu. (top left side)



2. 2. a) This will open central Administration screen, From left side list, Select
“General Application Settings.”



2. 2. b) When selected (General Application Settings) you will see list of settings
options on right side, Select “Manage Form Template” from InfoPath Forms Services.
(Assuming InfoPath Forms Services is installed and available)


2. 3. You will see list of available InfoPath forms. On top of the page you will
see link for uploading form template


2. 4. From upload page click on browse and select the file we saved in Step 5 of
first section and click Upload


2. 5. Once form is uploaded you will see confirmation message. By clicking OK you
will be redirected to Template management page where you will see your form on list.


2. 6. Select your form and open options menu (when you move your cursor to right
side of forms name you will see dropdown box click there which will open options
menu) and select “Activate to a Site Collection” option.


2. 7. Select site where you want to active this form and click OK. This ends part
2


Create a page to host this InfoPath form.

Now we have InfoPath form available on SharePoint site; we need to make it accessible
to end user for data entry. First of all (assuming we don’t have one) we need to
create a new page to host InfoPath form.

3. 1. To be able to do this open SharePoint website in web browser and select “New
Page” from Site actions menu. (top left)


3. 2. Type name of form and click “Create”. Your page will be available in edit
mode.


3. 3. From ribbon menu select “Library Settings” to make that InfoPath form available
as type.


3. 4. From Library Settings->Content Types select Add existing site content types.


3. 5. From list of available types select the InfoPath from you want to add and
click on “Add” button. Once done click OK to save your changes.


3. 6. Open your (new page) in edit mode, and from Ribbon menu select Insert->Web
Part.


3. 7. From Categories select “Forms”, From Web Parts select “InfoPath Form web part”
and click “Add”


3. 8. You will see following web part on your page, Click on Link to open tool pane.


3. 9. From tool pane select “Pages” from List or Library, Select

from Content Type List.


3. 10. (optional) By default any InfoPath webpart will appear with header, In some
cases you don’t need header, to remove header expand “Appearance” section and select
“None” from Chrome Type. Which will remove header.


3. 11. Once done Click Apply and your page is ready. You can publish it (after any
other formatting related changes you may want to do)

Happy Coding !!! :)

Thursday, June 2, 2011

The power of Basics, LINQ

Few days back I needed to write a small utility program in C# to update few million (91 to be exact) records in a table.

I thought it is fairly straight forward, single user, single connection application scenario hence I used WPF and Linq to do the job.

First run and updated record number started to fly like Jet. In a minute or two it processed entire table (yes! With 91 Million records). I was surprised to see that kind of performance.

Then I want to check database to check updated records, there were none

I was scratching my head for a while and it occurred to me that there may be some problem in table structure; hence I checked table structure, to my surprise there was no primary key defined (no wonder it got processed so fast !!!).

I requested that change to our DBA, and updated code accordingly, and I started the program again

Now I started to get more believable numbers (like 200 - 500 records / second)

Lesson learned:
  • Do not assume that input supplied to you (the table in this case) is correct; Verify it before you start processing it.
  • If there is no primary key defined in the table, given table will not update any rows (and it will not complaint either)

Wednesday, March 30, 2011

Join Multiple Text Files using Dirty Operating System (DOS)


Dirty Operating System (DOS)

Few days back I needed to join few (Thousand) text file as One file. I thought of
several options

  1. Open Notepad and copy paste files one by one :(
  2. Write a program which joins files.
  3. Download and use 3rd party program like TXTCollector
  4. Try something new ...

I remembered earlier (dark) days of DOS (Dirty Operation System) when screen was
black and text was white, everything was between 32 rows and 80 columns. Few of
DOS commands were very interesting like | (more) and > or >> (pipe). I thought to
do an experiment with that. I went to DOS prompt in same directory where all .txt
files were stored and used following command

type *.txt > MergedFile.txt

and ...

Yes !!! I got combined file named MergetFile.txt which was 14GB. There are few more
tricks like this

type firstFile.txt secondFile.txt thirdFile.txt fifthFile.txt > MergedFile.txt

This will join FirstFile.txt, SecondFile.txt, ThirdFile.txt and fifthFile.txt into
MergedFile.txt, this is useful when you want to join files in certain order.

Please note that each file is separated by a space. As you may already know, if
are in different folders please use full path instead of just file name.

type *.txt *.csv >MergedFile.txt

This will first join all files with .txt extension and then will join all files
with .csv extension to create a file called MergedFile.txt Here the magic command
is > which takes output of left side command and gives as input to right side command.
If used as >> then output gets be appended. As stated in
the principal of "Occam's razor" "The simplest explanation is
most likely the correct one"

Instead of writing my own code (spending few hours) or download or buy 3rd party
tool (spending money earned in few hours !!!) I used DOS to do the work. Hope this
helps Enjoy coding !!!

Thursday, September 24, 2009

Facelift to Forms; Part 2: Button - By Jay@Thakar.info


Introduction

This is second article in ’Facelift to Forms’series which describes various options to make beautiful, easy to implement and easy to use web forms. In my previous article ’Facelift to Forms; Part 1: TextBox - By Jay@Thakar.info’ we saw various options to make beautiful Textbox, In this article we will be looking at another element ’Button’.

Background

As you already know Button in web forms represents some action. In many cases web forms were designed very poorly where action items (like buttons) were not visible at all; In this article we will see how we can minimize/eliminate few common mistakes and make your (yes developer’s) and application user’s life easy. This article describes various ways to make beautiful (and highly visible) buttons.

Button

As you may agree, purpose of any web form is to collect information and send it to server. Without button it is difficult to send information to server as Button represents action item on form. There are many things which can be done with Button making it more fun and helping for end user and yet easy to implement. As end result a user will only click on a button if it is visible (to application user).

In this article we will use a simple registration form with 2 buttons ’Register’ and ’Clear’.

/*Code: 1. SimpleForm.htm*/
<input id="register" type="submit" value="Register" />
<input id="clear" type="reset" value="Clear" />
Simple Form
Simple form without any CSS

Now let’s start giving Facelift to this form.

Magic of CSS

One of most easy and effective way to change appearance of a Button (or any element) is to use CSS. There are few tricks which you can do with CSS to make Button more beautiful. Let’s see some...

/*Code: 2.CSS_Font.htm*/
<style>
...
...
.loginButton
{
    color: #B24F04;
    font-family: "Verdana" ,sans-serif;
}
</style>
</head>
...
...
<input id="register" type="submit" value="Register" 
    class="loginbutton" />
<input id="clear" type="reset" value="Clear" 
    class="loginButton" />
Added some fonts
Form with Fonts

By just applying fonts and changing color now these buttons look slightly better then earlier version. Another exciting thing is to use web fonts

/*Code: 3.Web_Font.htm*/
<style type="text/css">
@font-face
 {
    font-family: Gentium;
        src: url(http://www.princexml.com/fonts/larabie/berylibi.ttf) 
        format("truetype" );
}

.loginButton
{
    color: #B24F04;
    font-family: "Gentium" , serif;
}
</style>
...
...
<input id="register" type="submit" value="Register" class="loginButton" />
<input id="clear" type="reset" value="Clear" class="loginButton" />
Web fonts on Firefox
Web fonts rendered on Mozilla Firefox
Web fonts on IE
Web fonts on IE

As you can see here we are using fonts hosted on server hence it is not required that given fonts should be installed on client’s computer. If browser does not support 'web fonts' then substitute fonts (sans-serif in this case) will be used. This is one of 'cool' thing to do specially when you want to use some funky fonts and you know for sure that given fonts will not be available on client’s computer. Here is result in different browser from above code.

Note: Please be very careful about using this technique as this is still very early stage for this technology, very few browsers supports this, I was able to check this with IE 8 and Firefox 3.5

Now let’s add some borders to these Buttons ...

/*Code: 4.Border.htm*/
<style type="text/css">
...
...
.loginButton
{
    color: #B24F04;
    font-family: "verdana" ,sans-serif;
    border: solid thin #F58F1A;
}
</style>
<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" value="Register" class="loginButton" />
<input id="clear" type="reset" value="Clear" class="loginButton" />
</div>
Simple Borders
Buttons with Border

Here we just changed appearance of buttons to flat by adding solid border. You can experiment here with various border type and various sides to give various effects. Here is another example of using borders

/*Code: 5.Border_ThickLeft.htm*/
<style type="text/css">
...
...
.loginButton
{
 color: #B24F04;
 font-family: "Verdana" ,sans-serif;
 border: solid 1px #F58F1A;
 border-left: 5px solid #F58F1A;
 border-right: 5px solid #F58F1A;
}
</style>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" value="Register" class="loginButton" />
<input id="clear" type="reset" value="Clear" class="loginButton" />
</div>
Border with thick left border

 In above 2 examples we added borders, in second example we applied thin (1px) border to all sides of button and we added thick (5px) border on left & right side of Button. This gives nice visual effect which seems bit different the regular button. There are several other styles of border you can play with like outset, double, groove etc.

Different visual effect based on state

In most cases we want that disabled Button appear differently. Generally there are two ways to apply different visual style to represent disabled buttons.

/*Code: 6.Disabled_Different.htm*/
<style type="text/css">
...
...
.loginButton
{
    color: #B24F04;
    font-family: "Verdana" ,sans-serif;
    border: solid 1px #F58F1A;
    border-left: 5px solid #F58F1A;
    border-right: 5px solid #F58F1A;
}
input[disabled]
{
    font-family: "Verdana" ,sans-serif;
    border: solid 1px gray;
    border-left: 5px solid gray;
    border-right: 5px solid gray ;
    color:gray;
    filter: dropshadow(color=#555555,offX=0,offY=1);
    filter:alpha(opacity=60); /* IE’s opacity*/
    opacity: 0.60;
}
</style> <div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9"> ... ... <input id="register" type="submit" value="Register" disabled="disabled" /> <input id="clear" type="reset" class="loginButton" value="Clear" /></div>
Disabled textbox
Different Disabled formatting.

Now let’s add some background, Here are few ways we can use background; Let's start with adding solid background...

/*Code: 7.Background_Solid.htm*/
<style type="text/css">
...
...
.loginButton
{
 color: black;
 font-family: "Verdana" ,sans-serif;
 border: solid 1px #F58F1A;
 border-left: 5px solid #F58F1A;
 border-right: 5px solid #F58F1A;
 background-color:#D4E6F7;
}
</style>
<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" class="loginButton" value="Register" />
<input id="clear" type="reset" class="loginButton" value="Clear" />
</div>
Solid color as background
Button with solid background.

You can also use an image as background, There are two most popular visual effects ...

  1. Gradient effect
  2. Rounded corners

Let's see example.

/*Code: 8.Background_Image.htm*/
<style type="text/css">
...
...
.loginButton
{
 color: black;
 font-family: "Verdana" ,sans-serif;
 border: solid 1px #F58F1A;
 background-image: url("gr.jpg");
}
</style>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" class="loginButton" value="Register" />
<input id="clear" type="reset" class="loginButton" value="Clear" />
</div>
Gradient Background
/*Code: 9.Background_RoundCorner.htm*/
<style type="text/css">
...
...
.loginButton
{
 background: transparent url(’RoundCornerBtn.png’) no-repeat scroll top right;
 color: white;
 height: 38px;
 border: none;
 width: 130px;
 margin: 0px;
}
</style>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" class="loginButton" value="Register" />
<input id="clear" type="reset" class="loginButton" value="Clear" />
</div>
Button with Rounded Corner
Button with Rounded Corner

Note:As you may already have seen that we used width: 130px; that is because our image is 130 px wide. The textbox we used here have transparent background do whatever image you use ad background textbox will appear with that image. This style can only be used for fixed width buttons.

Slide Door Technique for Rounded Corners

After looking at above button a question comes to mind; what if I want to create buttons with dynamic width (i.e. background image gets adjusted based on button’s text). In that case there are several techniques available; Few uses some kind of scripting (i.e. jQuery or JavaScript etc), some uses CSS & HTML combination. We will take a look at one which involves HTML & CSS.

/*Code: 10.RoundCorner_slidedoor.htm*/
<style type="text/css">
...
...
button
{
 border: 0;
 text-align: center;
 padding: 0 12px 0 0;
}

button.submitBtn
{
 background: url(bg_button_right.png) right no-repeat;
}

button.submitBtn span
{
 height: 24px;
 line-height: 24px;
 background: url(bg_button_left.png) left no-repeat;
}

button span
{
 position: relative;
 display: block;
 white-space: nowrap;
 padding: 0 0 0 12px;
}
</style>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<button id="register" type="submit" class="submitBtn">
<span>Register</span></button>
<button id="clear" type="reset" class="submitBtn">
<span>Clear</span></button>

<button value="submit" class="submitBtn">
<span>Some very logn text</span></button>
</div>
Rounded corners with sliding door style.
Rounded corners with sliding door style

Here we used few things; first instead of using inputtag we used button tag we used buttonbecause it allows span tag inside which is not possible input tag. Here CSS for button takes care of left side of rounded corner and span takes care of right side. Here position: relative; in span will make sure that right side gets displayed after end of left side. We kept 12 px of padding on left and right side this is because of rounded curve of image on left and right where we don't want user to type.

Button with Icon

Adding icon to an image is always a great move as it adds more visual consistency and more focus from user; as you may already know human brain can process pictures faster than text. Let see various ways to add pictures (icons) to buttons.

/*11.Icon_CSS.htm*/
<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<button id="register" type="submit" class="submitBtn">
<span><img src="ico_submit.gif" border="0" alt="" 
height="18px" style="vertical-align:middle" /> Register</span></button>
<button id="clear" type="reset" class="submitBtn">
<span><img src="view_refresh.png" border="0" 
    alt="" height="18px" style="vertical-align:middle" />
    Clear</span></button>
</div>
Button with Icon
Button with Icon

In above code we added a span into button and added image into span. Unlike Input here Button tag allows us to add inner span where you can add Image.

Behavior

Now let’s move to next phase of customization for button, which is behaviors. Just like first section here too we have many different options for customization and add different behaviors.

Hover

/*Code: 12.Hover_CSS.htm*/
<style type="text/css">
...
...
button.submitBtn:hover
{
 background: url(bg_button_rightH.png) right no-repeat;
}

button.submitBtn:hover span
{
 background: url(bg_button_leftH.png) left no-repeat;
}

</style>
<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<button id="register" type="submit" class="submitBtn">
<span>Register</span></button>
<button id="clear" type="reset" class="submitBtn">
<span>Clear</span></button>
</div>
Highlight on Focus

Now this is very neat and apparently easy to implement; and we do not use JavaScript or any programming here :).CSS pseudo-classes are used to add special effects to some selectors. we used hover pseudo-class to add mouse over effect on button. It is possible to get same results using JavaScript.

/*Code: 13.HighlighJS_Hover.htm*/
<style type="text/css">
...
...
.loginButton
{
 color: black;
 font-family: "Verdana" ,sans-serif;
 border: solid 1px #F58F1A;
 background-image: url("gr.jpg");
}
 
.loginButton_Hover
{
 font-family: "Verdana" ,sans-serif;
 border: solid 1px #F58F1A;
 background-image: url("grHover.jpg");
}
</style>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" class="loginButton" 
 value="Register" onmouseout="this.className=’loginButton’;" 
 onmouseover="this.className=’loginButton_Hover’" />
<input id="clear" type="reset" class="loginButton" 
 value="Clear" onmouseout="this.className=’loginButton’;" 
 onmouseover="this.className=’loginButton_Hover’" />
</div>
Highlight on Focus with JavaScript

In code above in definition of Input we added JavaScript event handlers for onmouseover and onmouseout where in onmouseover we switch css class to loginButton_Hover and on onmouseout we flipped back to loginButton.

Providing Feedback about Action taken

According to one research conducted about page load time and user’s patients concluded that average user can wait approximately ’4 seconds’ for a page to load. Same thing applies to any action done on page like if user clicks on button he/she expects some kind of feedback; you must remember old days of windows forms where upon clicking on button cursor changes to wait (hour glass) cursor. There are various wa    ys for web application developer to provide feedback upon user action.

Displaying Message

One of popular method to provide feedback is to display message / or animated image indicating an action is initiated. In following example we are displaying a message.

/*14.Feedback_Label.htm*/
<script language="javascript" type="text/javascript">
function displayMessage() {
 var divMessage = document.getElementById("div_Message");
 divMessage.style.display = ’’;
 // following line is to simulate wait in real code you
 // may not need to include that; 
  setTimeout("window.location=’feedback_label.htm’;", 2000); 
  return true;
}
</script>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9">
...
...
<input id="register" type="submit" class="loginButton" value="Register" 
 onmouseout="this.className=’loginButton’;"
 onmouseover="this.className=’loginButton_Hover’" 
 onclick="return displayMessage();" />
<input id="clear" type="reset" class="loginButton" value="Clear" 
 onmouseout="this.className=’loginButton’;"
 onmouseover="this.className=’loginButton_Hover’" />
<div id="div_Message" style="display: none;">
 Form Submitter, Please wait ...</div>
</div>
Button before clicking.Button after clicking; Displays message.
Button before clicking.Button after clicking; Displays message.

In above code initially div_Messageis not visible; but on click event of ’Register’ button we call divMessage which displays a message indicating that you have clicked on button and you may need to wait to see result.

Fade effect on form

Another visual effect you can use is to fade entire form along with displaying message. This is very easy to implement and very effective and entire form changes it’s appearance which is visually more effective technique then just displaying only message.

/*Code: 15.Feedback_dimmer.htm*/
<style type="text/css">
...
...
.opac
{
 opacity: 0.4;
 filter: alpha(opacity=40);
}
</style>

<script language="javascript" type="text/javascript">
function displayMessage() {
 var divM = document.getElementById("div_Message");
 divM.style.display = ’’;
 var dForm = document.getElementById("div_Form");
 dForm.className = "opac"; // apply dimmer effect on entire form
 setTimeout("window.location=’15.Feedback_dimmer.htm’;", 2000);
 return true;
}
</script>

<div style="border: solid thin #FF4A00; padding: 10px; background-color: #FDEDD9"
id="div_Form">
...
...
<input id="register" type="submit" class="loginButton" value="Register" 
 onmouseout="this.className=’loginButton’;"
 onmouseover="this.className=’loginButton_Hover’"
 onclick="return displayMessage();" />
<input id="clear" type="reset" class="loginButton" value="Clear" 
 onmouseout="this.className=’loginButton’;"
 onmouseover="this.className=’loginButton_Hover’" />
</div>

<div id="div_Message" style="display: none;">
Form Submitted, Please wait ...</div>
Form before button clicked.
Form before button clicked.
Form after button clicked.
Form after button clicked.

As you can see in above code we used dForm.className = "opac"; to apply fade effect on form. This is very effective visual effect to let user know form processing is initiated and user needs to wait;

Preventing Accidents (Double Clicks)

 However in above 2 methods there is a potential problem; above methods cannot prevent user from clicking on button twice; In some cases we need to prevent user from clicking twice (like sites where you place online order; if user by mistake clicks twice same selection may gets order twice; which is not a good idea); following code prevents double clicks.

/*Code: 16.NoDBLClick.htm*/
<style type="text/css">
...
...
.opac
{
 opacity: 0.4;
 filter: alpha(opacity=40);
}
</style>

<script language="javascript" type="text/javascript">
function displayMessage() {
 var divM = document.getElementById("div_Message");
 var bRegister = document.getElementById(’register’);
 var dForm = document.getElementById("div_Form");
 divM.style.display = ’’;
 dForm.className = "opac";
 bRegister.form.submit();
 bRegister.disabled = 1;
 setTimeout("window.location=’16.NoDBLClick.htm’;", 2000);
 return true;
}
</script>

...
...
<input id="register" type="submit" class="loginButton" 
 value="Register" 
 onmouseout="this.className=’loginButton’;"
 onmouseover="this.className=’loginButton_Hover’" 
 onclick="return displayMessage();" />
<input id="clear" type="reset" class="loginButton" 
 value="Clear" 
 onmouseout="this.className=’loginButton’;"
 onmouseover="this.className=’loginButton_Hover’" />
</div>

<div id="div_Message" style="display: none;">
 Form Submitted, Please wait ...</div>
Disabled button after click.
Disabled button after click.

In above case we are first calling bRegister.form.submit();which will execute submit method in this form and then we call bRegister.disabled = 1;which will disable ’Register’ button which will prevent user from clicking it again.

History

Initial Submmision on September 24, 2009.

Summary

Download Source  Download Source

As developer if you start thinking about this question "How can I make End User’s life easy?" and implement changes You will make your life easy; In the end it is NOT you who decides how good your application is; it is Application’s Users. Happy Coding :)

If you have any questions, please feel free to contact me at: Jay@Thakar.info

Saturday, September 12, 2009

Inplace Edit in GridView By Jay@Thakar.info

Introduction

In this article I am presenting a different way to look at EditItemTemplate, using which user navigation for editing is reduced and … it looks very different (see screeen shots) then regular implementation of GridView.

Inline Edit

Background

Few days back I got an assignment, I needed to add few new fields in a DataViewand let user edit those (along with all existing fields).

I thought it was a very small change, just new fields in ItemTemplate and EditItemTemplateand perform few basic validations on it using JavaScript and I m done !. I was dead wrong. As soon as I modified EditItemTemplate, a Horizontal scrollbar started to appear on page. One of UI rule in our entire application on minimum resolution 1024x678 and with default font size, no Horizontal Scrollbar should appear on any of page.So I started thinking and creating new concepts. Here we had few options.

Download Source   Download Source

Various Options

Option 1:
Create new page for edit. When user clicks on edit button we redirect user to new page and when user clicks on Update/Close we bring them back on list page.
Problem:It was lengthy process to create new page, validate and bring them back, Also in this case link between current work and edit was broken as user was moving back and forth for editing.

Option 2:
Create a popup window, when user clicks on edit button we create a popup window and let user edit in new window, when user clicks on update/close user comes back to list page.
Problem:
As modern design approach we were moving away from popup windows (which opens as new window) and tried to find some other alternate for this popup windows. While talking about this option I also threw AjaxControlToolkit Model Popup but it had same problem as option 1, link between current data and editing data was broken.

Option 3:
Create inline form which gets displayed between 2 rows in GridView, Let user make any modification required there and when user clicks on update/close bring back same row. This is not new technique but there are many companies who offer this kind of facility (and change big bucks for that) As this was bit easy to implement I thought let’s try creating it on our own (and save some buck, of course for company :) )
Problem: NONE !!!

My IT Director was very happy to see this prototype, She suggested that I should implement this style. And the journey begins …

There are few things which generates this facility.

As you already know GridView gets rendered as table and each column defination in GridViewgenerates one column; so we defined only one column. To create an illusion of Table we defined only one column and created a table in it; We created required columns in this new inner table. This gets rendered as one table per DataRow with all columns we specified.

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="4"
ForeColor="#333333" GridLines="Both" Width="60%" OnRowEditing="GridView1_RowEditing">
<RowStyle BackColor="#EFF3FB" />
<Columns>
<asp:TemplateField>
...
...
<ItemTemplate>
<table width="100%" cellpadding="0" cellspacing="0" >
<tr>
<td style="width:15%;" align="left">
<%#DataBinder.Eval(Container.DataItem, "FirstName")%>
</td>
<td style="width:15%;" align="left">
<%#DataBinder.Eval(Container.DataItem, "LastName")%>
</td>
<td style="width:35%;" align="left">
<%#DataBinder.Eval(Container.DataItem, "Web")%>
</td>
<td style="width:35%;" align="left">
<%#DataBinder.Eval(Container.DataItem, "Email")%>
</td>
</tr>
</table>

</ItemTemplate>

Above ItemTemplate will generate one table per row. Now let's see how we can make inline editing possible.


<EditItemTemplate>
<center>
<ajx:RoundedCornersExtender ID="rce" runat="server" TargetControlID="pnlHead"
Radius="6" Corners="Top" BorderColor="black" />
<asp:Panel ID="pnlHead" runat="server" Font-Bold="true" ForeColor="white"
BackColor="#507CD1"
Width="50%">
<center>
<table>
<tr>
<td>
Updating
<%#DataBinder.Eval(Container.DataItem, "FirstName")%>,
<%#DataBinder.Eval(Container.DataItem, "LastName")%>
</td>
</tr>
</table>
</center>
</asp:Panel>
<ajx:RoundedCornersExtender ID="rceDetail" runat="server" TargetControlID="pnlDetail"
Radius="6" Corners="Bottom" BorderColor="black" />
<asp:Panel ID="pnlDetail" runat="server" Width="50%">
<table width="100%">
<tr>
<td align="right" style="width: 30%">
First Name:
</td>
<td style="padding-right:10px;">
<asp:TextBox ID="tbFirstName" Width="100%" runat="server"
Text='<%#DataBinder.Eval(Container.DataItem, "FirstName")%>'>
</asp:TextBox>
</td>
</tr>
<tr>
<td align="right">
Last Name:
</td>
<td style="padding-right:10px;">
<asp:TextBox ID="TextBox1" runat="server" Width="100%"
Text='<%#DataBinder.Eval(Container.DataItem, "LastName")%>'>
</asp:TextBox>
</td>
</tr>
<tr>
<td align="right">
Web:
</td>
<td style="padding-right:10px;">
<asp:TextBox ID="TextBox2" runat="server" Width="100%"
Text='<%#DataBinder.Eval(Container.DataItem, "Web")%>'>
</asp:TextBox>
</td>
</tr>
<tr>
<td align="right">
eMail:
</td>
<td style="padding-right:10px;">
<asp:TextBox ID="TextBox3" runat="server" Width="100%"
Text='<%#DataBinder.Eval(Container.DataItem, "eMail")%>'>
</asp:TextBox>
</td>
</tr>
</table>

<div style="padding-top: 3px; border-top: thin solid black;" id="divUpdate">
<asp:Button CssClass="btnNormal" runat="server" ID="bUpdate"
Text="Update" OnClick="bUpdate_Click" />    
<asp:Button OnClick="bCancel_Click" CssClass="btnNormal"
runat="server" ID="bCancel" Text="Cancel" />

</div>
</asp:Panel>
</center>
</EditItemTemplate>
</asp:TemplateField>

From our ItemTemplate it was clear that in edit mode we will get only one column, so just like in ItemTemplate here too we defined edit layout whichever way we want. In our case we wanted rounded corner center aligned table, with different style on header, to acheave this we used AjaxControlToolkit's RoundedCornerExtension, it has facility to define which corners we want as rounded corners so as header we defined only top corners as rounded and for content part we defined only bottom corners as rounded corners. And using table we defined a layout of form for editing. this gives us a nice looking form when user clicks on edit button. And as result we got following GridView.


Image 2: Edit View in inline Grid View

As final touch we also needed header to match given columns so we used same type of style we used on ItemTemplate.


<HeaderTemplate>
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td style="width:15%;" align="left">
First Name
</td>
<td style="width:15%;" align="left">
Last Name
</td >
<td style="width:35%;" align="left">
Web
</td>
<td style="width:35%;" align="left">
Email
</td>
</tr>
</table>
</HeaderTemplate>

Note:Please make sure that ItemTemplate table defination matches to header (specially width of each column)  as it will give one table look.

Update:After publishing this article I got several requests about few things ...

  1. Code to update data on click of Update button.
  2. Add paging and code to preserve edit while moving between pages in pageble GridView.
  3. Disable all Edit button in Edit state (so user can not 'by accident' update other row.

So here is code ...

1.Code to update data on click of Update button.

Main purpose for this article was to present different way to use ItemTemplate; I understand taht there may be few questions about updating data which is beyond of scope of this article. Here is sample code for how to access updated value when user clicks on Update Button.

protected void bUpdate_Click(object sender, EventArgs e)
{
TextBox tbF = (TextBox)GridView1.Rows[GridView1.EditIndex].FindControl("tbFirstName");
TextBox tbL = (TextBox)GridView1.Rows[GridView1.EditIndex].FindControl("tbLastName");
TextBox tbW = (TextBox)GridView1.Rows[GridView1.EditIndex].FindControl("tbWeb");
TextBox tbE = (TextBox)GridView1.Rows[GridView1.EditIndex].FindControl("tbEmail");

// now you have all values entered by user
// set those values in dataset and update
// OR generate UPDATE sql statement here and save values in db.
// more details / example can be found at
// http://msdn.microsoft.com/en-us/library/ms972948.aspx
// OR
// http://www.aspdotnetcodes.com/GridView_Insert_Edit_Update_Delete.aspx
refreshData();
}

private void refreshData()
{
GridView1.EditIndex = -1;
ViewState["EditRowID"] = null;
GridView1.DataSource = CreateTable();
GridView1.DataBind();
}

The trick here is to use FindControl on currently editing row and pass ID of control which you want to access. And to accesss currently editing row you can use GridView1.Rows[GridView1.EditIndex]. Once we are done updating data change EditIndex to -1 (indicating no row is in edit state); this will restore all rows of grid back to view state (i.e. hide edit template).

For more details on how to update data into database please refer to following link.

http://msdn.microsoft.com/en-us/library/ms972948.aspx

2. Add paging and code to preserve edit while moving between pages in pageble GridView.


private int iCurrentEdit = -1;
protected void GridView1_RowEditing(Object sender, GridViewEditEventArgs e)
{
GridView1.EditIndex = e.NewEditIndex;
GridView1.DataSource = CreateTable();
GridView1.DataBind();
ViewState.Add("editRowIndex", e.NewEditIndex);
ImageButton editButton;

foreach (GridViewRow row in GridView1.Rows)
{
if (row.RowIndex != e.NewEditIndex)
{
editButton = (ImageButton)(row.Cells[1].Controls[0]);
editButton.ImageUrl = "./edit_off.gif";
editButton.Enabled = false;
}
else
{
ViewState.Add("EditRowID", row.DataItemIndex.ToString());
if (ViewState["fname"] != null)
// indicates we have values from previous editing session which are not saved
{
// get elements from edit template
TextBox tbF = (TextBox)row.FindControl("tbFirstName");
TextBox tbL = (TextBox)row.FindControl("tbLastName");
TextBox tbW = (TextBox)row.FindControl("tbWeb");
TextBox tbE = (TextBox)row.FindControl("tbEmail");

// restore previous values
tbF.Text = ViewState["fname"].ToString();
tbL.Text = ViewState["lname"].ToString();
tbW.Text = ViewState["web"].ToString();
tbE.Text = ViewState["email"].ToString();
}

}
}
}

protected void GridView1_PageIndexChanging(Object objDGName, GridViewPageEventArgs e)
{

GridView1.DataSource = CreateTable();
GridView1.PageIndex = e.NewPageIndex;
GridView1.EditIndex = -1;
GridView1.DataBind();

if (iCurrentEdit != -1)
{
GridView1_RowEditing(GridView1, new GridViewEditEventArgs(iCurrentEdit));
}
}

protected void GridView1_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow ||
e.Row.RowType == DataControlRowType.EmptyDataRow)
{
int iEditRowID = int.Parse((ViewState["EditRowID"] == null ? "-2" :
ViewState["EditRowID"].ToString()));
if (iEditRowID != -2 && e.Row.DataItemIndex == iEditRowID)

{
iCurrentEdit = e.Row.RowIndex;
}
else if (iEditRowID != -2)
{
ImageButton editButton;
editButton = (ImageButton)(e.Row.Cells[1].Controls[0]);
editButton.ImageUrl = "./edit_off.gif";
editButton.Enabled = false;
}
}
}

There are few things here ...

We defined a class level variable called iCurrentEdit with initial value of -1 indicating no row is in edit state.

When user clicks on Edit (protected void GridView1_RowEditing(Object sender, GridViewEditEventArgs e)) button we loop thrue entire grid to find DataItemIndex and when we find it we store DataItemIndex into ViewState using ViewState.Add("EditRowID", row.DataItemIndex.ToString());

Now when user clicks to change page (protected void GridView1_PageIndexChanging(Object objDGName, GridViewPageEventArgs e)) first we call GridView1.DataBind(); which will call RowDataBound for each row.

Finally in RowDataBound event (protected void GridView1_RowDataBound(Object sender, GridViewRowEventArgs e)) we retrive value of currently editing DataItemIndex (int iEditRowID = int.Parse((ViewState["EditRowID"] == null ? "-2" : ViewState["EditRowID"].ToString()));) and then we compare this value against current row's DataItemIndex, If it is same then this indicates this is row in edit state hence we store RowIndex into iCurrentEdit so; when if (iCurrentEdit != -1) gets executed in PageIndexChanging there will be value in iCurrentEdit (if a row is in edit state) if this is the case then we call GridView1_RowEditing(GridView1, new GridViewEditEventArgs(iCurrentEdit)); to simulate rowEditClick.

In above code there is one potential problem, If user clicks on edit and changes First Name (or any other field in that matter); does not click on update button and goes to other page to view some information and comes back, above code will take care of putting correct row in edit mode but will not preserve values entered by user. To save those values temperarly we can use ViewState.


protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
GridView1.DataSource = CreateTable();
GridView1.DataBind();
}
else
{
if (ViewState["editRowIndex"] != null)
// indicates we have values from edit which needs to be saved in viewstate
{
int iEditRowID = int.Parse((ViewState["editRowIndex"] == null ? "-2" :
ViewState["editRowIndex"].ToString())); // get current edit row index

// get all elements from edit template
TextBox tbF = (TextBox)GridView1.Rows[iEditRowID].FindControl("tbFirstName");
TextBox tbL = (TextBox)GridView1.Rows[iEditRowID].FindControl("tbLastName");
TextBox tbW = (TextBox)GridView1.Rows[iEditRowID].FindControl("tbWeb");
TextBox tbE = (TextBox)GridView1.Rows[iEditRowID].FindControl("tbEmail");

// save all values into viewstate for future use
if (tbF != null)
{
ViewState.Add("fname", tbF.Text);
ViewState.Add("lname", tbL.Text);
ViewState.Add("web", tbW.Text);
ViewState.Add("email", tbE.Text);
}
}
}
}

private void refreshData()
{
GridView1.EditIndex = -1;
ViewState["EditRowID"] = null;
GridView1.DataSource = CreateTable();
GridView1.DataBind();

// clear viewstate (this is called from UpdateButtonClicked and CancelButtonClicked
ViewState["fname"] = null;
ViewState["lname"] = null;
ViewState["web"] = null;
ViewState["email"] = null;
ViewState["editRowIndex"] = null;

}

3. Disable all Edit button in Edit state (so user can not 'by accident' update other row.

When user clicks on edit button for any row; user can not (or should not) be able to click on edit button of any other row; for that we need to disable all edit buttons when user is in edit mode.


protected void GridView1_RowEditing(Object sender, GridViewEditEventArgs e)
{
...
...
ImageButton editButton;
foreach (GridViewRow row in GridView1.Rows)
{
if (row.RowIndex != e.NewEditIndex)
{
editButton = (ImageButton)(row.Cells[1].Controls[0]);
editButton.ImageUrl = "./edit_off.gif";
editButton.Enabled = false;

}
}
...
...
}
protected void GridView1_RowDataBound(Object sender, GridViewRowEventArgs e)
{
...
...
else if (iEditRowID != -2)
{
ImageButton editButton;
editButton = (ImageButton)(e.Row.Cells[1].Controls[0]);
editButton.ImageUrl = "./edit_off.gif";
editButton.Enabled = false;
}

}

Here when user clicks on Edit button we loop through entire grid and Disable edit button if it's index does not match with current edit row index. To access edit button we use editButton = (ImageButton)(e.Row.Cells[1].Controls[0]); as you already know we have created entire edit template in one table cell hence second cell (at cell index 1) will be for edit/update/cancel buttons and from that we access first control which is edit button; we disable it and change image icon to indicate this row is not available for editing. Almost similer thing we are doing on row bound which takes care of rows which are not on current page. When user goes from currently editing page to another page we wanted to disable editing which we took care in RowBound event.

History

Updated Code for various event on Sep 23, 2009.

Initial submission on Sep 14, 2009.

Download Source   Download Source

Summary

I hope this help you to create easy to use GridView

Happy Coding :)