OnChanged Event for User Controls

We often should know, whether were any changes on our form or not. And if such changes were, some process is carried out: just a message, some property has to be changed to "false", etc. There are no problems if we use "standard" controls (controls from the Toolbox: TextBox, ComboBox, etc), because every such control has OnChanged event (I mean TextChanged, SelectedIndexChanged, etc.) and we can catch every change. But... a little more interesting situation can be.

 

For example: we use UserControl1 that contains four TextBoxes, three ComboBoxes and UserControl2 that, in its turn, contains two TextBoxes and UserControl3 that, in its turn..., and so on; we have to catch any change on our form and when the number of changes of the UserControl2 will be equaled 2 we should "switch off" (enabled = false) this control.... In this case we have to "supply" the controls with event OnChanged.

 

In this article I will show some ways how to create user controls with OnChanged event. The examples are written using C# for Windows applications.

 

First of all let's divide our OnChanged events(which we are going to create) into two big parts: the events that have no event data and events that have.

 

The first case: The events that have no event data.

 

In this case in the code page of your user control you have to declare a delegate for OnChanged event and an event itself :

 

public delegate void Changed_EventHandler(object sender,EventArgs e);

public event Changed_EventHandler C_Changed;

 

But  .NET ( !!! ) have made part of this work: there is a method that handles the event that has no event data. And this is EventHandler Delegate. Using that you can replace (if you want) two lines of the above code by one:

 

public event EventHandler C_Changed;

 

Now you can add the next code

 

if (C_Changed != null) {C_Changed(this, e);}

 

to every OnChanged event of your control (again, saying "OnChanged event" I mean TextChanged, SelectedIndexChanged, etc.) and to force the control "to notice" each change occurring on it.

 

But more "elegant and programmer" way (if it is possible to say so) is use of a method, named OnC_Changed, that raises the event in all cases, that you need. In this case you can easily to use some method doOnChanged that executes the process logic for the C_Changed event :

 

protected virtual void OnC_Changed ( EventArgs e )               

{

if (C_Changed != null)

{

//Invokes the delegates.

C_Changed(this, e);

doOnChanged();

}

}

 

And now let's pass to practice and create some test project that has to have a Windows form with a user control "UC_TreeText". The last one consists of : textbox  "textBox_C1", textbox  "textbox _C2" with ReadOnly property set to "true" (just  to see total of changes of the UC_TreeTex control), user control "UC_TwoText". In its turn the UC_TwoText control consists of : textbox  "textBox_C1" ,  textbox  "textBox_C2", textbox  "textBox_C3" with ReadOnly property set to "true" (just  to see total of changes of the UC_TwoText control).

 

The process logic at changes of the UC_TwoText control is the following: at each change of the control color of the textBox_C3 control should vary and we should see total of changes of the UC_TwoText control. The process logic at changes of the UC_TreeText control is the following: at each change of the control we should see total of changes of the UC_TreeText control.

 

Our solution (named "WinProjects_Test") includes (by analogy) two projects: Output Type of the first project is "Windows Application" (named "OnChanged_UC") and Output Type of the second  is "Class Library" (named "OnChanged_UC_Controls"). The second project includes two User Controls: "UC_TreeText" and "UC_TwoText". The first project includes only a Windows form: "Form1".

 

The sequence of creation of our projects is the following.

 

After creating the solution with two projects add to the UC_TwoText control three textbox controls (with the names specified above). Double-click textBox_C1 and textBox_C2 to add TextChanged events and then add to the code page of the UC_TwoText control the next code:

 

#region "forEventChanged"

 

public event EventHandler C_Changed;           

                                                 

private int NumChanges = 0;//the number of changes

 

// The protected OnC_Changed method raises the event by invoking

// the delegates. In this case the sender is always this.

protected virtual void OnC_Changed(EventArgs e)                                          

{

if (C_Changed != null)

{

//Invokes the delegates.

C_Changed(this, e);                                           

doOnChanged();

}

}

