Yogesh Jagota's Personal Blog
small c# code snippets and free code libraries

Filtering WPF toolkit DataGrid

Saturday, 1 November 2008 20:52 by Yogesh Jagota

As you all know that Microsoft WPF toolkit version 1.0 is now available for download. I was experimenting  with the new datagrid included in the toolkit and found it to be quite good. I searched a little and found three good blog posts about sorting the datagrid. They are:

Improving Microsoft DataGrid CTP sorting performance Part I

Improving Microsoft DataGrid CTP sorting performance Part II

WPF DataGrid: Tri-state Sorting sample

Refering to the above posts will be really useful for anybody who wants to custom sort the datagrid.

Although I was able to find these good posts about sorting, I was not able to find any useful post about filtering the datagrid. After a little search I found a post titled WPF DataGrid sample: Add a preview ToolTip to a ScrollViewer. In this post, Vincent Sibal replied to a comment saying that extended filter is possible using CollectionViewSource. Learning this, I thought of writing the sorting functionality into the datagrid by my own. Initially I was unsuccessful in implementing this functionality as I was using CollectionView, but later I succeeded when I switched to ListCollectionView. I don't know if this a bug but I only tried my implementation with SubSonic and EF collections so CollectionView might not be working with only these two (although I cannot say this with confidence as I did not tried it with any other collection type).

We will be using a textbox's TextChanged method to filter the grid.

Here is how it goes:

First we declare the datagrid:

   1:  <toolkit:DataGrid Name="AreaDataGrid"  Margin="10,0,10,0" 
   2:      IsSynchronizedWithCurrentItem="False" Grid.Row="1" Height="250"
   3:      Width="400" SelectionMode="Extended" AutoGenerateColumns="False"
   4:      CanUserReorderColumns="False" GridLinesVisibility="None"
   5:      AlternationCount="2" CanUserResizeRows="False"
   6:      ItemsSource="{Binding}" >
   7:      <toolkit:DataGrid.Columns>
   8:          <toolkit:DataGridTextColumn Header="Id" Width="80"
   9:              Binding="{Binding Id}"
  10:              IsReadOnly="True"/>
  11:          <toolkit:DataGridTextColumn Header="Name" Width="200" 
  12:              Binding="{Binding Name}"
  13:              IsReadOnly="True"/>
  14:      </toolkit:DataGrid.Columns>
  15:  </toolkit:DataGrid>
  16:  


Then we attach the collection with the datagrid in the Loaded method of the window:

   1:  ListCollectionView dgcv = 
   2:      new ListCollectionView(new AreaCollection().Load());
   3:  dgcv.Filter = SearchFilter;
   4:  
   5:  AreaDataGrid.ItemsSource = dgcv;
   6:  


Now we write the SearchFilter method which actually filters the collection:

   1:  private bool SearchFilter(object sender)
   2:  {
   3:      if (tbSearch == null)
   4:          return true;
   5:  
   6:      // tbSearch is the textbox which contains the
   7:      // text to be filtered...
   8:      string search = tbSearch.Text;
   9:  
  10:      // convert the sent object to Area object...
  11:      Area item = (Area)sender;
  12:  
  13:      if (!search.IsNullOrEmpty())
  14:      {
  15:          if (item.Id.ToString().StartsWith(search) ||
  16:              item.Name.StartsWith(search))
  17:          {
  18:              return true;
  19:          }
  20:  
  21:          return false;
  22:      }
  23:  
  24:      return true;
  25:  }
  26:  


Finally in the TextChanged method of the text box which is used to filter the entries, we use this code:

   1:  ((ListCollectionView)AreaDataGrid.ItemsSource).Refresh();


This is all we need to do. I am not explaining much as most of the code is self explanatory. I tested this way of filtering with 20000 records in the datagrid and found it to be quite fast enough to be used in production scenarios, although it's real test will come when it will be used with more records.

Hope this is helpful to people who need such a functionality to filter the datagrid. I will be posting more about DataGrid soon.

PS: The problem I found with CollectionView was that if I set a breakpoint in the TextChanged method and check the ItemsSource of the datagrid after ((ListCollectionView)AreaDataGrid.ItemsSource).Refresh();, I found that it DID got filtered, but the corresponding changes do not reflect in the datagrid. It still showed all the items in the datagrid. Changing to ListCollectionView sorted out this issue.

Del.icio.usDigg It!DZone It!kick it on DotNetKicks.com
Tags:   ,
Categories:   C# | WPF
Actions:   E-mail | Permalink | Comments (2) | Comment RSSRSS comment feed

How to place all WPF commands and related events centrally in a static class

Friday, 25 July 2008 00:33 by Yogesh Jagota

Commands in WPF can be defined and maintained in a static class. The commands need to be bind with the window whose controls use them. To bind the commands, events CanExecute and Executed events need to defined in the window itself. But most of the times, people want all their commands and related CanExecute and Executed events in one single static class which makes it easier to maintain the program logic from one central place. The purpose of this article which I posted on codeproject is to tell you how to enable this facility.

Del.icio.usDigg It!DZone It!kick it on DotNetKicks.com
Tags:  
Categories:   C# | WPF
Actions:   E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

Ways of making a WPF application Single Instance

