Friday, December 3, 2010

C# Delegates as Method Parameters

Let's say you have 2 methods that go through similar steps:

public string GetPrevItem()
 {
    if (IsValidPrevEntry())
    {
        string item = GetPrevString();
        SetProperties();
         return item;
    }
   else
       return null;
 }

public string GetNextItem()
{
    if (IsValidNextEntry())
     {
        string item = GetNextString();
        SetProperties();
        return item;
     }
     else
       return null;
 }
 
These 2 methods essentially do the same thing. First, they check for some condition, and if the condition evaluates to true, they call another method, set some properties, and return a string. We can use C# delegates to refactor these 2 methods into one. Here is how you do it:
Declare 2 delegates whose signature matches the methods we want to replace:
  delegate bool IsValid();
  delegate string GetItem();
Create a method that will use the delegates:

 private string GetMyItem(IsValid isValid, GetItem getItem)
 {
   if (isValid())
   {
        string item = getItem();
         SetEventProperties();
         return item;
   }
   else
       return null;
  }       
An alternative method signature is as follows:
 private string GetNavItem(Func<bool> isValid, Func<string> getItem)
This way we don't have to declare delegates separately.

Now, we can call the new method, passing the methods that we called before refactoring, as parameters:
string strPrev =   GetMyItem(IsValidPrevEntry, GetPrevString);
string strNext =   GetMyItem(IsValidNextEntry, GetNextString);
An interesting discussion of delegates can be found on the stackflow.

Tuesday, November 16, 2010

Add JavaScript Dynamically to ASP.NET UpdatePanel

Use the following example:

System.Text.StringBuilder sb = new System.Text.StringBuilder();      
sb.Append(@" var inputs = document.getElementsByTagName('input');                                  for (var i = 0; i < inputs.length; i++) {
         if (inputs[i].type == 'text') {                
            inputs[i].onkeypress = function (event) {
               event = event || window.event;

               return myJavaScriptFunction(event);}
           }}"
       );
ScriptManager.RegisterClientScriptBlock (this, this.GetType(), "ajax", sb.ToString(), true); 

What it does is attaches  a JavaScript function (defined elsewhere) to each input box on the page on the key press event, and then registers it for use with a control that is inside an UpdatePanel.  This could be used for user input validation. The code has been verified in IE and Firefox.

Wednesday, November 10, 2010

ASP.NET GridView Subclass with Dynamic Footer Totals

A frequent requirement when using an ASP.NET GridView on a web page is to have a footer with totals for numeric columns in the GridView. Here is a GridView subclass that calculates footer totals dynamically.  It requires 3 parameters:

FirstComputedColumnIndex - the for index of the first column in the data set to be computed
FirstVisbleComputedColumnIndex - for the index of the first computed column to be shown
NumberOfComputedColumns - for the number of computed columns.

Here is the class code:

namespace Eric.TotalGrid {
  public partial class GridView : System.Web.UI.WebControls.GridView
    {
        private Indexer footerTotals;
        public int FirstComputedColumnIndex { get; set; }
        public int FirstVisbleComputedColumnIndex { get; set; }
        public int NumberOfComputedColumns { get; set; }    

        protected override void OnLoad(EventArgs e)
        {
            ShowFooter = true;
            base.OnLoad(e);
        }
        protected override void OnInit(EventArgs e)
        {
            footerTotals = new Indexer(NumberOfComputedColumns);
            InitializeComponent();
            base.OnInit(e);
        }

        private void InitializeComponent()
        {
            this.RowDataBound += new GridViewRowEventHandler(this.GridView1_RowDataBound);
        }

        protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
        {           

            DataRowView tableData = e.Row.DataItem as DataRowView;

            if (e.Row.RowType == DataControlRowType.DataRow)
            {            
                for (int i = FirstComputedColumnIndex; i < (FirstComputedColumnIndex + NumberOfComputedColumns); i++)
                {               
                    if (tableData[i] != DBNull.Value)
                    {
                        decimal result = 0;
                        if (Decimal.TryParse(tableData[i].ToString(), out result))
                        {
                            footerTotals[i - FirstComputedColumnIndex] += result;
                        }
                    }
                }
            }
            else if (e.Row.RowType == DataControlRowType.Footer)
            {
                for (int i = 0; i < NumberOfComputedColumns; i++)
                {
                    if (e.Row.Cells.Count > (i + FirstVisbleComputedColumnIndex))
                        e.Row.Cells[i + FirstVisbleComputedColumnIndex].Text = footerTotals[i].ToString();
                }
            }
        }
}



The key to this GridView is using an Indexer class that can be accessed like an array. Here is its listing:


class Indexer
        {
            private int _upperLimit;      

