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!

April 13th, 2009 at 5:49 pm
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!
April 13th, 2009 at 6:19 pm
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?
April 13th, 2009 at 6:24 pm
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!
April 13th, 2009 at 7:32 pm
Seems to work like a charm! Thanks! You saved me a few hours (possible much more) with this snippet.
March 11th, 2010 at 9:07 am
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.
March 17th, 2010 at 4:59 am
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?
March 30th, 2010 at 7:22 am
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 !