Dynamic DataGridView filtering in C#

Posted in .NET 2.0 | .NET UI Controls | DataGridView at Wednesday, 03 December 2008 21:31 GMT Standard Time

In a few lines of code, we can convert a DataGridView control in a powerfull fully searchable control with dynamically generated filters for each column of field the DataGridView is binded to.

How does it work?

  1. The DataGridControl is binded to the datasource.
  2. The user hits the button (or shortcut) to start the dynamic filtering feature.
    1. A new form is dinamycally generated on top of the DatagridView control (it has some transparency on it so you can still see the DataGridView data on the background)
    2. The form contains several textboxes, one per each column or field the DataGridView datasource contains (you can change that behaviour)
    3. When the user starts entering text in any of the textboxes, the filter condition is applied to the underlying datasource (for every keystroke) so you can see how the filtered data looks on the DataGridView as you are typing.
    4. You can, of course define filters for one or more textboxes (fields) at the time, so you can work with multiple filters for many columns/fields.
  3. You can choose whatever you create a dynamically generated search form that contains all the fields on DataGridView datasource, or the ones of your choice (video and example code shows both cases).


This screenshot shows the dynamically search form generated with all the fields of the underlying DataGridView

This screenshot shows the dynamically search form generated with some of the fields of the underlying DataGridView

Demo Video

(sorry about the video quality. if you are interested, please download the demo. it may be worthy)