            private decimal[] myArray;

            public Indexer(int limit)
            {
                _upperLimit = limit;
                myArray = new decimal[_upperLimit];
            }
            public decimal this[int index]   // Indexer declaration
            {
                get
                {
                    // Check the index limits.
                    if (index < 0 || index >= _upperLimit)
                        return 0;
                    else
                        return myArray[index];
                }
                set
                {
                    if (!(index < 0 || index >= _upperLimit))
                        myArray[index] = value;
                }
            }
        }
The Indexer class can be nested within the GridView subclass.


The last example shows how to use the custom GridView on a ASP.NET web page:
First,  use the register tag:
<%@ Register Namespace="Eric.TotalGrid" TagPrefix="x" Assembly="TotalGrid" %>

Then, use it just like any other GridView:

<x:GridView runat="server" ....
FirstComputedColumnIndex="4" FirstVisbleComputedColumnIndex="3" NumberOfComputedColumns="10"  >

Thursday, October 28, 2010

Set DataRow Values and Other ASP.NET GridView Tips

You can set DataRow values in two ways:
First, using a column name:
myDataRow["City"]= "London";

Or, using an index:
myDataRow[2] = "London";

Don't use ItemArray property to set values, it won't work.

Set Text Box Width in a GridView Bound Field:

Use ControlStyle properties:

<asp:BoundField... ControlStyle-Width="60px"/>

Change GridViewRow Appearance: 

There are different ways to change GridViewRow appearance. The approach I like involves using the GridViewRow Style property. On RowDataBound event use this:

if (e.Row.RowType == DataControlRowType.Footer)
 {                              
     e.Row.Style.Add("color", "#999999");
     e.Row.Style.Add("font-weight", "bold"); 
     ...

Did you know that you could pass data format string as a parameter to the ToString() method?

For example:
e.Row.Cells[1].Text = iTotal.ToString("F2")
The above format string specifies 2 decimal places in a numeric value.

Tuesday, October 26, 2010

Simple Linq to Object example without Casting

I found this discussion related to casting the result of a LINQ query to a static object at http://devlicio.us/blogs/derik_whittaker/archive/2008/02/22/simple-linq-to-object-example-with-casting.aspx Since it was not possible to post a comment I decided to write a blog entry on my blog.

The example given at the above page gives the following code:


List<Sport > sports = new List <Sport>();
sports.Add(new Sport { SportID = 1, Name = "Sport 1", Description = "Sport Desc 1" });
sports.Add(new Sport { SportID = 2, Name = "Sport 2", Description = "Sport Desc 2" });
sports.Add(new Sport { SportID = 3, Name = "Sport 3", Description = "Sport Desc 3" });
sports.Add(new Sport { SportID = 4, Name = "Sport 4", Description = "Sport Desc 4" });
sports.Add(new Sport { SportID = 5, Name = "Sport 5", Description = "Sport Desc 5" });

var query = from s in sports
         where s.Name == "Sport 2"
         select s;

Sport sport = (Sport)query.First();


I would like to note that you could rewrite the above statements using lambdas:

Sport sport = sports.Where(s => s.Name == "Sport 2").First();

No "casting" is required.

Tuesday, August 3, 2010

Resize WinForm Controls Automatically

Let' say you have a form with a ListBox and OK and Cancel buttons.
 




You want controls to be resized automatically when a form is resized. This is how you can achieve this by using the Anchor property:

Place all controls inside a GroupBox. Set the Anchor and Dock properties

GroupBox properties:
Anchor : Top, Bottom, Left, Right
Dock : None

Buttons properties:
Anchor : Bottom, Right
Dock : None

ListBox properties:
Anchor : Top, Bottom, Left, Right
Dock : None

Save Window Position in Registry

This is how you could save a Window position in the Registry so that the Window position could be restored next time the Form loads.


Declare the following constant strings:

 public const string REGKEY = @"SOFTWARE\MyCompany\MyApp\"; 
public const string WINWIDTH = "windowWidth";
public const string WINHEIGHT = "windowHeight";

Then, create a key for your application in the Registry. 
Declare a RegistryKey variable:
private RegistryKey key;

Use the following method to obtain the key:

public RegistryKey GetKey(string RegKey)
{
            RegistryKey Key = Registry.CurrentUser.OpenSubKey(RegKey, true);
            if (Key == null)
            {
                Key = Registry.CurrentUser.CreateSubKey(RegKey);
            }
            return Key;
}

key =  GetKey(REGKEY);

Get window width and height:

int width = Form1.Size.Width;
int height = Form1.Size.Height;
Save window width and height in the registry and close the RegistryKey.


key.SetValue(WINWIDTH, width);
key.SetValue(WINHEIGHT, height);
key.Close(); 
When you load your form, read the values from the registry:
width = Convert.ToInt16 (key.GetValue(WINWIDTH));
height = Convert.ToInt16 (key.GetValue(WINHEIGHT));

Set the new window size:
Form1.Size = new Size(width, height);

Tuesday, July 20, 2010

Concise Code to Enable/Disable WinForm Controls

Let's say you have a check box (cb1)  on a form that enables a text box (txt1)  and disables a listbox (lst1).  You can write a statement that shows this dependency on 2 lines:

  txt1.Enabled = cb1.Checked;
  lst1.Enabled = !cb1.Checked;

This works fine. However, we repeat the reference to the check box state twice.
There is a way to write the above statements on the same line:

   lst1.Enabled = !(txt1.Enabled = cb1.Checked);

Looks simple when you get it right. But it took me a few minutes to figure it out.

Friday, July 16, 2010

Examine Columns in ASP.NET Dynamic Data MetaTable

This is how you get access to all columns in a MetaTable that serves as a data source to your controls like a DetailsView or a GridView. Dynamic Data generates the following code for each ASP.NET page by default:


   protected MetaTable table;

   protected void Page_Load(object sender, EventArgs e)
   {
       table = DetailsDataSource.GetTable();
       Title = table.DisplayName;
   }
The MetaTable class has a Columns property that returns a collection of MetaColumns for the table. This allows us to enumerate through the collection and check for any custom attributes on table columns:
    
   foreach (MetaColumn column in table.Columns)
   {
      if (column.Attributes.OfType<MyCustomAttribute>().Count() > 0)
          ......
      }
   }

This is based on a tip found at this blog.

Thursday, July 8, 2010

Reverse .NET SortedList