//The method doOnChanged executes the process logic

//for "C_Changed" event.

private void doOnChanged()

{

//increase value of NumChanges by one

++NumChanges;

//changes BackColor to another color

if (textBox_C3.BackColor != Color.Yellow)

{

textBox_C3.BackColor = Color.Yellow ;

}

else

            {

                        textBox_C3.BackColor = Color.LightGreen ;

            }

            //write down and show the number of changes

            textBox_C3.Text = NumChanges.ToString();

}

#endregion

 

private void textBox_C1_TextChanged(object sender, System.EventArgs e)

{

            OnC_Changed(e);                    

}

 

private void textBox_C2_TextChanged(object sender, System.EventArgs e)

{

            OnC_Changed(e);                       

}

 

Now add to the UC_TreeText control two textbox controls (with the names specified above) and the UC_TwoText control. Double-click textBox_C1; then right-click on the UC_TwoText and select Properties, click "Events" button, double-click on "C_Changed". Now the UC_TreeText.cs code page are ready for adding a few lines of the following code:

 

#region "forEventChanged"

public event EventHandler C_Changed;

private int NumChanges =0;//the number of changes

 

// The protected OnC_Changed method raises the event by invoking

// the delegates. In this case the sender is always this.

protected virtual void OnC_Changed(EventArgs e)                              

{

            if (C_Changed != null)

            {

                        //Invokes the delegates.

                        C_Changed(this, e); 

                        doOnChanged();

            }

 

}

 

//The method doOnChanged executes the process logic

//for "C_Changed" event.

private void doOnChanged()

{

            ++NumChanges;

            textBox_C2.Text = NumChanges.ToString();

}

#endregion

 

private void textBox_C1_TextChanged(object sender, System.EventArgs e)

{

            OnC_Changed(e);

}

 

private void uC_TwoText1_C_Changed(object sender, System.EventArgs e)

{

            OnC_Changed(e);

}

 

Inside of the OnChanged_UC project from "My User Controls" tab drag and drop on the Form1 our UC_TreeText control.

 

That is all for the first part and you may test the project. Just start the project and be convinced that all our tasks (see above) are carried out.

 

The second case: The events that have event data.

 

Now we have to have some class that holds event data. In order to "facilitate" process to declare delegate for our OnChanged event and to create the event data class I recommend the following steps. Within the same namespace of the project of our user controls two classes have to be contained : a class for delegate declaration (public class OnChangedEvent) and a class that contains the data for the OnChanged event and derives from System.EventsArgs (public class C_EventArgs : EventArgs) .

 

To the OnChangedEvent class add the delegate declaration:

 

// Delegate declaration.

public delegate void Changed_EventHandler(object sender,C_EventArgs e); 

 

The C_EventArgs class may have a few constructors, properties, methods and has to  define some logic processes that corresponds to the certain changes of the certain control.

 

Now, to create OnChanged event in some user control of your project just add to the code page of this control:

 

// The event member that is of type Changed_EventHandler

//(class OnChangedEvent.cs).

public event OnChangedEvent.Changed_EventHandler C_Changed; 

 

To raise the event you use a method similar to the method considered above with at least one differences: you have to use "C_EventArgs":

 

// The protected OnC_Changed method raises the event by invoking

// the delegates. In this case the sender is always this.

protected virtual void OnC_Changed(C_EventArgs e)                         

{

            if (C_Changed != null)

            {

                        //you can set some properties here : e.[property_1];     

                        //Invokes the delegates.

                        C_Changed(this,e);                                            

                        doOnChanged ();

            }

}

 

Now we are going to pass to practice. Let's a little change our test project.

 

One of  the logical processes for our control, that we are going to use for our OnChanged event, is the following : after the certain quantity of changes of the control the last one has to change its "Enabled" property to "false" and to inform us on that (some message). Of course you can add some more processes, but for our example enough one process (and accordingly one "Constructor"). We are going to use this process only for the UC_TwoText control.

 