Thursday, 3 July 2008 00:11 by Yogesh Jagota

There are many ways of making a WPF application a single instance application but such solutions are mostly difficult to find, or even if you manage to find one, they solve one problem and create another. Here are two ways of doing the same, and a solution created by me which is a hybrid of two posts I saw on the net.

1. Remove the App.xaml file completely
The first method is by removing the App.xaml completely which is perfectly demonstrated by this MSDN article.

2. Use Interop services
The second method is by using COM and Interop services which makes your application single instance with almost no code at all. This method was posted on this thread by Marco Zhou. A example is given below:

   1:  <cc:WpfApplication x:Class="SingleInstanceAppDemo.App"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:cc="clr-namespace:Sheva.Windows"
   5:      IsSingleInstance="True"
   6:      StartupUri="Window1.xaml">
   7:   
   8:    <cc:WpfApplication.Resources>
   9:      <Style TargetType="{x:Type TextBlock}">
  10:        <Setter Property="Foreground" Value="White"/>
  11:        <Setter Property="FontSize" Value="40"/>
  12:        <Setter Property="FontWeight" Value="Bold"/>
  13:        <Setter Property="FontFamily" Value="Verdana"/>
  14:      </Style>
  15:    </cc:WpfApplication.Resources>
  16:  </cc:WpfApplication>
  17:  

The source can be downloaded here.

3. Making application Single Instance without deleting App.xaml or using Interop

This is the way I pefer it but I tried to search the net but did not found any way of doing anything like that. Then I stumbled upon this post which shows you how to provide your own Main() method in a WPF application. As soon as I found this, I made a hybrid of this Main() method output and the MSDN article to successfully create a solution to the problem at hand.

All you have to do is, make the build action of App.xaml to ApplicationDefinition as mentioned in this post and write this in your App.cs:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Configuration;
   4:  using System.Data;
   5:  using System.Linq;
   6:  using System.Windows;
   7:  
   8:  using Microsoft.VisualBasic.ApplicationServices;
   9:  
  10:  namespace YourNamespace
  11:  {
  12:      public class EntryPoint
  13:      {
  14:          [STAThread]
  15:          public static void Main(string[] args)
  16:          {
  17:              SingleInstanceManager manager = new SingleInstanceManager();
  18:  
  19:              manager.Run(args);
  20:          }
  21:      }
  22:  
  23:  // Using VB bits to detect single instances and process accordingly:
  24:  //  * OnStartup is fired when the first instance loads
  25:  //  * OnStartupNextInstance is fired when the application is re-run again
  26:  //    NOTE: it is redirected to this instance thanks to IsSingleInstance
  27:      public class SingleInstanceManager : WindowsFormsApplicationBase
  28:      {
  29:          YourApplication app;
  30:  
  31:          public SingleInstanceManager()
  32:          {
  33:              this.IsSingleInstance = true;
  34:          }
  35:  
  36:          protected override bool OnStartup(
  37:              Microsoft.VisualBasic.ApplicationServices.StartupEventArgs e)
  38:          {
  39:              // First time app is launched
  40:              app = new YourApplication();
  41:              // You have to add this line to the MSDN sample
  42:              // to make it work here...
  43:              app.InitializeComponent();
  44:              app.Run();
  45:  
  46:              return false;
  47:          }
  48:  
  49:          protected override void OnStartupNextInstance(
  50:              StartupNextInstanceEventArgs eventArgs)
  51:          {
  52:              // Subsequent launches
  53:              base.OnStartupNextInstance(eventArgs);
  54:              app.Activate();
  55:          }
  56:      }
  57:  
  58:      /// <summary>
  59:      /// Interaction logic for App.xaml
  60:      /// </summary>
  61:      public partial class YourApplication : Application
  62:      {
  63:          protected override void OnStartup(
  64:              System.Windows.StartupEventArgs e)
  65:          {
  66:              base.OnStartup(e);
  67:  
  68:              /* And remove this ...
  69:              // Create and show the application's main window
  70:              MainWindow window = new MainWindow();
  71:              */
  72:  
  73:              window.Show();
  74:          }
  75:  
  76:          public void Activate()
  77:          {
  78:              // Reactivate application's main window
  79:              this.MainWindow.Activate();
  80:          }
  81:      }
  82:  }
  83:  

Download the file here.

We use InitializeComponent in the OnStartup routine (line 43) to make the main window run and show immediately. As your App.xaml already contains this information as the StartupUri, InitializeComponent will itself determine which is the window you want. A good side effect of this approach is that you don't need to have the name of your main window defined explictly.

As the main window is already visible, we don't need to show the main window yet again in YourApplication.OnStartup, do we? If we do, we will have two copies of the main window running together.To avoid this, we have to comment line 69 and 70.

But this approach has a flaw. If you use this approach, every global resource you will define in the App.xaml will not show up in the designer windows of both Visual Studio 2008 and Expression Blend. Although the application will show up the resources just fine when ran. I am trying to find a workaround to this problem and will post it as soon as I find it.

I hope this helps everybody who is looking for such a hybrid solution.

Del.icio.usDigg It!DZone It!kick it on DotNetKicks.com
Tags:  
Categories:   C# | WPF
Actions:   E-mail | Permalink | Comments (3) | Comment RSSRSS comment feed