 This post shows how you could reverse a SortedList in C# by its keys:

SortedList<string, string> strlist = new SortedList<string, string>();
strlist.Add("f3", "xxx");
strlist.Add("f1", "ccc");
strlist.Add("f2", "aaa");

foreach (KeyValuePair<string, string> kvp in strlist)
{
    Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}           

Console.WriteLine("reverse the list");
IEnumerable<KeyValuePair<string, string>> revList = strlist.Reverse();
foreach (KeyValuePair<string, string> kvp in revList)
{
    Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}




Monday, June 7, 2010

Open a Text File If It Is Used by Another Process

Use the FileStream class's Open method to open a file with FileShare.ReadWrite as a parameter. Then use StreamReader class supplying the FileStream object as a constructor argument:
FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader sr = new StreamReader(fs);

Thursday, June 3, 2010

Set Instance Property Value through Reflection

This is how you can set object property value using reflection:

Employee emp = new Employee();
PropertyInfo pi = emp.GetType().GetProperty("myPropertyName");
pi.SetValue(emp, "myValue", null);

This is how you read a value:

string value = pi.GetValue(emp, null);

To access all properties of an object:

foreach (PropertyInfo info in emp.GetType().GetProperties()){
    ....
}
Using reflection you can automatically populate a transfer object from a database record if the object property names and types are the same as the record names and types.

An example using a DataReader:

List <employee>list = new List <employee>();
while (dataReader.Read())
{
Employee emp = new Employee();
foreach (PropertyInfo info in emp.GetType().GetProperties())
{
   if (!DBNull.Value.Equals(dr[info.Name]))
      info.SetValue(emp, dr[info.Name], null);
}
list.Add(emp);
}
 
The resulting list will hold Employee records.





Wednesday, June 2, 2010

Object Class Names of the The Authorization Service

The IAuthorizationService interface exposes two methods you can use to obtain object class names available in TFS.

The first one is ListObjectClasses. It returns a string array of IDs of all defined object classes :

CSS_NODE
EVENT_SUBSCRIPTION
ITERATION_NODE
NAMESPACE
PROJECT

The other one is ListObjectClassActions. You need to supply an object class id as a string parameter to the method. It will return a string array of actions defined for a specified class. For example, if you supply CSS_NODE as a parameter, it will return the following action ids:

GENERIC_READ
GENERIC_WRITE
CREATE_CHILDREN
DELETE
WORK_ITEM_READ
WORK_ITEM_WRITE

Tuesday, June 1, 2010

Query Operator First

Let's say you have the following method that returns a FileInfo object from an array if the Name property equals a certain value:
public static FileInfo FindFile(FileInfo[] fileInfoArray, string name)
{           
    foreach (FileInfo fi in fileInfoArray)
    {
        if (fi.Name == name)
            return fi;
    }
    return null;
}
You can replace this method with a call to the query operator First :

FileInfo fileInfo = fileInfoArray.First(fi => fi.Name =="myFileName.txt");
Since the query operator First returns a single value, it forces immediate query evaluation.

Thursday, May 20, 2010

Get TFS Team Project Names

Here is a method that returns a List of names of team projects available at Team Foundation Server

public static List<string> GetProjects(TeamFoundationServer tfsServer)
        {
            List <string> listProjects = new List<string>();      

            ICommonStructureService iss = (ICommonStructureService)tfsServer.GetService(typeof(ICommonStructureService));           
            ProjectInfo[] projectInfo = iss.ListProjects();

            foreach (ProjectInfo pi in projectInfo)
            {
                listProjects.Add (pi.Name);
            }                                                

            return listProjects;
        }

Thursday, May 13, 2010

Some Registry Methods

Here are a couple of helper methods to work with Windows Registry:
The first method makes sure you get a reference to a registry subkey. If it does not exist it creates one first. The second one is a preferred way to read a registry value.



