Friday, January 25, 2008

Observer Pattern

Using a change observer in your application is really useful in windows forms applications where you have textboxes, checkboxes etc. that are inherently designed for event generation. A great implementation of the pattern is to observe a dirty property on your form letting you know if anything has changed.

A great starting point to learning the basics of the Observer pattern is on wikipedia. The subject (your form) will maintain a list of observers, remove observers from the list and notify all observers of any changes. Here is the interface for the subject.




public interface ISubject
{
void RegisterObserver(IObserver observer);
void UnRegisterObserver(IObserver observer);
void NotifyObservers();
}


When you implement this interface on your form, you create your observer and register it. For my purposes I only need to create one observer to monitor changes on any of my form controls. Here is the interface for the observer.





public interface IObserver
{
void Update();
bool IsDirty { get;}
}




As soon as a change occurs on any control I need to let the change observer know. So, whenever a TextChanged event occurs on my controls I can handle it and notify the change observer that something has happened by calling the NotifyObservers method.




public void NotifyObservers()
{
foreach (IObserver observer in changeObservers)
{
observer.Update
}
}


NotifyObservers will loop through all my registered observers and call the Update method. The Update method on the observer is really simple....it just flips the dirty property to true.

So now my form is dirty. If I happen to for example close my form without saving I can check with the changeobservers and see if there is anything dirty. If there is I can let the user know and ask if they want to save.





public void HandleFormClosing(object sender, CancelEventArgs e)
{
DialogResult result;

if (FormIsDirty)
{
result = MessageBox.Show
("You have unsaved Changes. Do you want to save?", "Unsaved", MessageBoxButtons.YesNoCancel);
if (result.Equals(System.Windows.Forms.DialogResult.Yes))
MessageBox.Show("Saved!");
else if (result.Equals(System.Windows.Forms.DialogResult.No))
MessageBox.Show("Not Saving!");
else
{
e.Cancel = true;

}
}
else
MessageBox.Show("No changes detected.");

}


One limiting feature of this implementation is that there is very little re-use.
I need to wire up all of my Handlers for each control on the form and then call NotifyObservers. If I have another form and want to do the same thing I have to write the same thing again. A better solution would be to push this event handling onto the ChangeObserver to register the controls and the event to fire whenever a change occurs.

public void RegisterControls(Control.ControlCollection controls)
{
foreach (Control control in controls)
{
if (control.GetType().Equals(typeof(TextBox)))
control.TextChanged += new EventHandler(HandleChange);
if (control.GetType().Equals(typeof(CheckBox)))
{
CheckBox checkbox = (CheckBox) control;
checkbox.CheckedChanged +=new EventHandler(HandleChange);
}
if (control.GetType().Equals(typeof(RadioButton)))
{
RadioButton radioButton = (RadioButton) control;
radioButton.CheckedChanged +=new EventHandler(HandleChange);
}
}
}

void HandleChange(object sender, EventArgs e)
{
Update();
}


So now I can reuse this code on any form that I want to observe.
If I have other types of controls I can throw them into the RegisterControls method.

0 Comments:

Post a Comment

<< Home