Some code:

        /// Build texbox controls for dinamyc search form based on fields collection
        private void BuildControls(List fields) {
            int top = 10;
            bool focused = false;

            #region Loop for each field
            foreach (field f in fields)
                Label label = new Label();
                label.Text = f.FriendlyName + ":";
                label.Top = top;
                label.Left = 5;
                label.AutoSize = true;

                TextBox textbox = new TextBox();
                textbox.TextChanged += new EventHandler(textBox_TextChanged);
                textbox.Tag = f.Field;
                textbox.Top = top;
                textbox.Left = 68;
                textbox.Width = this.Width - 80;
                if (!focused) {

                    textbox.Focus(); //the first control focused
                    focused = true;
                top += 35;

            this.Height = top + 30;

  // Raise event to parent form if textbox content changes
        private void textBox_TextChanged(object sender, EventArgs e)
            if (TextChanged != null)

        #region Shortcuts
        private void frmSearch_KeyDown(object sender, KeyEventArgs e)
            if ((e.KeyCode == Keys.Escape) || (e.KeyCode == Keys.Enter))

namespace dynamicGridFilter
    /// This struct contains information about each field to be filtered along with filter value, if exists.
    public struct field {
        public string Field;
        public string FriendlyName;
        public string Value;

namespace dynamicGridFilter
    public delegate void SearchContextChangedHandler(List fields);

Complete source code available to download:

Includes full source code and compiled binary (use it at your own risk)
dynamicGridFilter.zip (50,23 KB)

What's next?!

If could have gone all the way up to creating a custom control that inherits from DataGridView and has all the logic embedded. Well, you have here all you need for doing it yourself, so don't call me lazy! :P

Comments are welcome!

AddThis Social Bookmark Button

Simple download protection for files and document using ASP.NET / C#

Posted in .NET 2.0 | Authentication | Security at Monday, 24 December 2007 14:36 GMT Standard Time
Really really simple. Feel free to write your own custom authentication method to fit your project context.

This is download.aspx:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="download.aspx.cs" Inherits="download" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Download Page</title>
    <form id="form1" runat="server">

and this is the code behind that file:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class download : System.Web.UI.Page 

    /// Show error to user and close response object.
    private void WriteError(string error) {

    /// Check authentication ticket
    private bool Authenticated() {
        //whatever is a session ticket, membership provider base, container in a coded URL querystring parameters, etc..
        return true;    

    private string GetRepositoryFolder() {
        System.Configuration.AppSettingsReader r = new AppSettingsReader();
        return r.GetValue("RepositoryFolder", typeof(string)).ToString();                

    protected void Page_Load(object sender, EventArgs e)
        if (!Authenticated()){
            WriteError("You are not allowed to download this file");
        else if (Request.QueryString["id"] == null)
            WriteError("Missing parameter : id");
        string filePath = System.IO.Path.Combine(GetRepositoryFolder(),Request.QueryString["id"]);
        System.IO.FileInfo file = new System.IO.FileInfo(filePath);
        if (!file.Exists)
            WriteError("File doesn't exists");
        else {
            Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);
            Response.AddHeader("Content-Length", file.Length.ToString());
            Response.ContentType = "application/octet-stream";
If successfully authenticated, you will be able to directly download the file:

If not, you will get an error message:

You are not allowed to download this file

AddThis Social Bookmark Button

Querying Youtube API using C#

Posted in .NET 2.0 | Google API | YouTube at Thursday, 20 December 2007 22:40 GMT Standard Time

This example shows how to use Google Data API to query and obtain videos from YouTube. There is also a downloadable zip file with the example in ASP.NET / C#.

  1. First of all, you need to download the Google Data APIs Client Libraries.
  2. Secondly, use the following namespaces:
    using Google.GData.Client;
    using Google.GData.Extensions;
  3. Use the service by querying by tag, or by search (you can also query related items, etc..):

        //by tag
        //feel free to change number of items, by there is a limit of 50, I believe. 
        //If you want to retreive more, you have to do a loop (retrieve 1-50, then 51 to 100, etc)
        protected void btoGo_Click(object sender, EventArgs e)
            string url = "http://gdata.youtube.com/feeds/videos/-/" + this.txtTag.Text;
            AtomFeed myFeed = GetFeed(url, 1, 20);
        //by search
        //feel free to change number of items, by there is a limit of 50, I believe. 
        //If you want to retreive more, you have to do a loop (retrieve 1-50, then 51 to 100, etc)
        protected void btoSearch_Click(object sender, EventArgs e)
            string url = "http://gdata.youtube.com/feeds/videos?q=" + this.txtSearch.Text;
            AtomFeed myFeed = GetFeed(url, 1, 15);
  4. Use the following methods, or similars to get and display the Feed:

        /// Create and returns and Google.GData.Client.AtomFee from url with the specific start and number of items
        private static AtomFeed GetFeed(string url, int start, int number)
            System.Diagnostics.Trace.Write("Conectando youtube at " + url);
            FeedQuery query = new FeedQuery("");
            Service service = new Service("youtube", "exampleCo");
            query.Uri = new Uri(url);
            query.StartIndex = start;
            query.NumberToRetrieve = number;
            AtomFeed myFeed = service.Query(query);
            return myFeed;
        /// Renders feed in example aspx page
        private void DisplayFeed(AtomFeed myFeed)
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            foreach (AtomEntry entry in myFeed.Entries)
                #region render each
    Title: "); sb.Append(entry.Title.Text); sb.Append("
    Categories: "); foreach (AtomCategory cat in entry.Categories) { sb.Append(cat.Term); sb.Append(","); } sb.Append(RenderVideoEmbedded(getIDSimple(entry.Id.AbsoluteUri))); sb.Append("
    Published on: "); sb.Append(entry.Published); #endregion } this.lblResults.Text = sb.ToString(); } private string RenderVideoEmbedded(string idSimple) { return string.Format("
    ", idSimple); }
Related resources:

Download example web site project in C#:
youtubeAPIExample.zip (65,91 KB)

AddThis Social Bookmark Button

Inheriting from System.Exception or System.ApplicationException?

Posted in .NET quirks at Monday, 17 December 2007 18:02 GMT Standard Time
Yes, there is a bit of confusion about this (also read this post).

I understand that the right way to create your application specific exceptions classes is to inherit from Exception class:
public class MyCustomEx : System.Exception{
   //class implementation
And if in any case you have to throw a general application exception that has not specific type, throw a System.ApplicationException:
throw new System.ApplicationException("Error retrieving data. The web service may be down");

AddThis Social Bookmark Button

Using a RAM drive to compile Visual Studio .NET projects

Posted in .NET 2.0 | Performance | Productivity | Visual Studio 2005 | RAM disk at Saturday, 15 December 2007 10:18 GMT Standard Time
(updated 22 dec 07)

After reading some blog posts like this I decide to test on my computers how important is hard disk speed in order to increase your productivity when compiling Visual Studio solutions with more than a few project.

I also wanted to see the gain if I placed my solution (more than 25 projects) in a RAM drive disk instead of the hard disk (be sure you backup frecuently if you do that, or get one of this things to avoid data lost).

In order to create a RAM drive, I download the trial version of Virtual Hard Drive Pro
 from FarStone.

There are the results:

1. Environment hardware information

My desktop:
  1. CPU: AMD Sempron, 1800 MHz
  2. RAM: 2 x (1 Gb Kingston)
  3. Hard disks (2):
    1. System: ATA-100 40 Gb Barracuda 7200 rpm. Average seek: 8.5 ms. Buffer size: 2Mb. Average read speed: 40 Mb/second (*).
    2. Data and code: SATA 200 Gb Barracuda 7200 rpm. Average seek: 8.5 ms. Buffer size: 8 Mb. Average read speed: 50 Mb/second (*).
  4. RAM drive speed (read): 900 Mb/sec
My laptop (Toshiba Satellite Pro M70):
  1. CPU: Intel Pentium Celeron 1.73 Ghz
  2. RAM: 2 x (512 Mb Toshiba)
  3. Hard disk (1) : Toshiba 5400 rpm. Average read speed: 30 Mb/second (*)
  4. RAM drive speed (read): 1500 Mb/sec
* (Hard disk speed tests have been done with HD_Speed from www.steelbytes.com)

2. The Microsoft Visual Studio solution

A Microsoft Visual Studio .NET 2005 solution with 26 projects (Windows Forms and Windows library projects mainly), in C#.

3. Results


Desktop machine:

a) Compilation on hard disk (no RAM Drive installed yet): 72 seconds*
b) Compilation on a 500 MB RAM Drive : 71 seconds*

Laptop machine:

a) Compilation on hard disk (no RAM Drive installed yet): 65 seconds*
b) Compilation on a 400 MB RAM Drive : 32 seconds*

*(average of three compilations)

4. Conclusions:

Seems like my desktop doesn't have much processor power, and the bottleneck is mainly on the CPU, since there is not much different results on readig from a 50 Mb/seconds hard disk or from a 900 MB/second one (the RAM Drive)

My laptop, instead has a slower disk drive but more processor power, so there is a lot of difference between compiling from hard disk and RAM drive. (it took half the time on the RAM disk - also RAM disk read speed is higher than in the desktop)

I still will do more tests with some other combinations of CPU and hard disk..

Feel free to do the above tests yourself and share the results.

  • 22 dic 07: Added test with laptop. Desktop tests done again.

AddThis Social Bookmark Button

Storing a collection on your app.config using Section Handlers

Posted in app.config | Section Handlers at Tuesday, 16 October 2007 19:22 GMT Daylight Time
If you followed this article, you learned how to implement the IConfigurationSectionHandler interface in a custom class to store and read data on or from the app.config (or web.config) using your own typed data structure.

It is also common that you need to store a collection of values on that app.config. Example (see ProxyConfiguracionProcesos tag):

(note that the above image has been cropped to fit the space)

If that's the case, you can create a class to store a single item (in this case a "Proceso"):
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;

namespace PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler
struct ProxyConfigurationItem
public string Name;
public string AssemblyLocation;
public string ClassName;
public int ExecutionInterval;
public byte Priority;
public byte LogLevel;
public int AlarmIfTakesMore;
public int AlertIfTakesMore;

and then parse every item on the custom section using your SectionHandler class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Xml;

namespace PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler
class ProxyConfigurationHandler : IConfigurationSectionHandler
public ProxyConfigurationHandler() { }

public object Create(object parent,
object configContext, System.Xml.XmlNode section)
List<ProxyConfigurationItem> items = new List<ProxyConfigurationItem>();
System.Xml.XmlNodeList processesNodes= section.SelectNodes("Proceso");

//process each Node "Proceso" foreach (XmlNode processNode in processesNodes){
ProxyConfigurationItem item = new ProxyConfigurationItem();
item.Name = processNode.Attributes["Name"].InnerText;
item.AssemblyLocation = processNode.Attributes["Assembly"].InnerText;
item.ClassName = processNode.Attributes["Clase"].InnerText;
item.ExecutionInterval = Convert.ToInt16(processNode.Attributes["Interval"].InnerText);
item.Priority = Convert.ToByte(processNode.Attributes["Prioridad"].InnerText);
item.LogLevel = Convert.ToByte(processNode.Attributes["LogLevel"].InnerText);
item.AlertIfTakesMore = Convert.ToInt32(processNode.Attributes["AlertIfTakesMore"].InnerText);
item.AlarmIfTakesMore = Convert.ToInt32(processNode.Attributes["AlarmIfTakesMore"].InnerText);
return items;

Also note that you will need a section handler declaration like this:
<section name="ProxyConfiguracionProcesos" type="PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationHandler, PIENSA.UI.Service.ComunicacionAutomata" />
where the first part in the type attribute is the custom class that implements the IConfigurationSectionHandler interface and the second is the assembly where that class is located.

Now, from any part of the code, you can get your collection of processes, as defined on the app.config:

  private List<PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationItem> GetProcessesConfiguration() {
List<PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationItem> processesConfig= (List<PIENSA.UI.Service.ComunicacionAutomata.ConfigurationHandler.ProxyConfigurationItem>)System.Configuration.ConfigurationSettings.GetConfig("ProxyConfiguracionProcesos");

AddThis Social Bookmark Button