 public static RegistryKey GetKey(string RegKey)
 {
     RegistryKey Key = Registry.CurrentUser.OpenSubKey(RegKey, true);

     if (Key == null)
     {
         Key = Registry.CurrentUser.CreateSubKey(RegKey);
     }
     return Key;
}

public static string ReadRegistryValue(RegistryKey key, string regValName)
{
     string[] values = key.GetValueNames();
     if (values.Contains(regValName))
          return key.GetValue(regValName).ToString();
     else
          return String.Empty;
}

Thursday, February 25, 2010

Header Cell with CheckBox in DataGridView

This is my version of a class that displays a check box in a DataGridView Header cell.
This is based on the code from http://social.msdn.microsoft.com/Forums/en-US/winformsdatacontrols/thread/827907ea-c529-4254-9b15-2e6d571f5c5b

It assumes that the check box is located in the first DataGridView column. It allows for column caption and provides built-in check/uncheck functionality. Play with x and y parameters to place the checkbox where you want in the header cell.

Here is a usage example:

DataGridViewColumn column = new DataGridViewCheckBoxColumn();
CheckBox ckBoxMaster = CheckBoxInHeader.CreateCheckBoxInHeader(column, 12, 4, "     Select All");
DataGridView1.Controls.Add(ckBoxMaster);


The class listing:

  class CheckBoxInHeader
    {
    public static CheckBox CreateCheckBoxInHeader(DataGridViewColumn column, int x, int y, string caption)
    {
        CheckBoxInHeader cbInHeader = new CheckBoxInHeader();
        CheckBox ckBox = cbInHeader.CreateCheckBox(column, x, y, caption);
        ckBox.CheckedChanged += new EventHandler(cbInHeader.ckBox_CheckedChanged);
        return ckBox;
    }