Add to the UC_TwoText  project class "OnChangedEvent.cs". Add to the code page class C_EventArgs and then add code for our logical process:

 

public class OnChangedEvent

{

            public OnChangedEvent()

            {

                        //

                        // TODO: Add constructor logic here

                        //

            }

            // Delegate declaration.

            public delegate void Changed_EventHandler(object sender,C_EventArgs e); 

}

//=========================================================

// Class that contains the data for

// the C_Changed event. Derives from System.EventArgs.

public class C_EventArgs : EventArgs

{

            private readonly int i_Changes = 0;

            private int i_NumChanges = 0;

            private Control o_Control ;

     

            //Constructor.

            public C_EventArgs(Control oControl, int iChanges)

            {

                        this.i_Changes = iChanges;

                        o_Control = oControl ;

            }

 

            // The C_Changes property returns the maximal number of changes ,

            // which can be made for the current control.

            public int C_Changes

            {    

                        get

                        {

                                    return i_Changes;

                        }     

            }

     

//The C_NumChanges property allows to set the number of changes

//( for the current control ) when the C_Changed event is generated.

//Here there is some logic process: to show a message and to change

//"Enable" property to "false" when the number of changes equel

//the maximal allowed number of changes . 

            public int C_NumChanges

            {

                        set

                        {

                                    i_NumChanges = value;

                                    if (i_Changes <= i_NumChanges + 1 &&

                                                                                     o_Control  !=   null)

                                    {

                                                MessageBox.Show (

" Enough changes for this control!","OnChanged_UC");

                                                o_Control.Enabled = false;

                                    }

                        }

            }

}

 

Now change the code page of the UC_TwoText control :

 

#region "forEventChanged"

// The event member that is of type Changed_EventHandler

//(class OnChangedEvent.cs).

public event OnChangedEvent.Changed_EventHandler C_Changed;            

private int NumChanges = 0;//the number of changes

 

// The protected OnC_Changed method raises the event by invoking

// the delegates. In this case the sender is always this.

protected virtual void OnC_Changed(C_EventArgs e) 

{

            if (C_Changed != null)

            {

                        e.C_NumChanges = NumChanges ;

                        //Invokes the delegates.

                        C_Changed(this,e);                                            

                        doOnChanged ();

            }

}

 

//The method doOnChanged executes the process logic

//for "C_Changed" event.

private void doOnChanged()

{

            //increase value of NumChanges by one

            ++NumChanges;

            //changes BackColor to another color

            if (textBox_C3.BackColor != Color.Yellow)

            {

                        textBox_C3.BackColor = Color.Yellow ;

            }

            else

            {

                        textBox_C3.BackColor = Color.LightGreen ;

            }

            //write down and show the number of changes

            textBox_C3.Text = NumChanges.ToString();

}

#endregion

 

private void textBox_C1_TextChanged(object sender, System.EventArgs e)

{

            //after five changes the control will be "swiched off"

            OnC_Changed(new C_EventArgs(this,5));

}

 

private void textBox_C2_TextChanged(object sender, System.EventArgs e)

{

                        //after five changes the control will be "swiched off"             

OnC_Changed(new C_EventArgs(this,5));

}

 

That is all for  the second part and you may test the project. Just start the project and be convinced that  all our tasks (see above for the second case) are carried out: every change of one of three our textboxes is followed by some logical process: writing down of the total of changes for every user control, changing of color and so on... and "switch off" one of our user controls after the certain quantity of its changes (we  have chosen 5; see Figure 1, Figure 2).

 

Fig_1.GIF

Figure 1.

 

Fig_2.GIF

Figure 2.

 

CONCLUSION

 

I hope that this article (with the detailed description of the test project ) will help you to choose the most suitable way when you need to "catch" changes, which occur on your user controls, and to "react" to this change according to necessary logic.

 

Good luck in programming !