Monday, December 3, 2012

A Comparison of IComparable and IComparer interfaces in C#




.Net framework provides 2 interfaces (IComparable and IComparer) for purpose of sorting/Comparing . 

IComparable <T>:

This interface is to be implemented by the class itself whose instances need ordering/sorting .It provides CompareTo method to implement.
All .net primitive types like Int32,String etc. implements IComparable.So we can easily compare 2 built in types using CompareTo method.
Eg.
  int i = 9;
  int j=10;
  i.CompareTo(j);
Also if we have a collection of a built in type we can sort the collection easily.

Eg.
List<int> lstInt = new List<int>();
lstInt.Sort();

Now say we have a custom type Employee. Lets try the following
 List< Employee > lstEmp = new List< Employee >();
…………………………………….
Add some employees
…………………………………
lstEmp.Sort();

Now when we try to do sorting on a custom type we will get exception “Failed to compare two elements in the array.”
It is because to use sort() on anytype , the type has to implement IComparable .Now lets try by implementing IComparable .
e.g.
  public class Employee:IComparable<Employee>
    {
        public string Name { get; set; }
        public int JanSalary {get;set; }
        public int FebSalary { get; set; }

       
        #region IComparable<Employee> Members

        public int CompareTo(Employee other)
        {
            return this.Name.CompareTo(other.Name);
        }

        #endregion
    }

public class Comparison1
  {
      List<Employee> lstemp = new List<Employee>();
      public void docomparison()
      {

          Employee emp1 = new Employee();
          emp1.Name = "satya";
          emp1.JanSalary = 2000;
          Employee emp2 = new Employee();
          emp2.Name = "biswa";
          emp2.JanSalary = 300;
          Employee emp3 = new Employee();
          emp3.Name = "parsottam";
          emp3.JanSalary = 1000;
          Employee emp4 = new Employee();
          emp4.Name = "kalyani";
          emp4.JanSalary = 15000;
          EmpJanSalComparer cc = new EmpJanSalComparer();
          lstemp.Add(emp1);
          lstemp.Add(emp2);
          lstemp.Add(emp3);
          lstemp.Add(emp4);
          lstemp.Sort();

      }
  }

Here when we try to sort the list , CompareTo method will be called in Employee class and sorting/ordering can be performed.
We can use LINQ for the same . In that case we don’t have to implement IComparable in “Employee” class.

lstemp.Sort((x,y)=>x.Name.CompareTo(y.Name));

As we have to use IComparable with the class which needs comparison(Employee in above case) , we are restricted to do one type of comparison / ordering only(in this case on basis of property “Name“).

Suppose I need comparison on basis of “JanSalary” then I can’t do it now with IComparable .



IComparer<T> :

For these scenarios we have “IComparer<T>” interface using which we can have our custom comparisons. IComparer is used with a separate class as shown below.
public class EmpJanSalComparer : IComparer<Employee>
    {
        #region IComparer<Employee> Members

        public int Compare(Employee x, Employee y)
        {
            if (x.JanSalary > y.JanSalary)
            {
                return 1;
            }
            else if (x.JanSalary < y.JanSalary)
                return -1;
            else
                return 0;
        }

        #endregion
    }

    public class EmpFebSalComparer : IComparer<Employee>
    {
        #region IComparer<Employee> Members

        public int Compare(Employee x, Employee y)
        {
            if (x.FebSalary > y.FebSalary)
            {
                return 1;
            }
            else if (x.FebSalary < y.FebSalary)
                return -1;
            else
                return 0;
        }

        #endregion
    }


So as we see   it has a method “Compare” which takes 2  parameters of type “T” .And it is used with a separate class and we can use many of these classes as needed. So it gives flexibility/extendibility in terms of comparison/sorting.
Here for comparing jan month salary we used  EmpJanSalComparer” class and for comparing feb month salary we used “EmpFebSalComparer .We can use these comparer for sorting as shown below.
  EmpJanSalComparer cc = new EmpJanSalComparer();
  lstemp.Add(emp1);
  lstemp.Add(emp2);
  lstemp.Add(emp3);
  lstemp.Add(emp4);
  lstemp.Sort(cc);

Hope you enjoyed the article. Please leave a honest comment/feedback.




Tuesday, October 30, 2012

DataTemplateSelector in wpf



Say I have a scenario where I have to apply different templates to different rows of a listbox,datagrid etc  based on some condition .
Example :
I want to show employee data in a listbox . But only if age of employee’s age is greater that 20 he/she will be able to edit their name.
So here for  rows where age> 20 we need a textbox in name column and for others we need textblocks.
For these type of scenarios we need DataTemplateSelector .Below is the way to do it.

Employee class

    public class Employee
    {
        public string name { get; set; }
        public int age { get; set; }
    }

The following steps will give our solution

Step 1:

Create templates in xaml..
 
  <DataTemplate x:Key="t1">  
       <StackPanel Orientation="Horizontal">  
         <TextBlock Text="{Binding Path=age}"/>  
         <TextBlock Text="  "/>  
         <TextBlock Text="{Binding Path=name}"/>  
       </StackPanel>  
     </DataTemplate>  
     <DataTemplate x:Key="t2">  
       <StackPanel Orientation="Horizontal">  
         <TextBlock Text="{Binding Path=age}"/>  
         <TextBlock Text="  "/>  
         <TextBox Text="{Binding Path=name}"/>  
       </StackPanel>  
   </DataTemplate>  



Step 2:
Create a class that inherits DataTemplateSelector class and override its SelectTemplate method.


  public class tselector:DataTemplateSelector  
   {  
     public override DataTemplate SelectTemplate(object item, DependencyObject container)  
     {  
       Employee currEmp = item as Employee;  
       if (currEmp!=null)  
       {  
         if (currEmp.age>20)  
         {  
           return System.Windows.Application.Current.MainWindow.FindResource("t2") as DataTemplate;  
         }  
         else  
         {  
           return System.Windows.Application.Current.MainWindow.FindResource("t1") as DataTemplate;  
         }  
       }  
       return System.Windows.Application.Current.MainWindow.FindResource("t1") as DataTemplate;  
     }  
   }  



Step 3:

In ListBox set ItemtemplateSelector property



    <ListBox  ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource ResourceKey=tsel}" />

The Full xaml is

 <Window x:Class="DataTemplateSelectorSol.MainWindow"  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
     xmlns:sel="clr-namespace:DataTemplateSelectorSol"  
     Title="MainWindow" Height="350" Width="525">  
   <Window.Resources>  
     <sel:tselector x:Key="tsel"/>  
     <DataTemplate x:Key="t1">  
       <StackPanel Orientation="Horizontal">  
         <TextBlock Text="{Binding Path=age}"/>  
         <TextBlock Text="  "/>  
         <TextBlock Text="{Binding Path=name}"/>  
       </StackPanel>  
     </DataTemplate>  
     <DataTemplate x:Key="t2">  
       <StackPanel Orientation="Horizontal">  
         <TextBlock Text="{Binding Path=age}"/>  
         <TextBlock Text="  "/>  
         <TextBox Text="{Binding Path=name}"/>  
       </StackPanel>  
     </DataTemplate>  
   </Window.Resources>  
   <StackPanel>  
     <ListBox ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource ResourceKey=tsel}" >  
     </ListBox>  
   </StackPanel>  
 </Window>  


 So its simple to implement.