    private CheckBox CreateCheckBox(DataGridViewColumn column, int x, int y, string caption)
    {
        CheckBox ckBox = new CheckBox();
        //Get the column header cell bounds          
        Rectangle rect = column.DataGridView.GetCellDisplayRectangle(0, -1, true);
        ckBox.Size = new Size(18, 18);
        //play with check box position
        rect.Offset(x, y);
        //Change the location of the CheckBox to make it stay on the header
        ckBox.Location = rect.Location;
        column.Name = caption;
        return ckBox;
    }

    private void ckBox_CheckedChanged(object sender, EventArgs e)
    {
        CheckBox cb = sender as CheckBox;
        DataGridView dgv = (DataGridView)cb.Parent;

        foreach (DataGridViewRow row in dgv.Rows)
        {

            row.Cells[0].Value = cb.Checked;
        }
    }

Thursday, February 18, 2010

Double-Click to Close WinForm ListBox

If you have a ListBox on a WinForm, and you want the double-click on a selected item to close the form, you need to create a handler for the ListBox double click event.
In the handling method, use the following code:
 private void listBox1_DoubleClick(object sender, EventArgs e)
        {
        if (listBox1.SelectedItem != null)
        {
            listBox1.FindForm().DialogResult = DialogResult.OK;
        }
        }
or, you could use a generic version of the above code:
 ListBox listBox = sender as ListBox ;
       if (listBox.SelectedItem != null)
       {
       listBox.FindForm().DialogResult = DialogResult.OK;
       }

Monday, February 8, 2010

Saving Multiple Files with the Same Name

If you need to save multiple files with the same name in the same directory, a common technique is to construct a file name adding an incremental counter to it, e.g. myfilename(1).txt, myfilename(2).txt, etc.

The method below allows you to create such a file name. It requires 1 parameter: a full file path. The method returns a FileInfo type.
It attempts first to construct a FileInfo object based on a file path, and then checks if such a file already exists. If it does not, the FileInfo object is returned. Otherwise, it constructs a new file name and checks for its existence until such a name no longer exists.

 

private static FileInfo GetFileInfo(string filePath)

{

    FileInfo fInfo = new FileInfo(filePath);

    string fileName = Path.GetFileNameWithoutExtension(filePath);

    string dirName = fInfo.DirectoryName;

    int counter = 1;

 

 

    while (fInfo.Exists)

    {

        string extension = fInfo.Extension;

        string nameProper = fileName + "(" + (counter++) + ")";

        filePath = Path.Combine(dirName, nameProper + extension);

        fInfo = new FileInfo(filePath);

        nameProper = String.Empty;

    }

    return fInfo;

}

Tuesday, February 2, 2010

Have DataGridView Scroll Down Automatically

The trick to having a WinForm DataGridView scroll down automatically to the last row as you populate it is to use the DataGridView CurrentCell property. Just set it to a cell in the last added row.
The DataGridView also has CurrentRow property but that is read-only.

Friday, January 8, 2010

Get TreeNode from Full Path in Winform

This is a recursive method that finds a TreeNode in a .NET TreeView control from its full path.

public TreeNode GetNodeFromPath(TreeNode node, string path)
        {
        TreeNode foundNode = null;
        foreach (TreeNode tn in node.Nodes)
        {
            if (tn.FullPath == path)
            {                 
                return tn;
            }
            else if (tn.Nodes.Count > 0)
            {
                foundNode = GetNodeFromPath(tn, path);
            }
            if (foundNode != null)
                return foundNode;
        }
        return null;
        }


Thursday, January 7, 2010

List and ForEach

Let's say you have a List of strings in C# and you add each element of the List to a StringBuilder.

StringBuilder sb = new StringBuilder();
List  <string> list = new List <string>();
list.add("one");
list.add("two");
list.add("three");

You could do the following loop:

foreach(var item in list){
  sb.Append(item); 
}
An alternative method to achieve the same functionality would go like this:
list.ForEach(item =>sb.Append(item));

With this method you save 2 lines of code and improve readability.