Quick and dirty “dirty checking” for Windows form, C#

While designing a CRF, little short-cuts can save you lots and lots of time.  This time-saver is straight-forward code-wise and should fit right into your C# code without much modification.

During CRF design time, I often have CRFs that have dozens and dozens of input controls (radio buttons, dropdowns, listboxes, textboxes…).  I’d like to add OnChange handlers to all of these input controls so that I know when a change has been made. Basically, what it comes down to is I want to know when the form is dirty, but I don’t want to add all the handlers by hand.  Fortunately, I didn’t have to and neither do you!

The code below is a recursive function which traverses the Control tree.  Whenever it locates a control that can be classified as an input control, it attaches an event handler.

void  AddOnChangeHandlerToInputControls(Control ctrl)
{
    foreach (Control subctrl in ctrl.Controls)
    {
        if (subctrl is TextBox)
            ((TextBox)subctrl).TextChanged +=
                new EventHandler(InputControls_OnChange);
        else if(subctrl is CheckBox)
            ((CheckBox)subctrl).CheckedChanged +=
                new EventHandler(InputControls_OnChange);
        else if(subctrl is RadioButton)
            ((RadioButton)subctrl).CheckedChanged +=
                new EventHandler(InputControls_OnChange);
        else if(subctrl is ListBox)
            ((ListBox)subctrl).SelectedIndexChanged +=
                new EventHandler(InputControls_OnChange);
        else if(subctrl is ComboBox)
            ((ComboBox)subctrl).SelectedIndexChanged +=
                new EventHandler(InputControls_OnChange);
        else
        {
            if (subctrl.Controls.Count > 0)
                this.AddOnChangeHandlerToInputControls(subctrl);
        }
    }
}

Keep in mind the recursion is necessary, because the Controls property field only lists a control’s immediate children. Those immediate children may have children of their own. That’s right… exactly like a family tree! The use of recursion creates an elegant way to traverse the control’s control tree.

I’ve only picked up on the simplest input controls. It should be straight-forward to extend this for other controls like a DateTimePicker or a CheckedListBox (although the CheckedListBox is a little tricky because it uses a different type of event handler).

void InputControls_OnChange(object sender,  ItemCheckEventArgs e)
{
    // Do something to indicate the form is dirty like:
    // this.formIsDirty = true;
}

All the event handler does is set a flag which indicates the form is dirty.  How you process the dirty flag is up to you, and there you have it! Hopefully, you’ve saved a few hours of manually adding event handlers!

Tags: ,

7 Responses to “Quick and dirty “dirty checking” for Windows form, C#”

  1. Wayne Says:

    Thanks, I think I’ll steal this for my own nefarious purposes.

    Actually, I’m going to use this to detect the ‘isDirty’ on each tab change (36 tabs, 300+ controls, yes, I know…)

    Thanks for providing the code!

  2. Wayne Says:

    Hmm, trying to work out an issue with your code. I get a “No overload for ‘InputControls_OnChange’ matches delegate ‘System.EventHandler” error. Still debugging it….using .net 2.0. Any clue?

  3. Wayne Says:

    Figured it out. Instead of the handler you have above, using:

    void InputControls_OnChange(object sender, EventArgs e)

    does the trick. I’ll update you with anything else I find to round out your post. Cheers!

  4. Wayne Says:

    Seems to work like a charm! Thanks! You saved me a few hours (possible much more) with this snippet.

  5. Anthony Says:

    I implemented a similary is dirty logic but it is dynamic and based on the event. It uses some reflection so it may be a bit slower then your implementation, but it allows you to add new events without code changes. Let me know what you think:

    private List _dirtyEvents = new List();
    _dirtyEvents.Add(new DirtyEvent(“TextChanged”));
    _dirtyEvents.Add(new DirtyEvent(“SelectedIndexChanged”));

    private void recurseControls(Control.ControlCollection Controls)
    {
    foreach (Control c in Controls)
    {
    if (c is INotifyPropertyChanged)
    {
    ((INotifyPropertyChanged)c).PropertyChanged += new PropertyChangedEventHandler(DirtyFormBase_PropertyChanged);

    }
    for (int i = 0; i < _dirtyEvents.Count; i++)
    {
    EventInfo ei = c.GetType().GetEvent(_dirtyEvents[i].EventName);
    if (ei != null)
    {
    Delegate textChangedDelegate = Delegate.CreateDelegate(ei.EventHandlerType, typeof(DirtyFormBase).GetMethod(“GenericHandler”, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.IgnoreCase));
    ei.AddEventHandler(c, textChangedDelegate);
    }
    }

    if (c.HasChildren)
    recurseControls(c.Controls);
    }
    }

    private static void GenericHandler(object sender, EventArgs args)
    {
    DirtyFormBase form = (DirtyFormBase)((Control)sender).FindForm();
    if (!form.SuspendIsDirtyCheck)
    {
    form.IsDirty = true;
    }
    }

    This is a bit more code as it is a base class for all forms that need dirty logic. If you are interested let me know and I will e-mail the class.

  6. Guest Says:

    All this is handled on text change event of a textbox or selected index changed of drop downs. as i am assigning values to controls on grid cell double click, that time also the change event get fired, i only want to track that is user made some changes in existing values or not. then only i want to prompt to save changes.
    can anyone guide me?

  7. Guillaume JAY Says:

    This is a very good post. As you said, a straight forward time saver. This should go to the new blog.

    Anthony, I’d like to see your whole class.

    email guillaume(dot)jay(at)gmail(dot)com

    Thanks !

Leave a Reply