Events & Delegates In C# - Win Forms Controls
Posted by 9/07/2012 07:07:00 PM with No comments
on
I encourage you to first read Events & Delegates In C# - The Concept to understand the main concept of Events & Delegates in C# before starting reading this article.
Assume that you need to make a win forms control to be used by wide range of users for wide range of purposes. This control will include 3 sliders which the control user can move and change their values at rum-time and accordingly an action will happen. This action may be changing a background color or changing volume of a media player or changing values in some fields and so on ... But, the control user should not actually access the 3 sliders inside your control programmatically. It is your responsibility to expose all info and actions that the user can make use of when using your control. So, your control should be as a black box for the user and he can only use and see when you permit him to use and see.
So, you need to make your control raise some sort of events when the sliders are moved telling the control user the new values of the sliders beside which of the 3 sliders has moved. At this point you don't care about how the user will make use of these values cause this is not your concern and it doesn't affect your logic by any mean.
But, before you raise the event, you should tell the control user how he can receive the new sliders value. This is done by defining the delegate which will be connected to your event.
Also, you will need to tell him about the structure of the object/class which you will use to encapsulate the value of the sliders and the name of the moved slider. This is done by defining your custom EventArgs class.
So, now you know the steps we should follow to complete the task, so let's see some code.
{
Slider1 = 1,
Slider2 = 2,
Slider3 = 3
}
#endregion
{
#region Fields & Properties
SliderName changedSlider;
public SliderName ChangedSlider
{
get { return changedSlider; }
set { changedSlider = value; }
}
int slider1Value;
public int Slider1Value
{
get { return slider1Value; }
set { slider1Value = value; }
}
int slider2Value;
public int Slider2Value
{
get { return slider2Value; }
set { slider2Value = value; }
}
int slider3Value;
public int Slider3Value
{
get { return slider3Value; }
set { slider3Value = value; }
}
#endregion
#region Constructors
public SliderPositionChangedEventArgs()
{
}
public SliderPositionChangedEventArgs(SliderName _changedSlider, int _slider1Value, int _slider2Value, int _slider3Value)
{
ChangedSlider = _changedSlider;
Slider1Value = _slider1Value;
Slider2Value = _slider2Value;
Slider3Value = _slider3Value;
}
#endregion
}
#endregion
#endregion
#region Constructor
public TripleSliders()
{
InitializeComponent();
}
#endregion
#region Custom Events
public event SliderPositionChangedEventHandler SliderPositionChanged;
#endregion
{
if (null != SliderPositionChanged)
{
SliderPositionChanged(sender, e);
}
}
#endregion
{
SliderPositionChangedEventArgs args = new SliderPositionChangedEventArgs(SliderName.Slider1, trackBar1.Value, trackBar2.Value, trackBar3.Value);
OnSliderPositionChanged(this, args);
}
private void trackBar2_MouseUp(object sender, MouseEventArgs e)
{
SliderPositionChangedEventArgs args = new SliderPositionChangedEventArgs(SliderName.Slider2, trackBar1.Value, trackBar2.Value, trackBar3.Value);
OnSliderPositionChanged(this, args);
}
private void trackBar3_MouseUp(object sender, MouseEventArgs e)
{
SliderPositionChangedEventArgs args = new SliderPositionChangedEventArgs(SliderName.Slider3, trackBar1.Value, trackBar2.Value, trackBar3.Value);
OnSliderPositionChanged(this, args);
}
#endregion
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace TestApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
tripleSliders1.SliderPositionChanged += new WinFormsControlsProject.SliderPositionChangedEventHandler(tripleSliders1_SliderPositionChanged);
}
void tripleSliders1_SliderPositionChanged(object sender, WinFormsControlsProject.SliderPositionChangedEventArgs e)
{
this.BackColor = Color.FromArgb(e.Slider1Value, e.Slider2Value, e.Slider3Value);
}
}
}
Assume that you need to make a win forms control to be used by wide range of users for wide range of purposes. This control will include 3 sliders which the control user can move and change their values at rum-time and accordingly an action will happen. This action may be changing a background color or changing volume of a media player or changing values in some fields and so on ... But, the control user should not actually access the 3 sliders inside your control programmatically. It is your responsibility to expose all info and actions that the user can make use of when using your control. So, your control should be as a black box for the user and he can only use and see when you permit him to use and see.
So, you need to make your control raise some sort of events when the sliders are moved telling the control user the new values of the sliders beside which of the 3 sliders has moved. At this point you don't care about how the user will make use of these values cause this is not your concern and it doesn't affect your logic by any mean.
But, before you raise the event, you should tell the control user how he can receive the new sliders value. This is done by defining the delegate which will be connected to your event.
Also, you will need to tell him about the structure of the object/class which you will use to encapsulate the value of the sliders and the name of the moved slider. This is done by defining your custom EventArgs class.
So, now you know the steps we should follow to complete the task, so let's see some code.
Steps:
1. Open visual studio and create a new project of type "Windows Forms Control Library"
2. The name of the project is "WinFormsControlsProject"
3. The name of the solution is "WinFormsControls"
4. Rename the "UserControl.cs" file to "TripleSliders.cs"
5. The solution should now look like the picture below
6. Now, drag 3 trackbars and arrange them as in the picture above. Their names are "trackBar1" and "trackBar2" and "trackBar3"
7. Also, drag 3 labels and arrange them as in the picture above
8. Now, we will define our custom eventargs class that we will use to tell the control user which slider has moved and the values of the 3 sliders.
9. So, in the "TripleSliders.cs" file we will write the code below
#region SliderName enum
public enum SliderName{
Slider1 = 1,
Slider2 = 2,
Slider3 = 3
}
#endregion
#region EventArgs
public class SliderPositionChangedEventArgs : EventArgs{
#region Fields & Properties
SliderName changedSlider;
public SliderName ChangedSlider
{
get { return changedSlider; }
set { changedSlider = value; }
}
int slider1Value;
public int Slider1Value
{
get { return slider1Value; }
set { slider1Value = value; }
}
int slider2Value;
public int Slider2Value
{
get { return slider2Value; }
set { slider2Value = value; }
}
int slider3Value;
public int Slider3Value
{
get { return slider3Value; }
set { slider3Value = value; }
}
#endregion
#region Constructors
public SliderPositionChangedEventArgs()
{
}
public SliderPositionChangedEventArgs(SliderName _changedSlider, int _slider1Value, int _slider2Value, int _slider3Value)
{
ChangedSlider = _changedSlider;
Slider1Value = _slider1Value;
Slider2Value = _slider2Value;
Slider3Value = _slider3Value;
}
#endregion
}
#endregion
10. Now, we need to define the delegate to which our event will be related and which will define the header (return type and input parameters) of the method by which the control user will communicate and interact with our control. This method should provide our custom eventargs as an input parameter and could receive a return from the control user if we need so
11. In our case, our control doesn't need to receive any feedback from the control user when he moves any of the sliders. We just raise our event and provide him with the info encapsulated in the custom eventargs. We need nothing after that
12. This means that the method should not have a return type. In other words, it should have "void" as a return type
13. So, up to this moment, we can say that our method should look like this
public void methodname(SliderPositionChangedEventArgs e)
14. But, as a best practice, and as we always see when using .Net controls, the empty method generated when binding to a control event always has an input paramater called "sender" and is of type "object". This "sender" is a reference to the party which raised the event. This is useful because the party which is interested into the event can now have the chance to get a reference to the other party which raised the event in the first place. Sometimes this piece of info can be needed
15. So, to follow the best practice, we will need to update our method header to be like this
public void methodname(object sender, SliderPositionChangedEventArgs e)
16. So, to define a delegate which defines the header above, we will write the line below in the "TripleSliders.cs" file as follows
#region Delegate
public delegate void SliderPositionChangedEventHandler(object sender, SliderPositionChangedEventArgs e);#endregion
17. Now, we need to define the event which we will raise at some point in our control when a slider is moved. Also, we should somehow relate this event to the delegate we already defined so that when the control user tries to bind to our event the C# compiler will restrict him to write a method with a header matching to the header we defined in the delegate, not any other header
18. Also, note that this event should be a member of our control class. This way, when the control user writes the name of the control instance followed by the "." he will find a member event to bind to using the "+=" operator
19. Also, note that relation between our event and the delegate should somehow be defined. This is done by following the "event" modifier with the name of the delegate we created
20. To do so, we will write the code below inside the "TripleSliders.cs" file inside the class of our control
public partial class TripleSliders : UserControl
{#region Constructor
public TripleSliders()
{
InitializeComponent();
}
#endregion
#region Custom Events
public event SliderPositionChangedEventHandler SliderPositionChanged;
#endregion
}
21. Now, we should be ready to raise our event wherever we want in the code of our control. This is done by calling the event as if we call any other method. Actually, it is not like calling any other method, it is like calling a method having the same header as our delegate and this is logical. Why, because the moment inside our code when we are raising the event, this is the moment when we should provide the info encapsulated in our custom eventargs. So, you can now see the relation between our event and the delegated coming to life
22. So, we can always fire our event by writing the code "SliderPositionChanged(the source, the event args instance encapsulating the changed slider and the values for the 3 sliders)"
23. But, before raising the event, we should check if any other party is bound to our event. This is because it will be useless to raise the event if there is no one interested into knowing that any of the sliders has moved
24. So, how will we check if anything is bound to our event? This is done by checking the value of the event itself. If it is null then nothing is already bound to it at runtime. Otherwise, then something is bound to it
25. So, each time we want to raise the event, we can check if (SliderPositionChanged == null) or not. That is so easy. But, why don't we move this part of logic to a separate method. This method would be called whenever we want to raise the event, whenever it is called, it will check if something is bound to the event and if so it will fire/raise the event. This is a best practice we should follow
26. Also, since this method will be responsible for firing/raising the event, then it should have the same input parameters which the event need
27. So, now we will define a method which will carry out this logic as follows
#region Custom Event Handlers
public virtual void OnSliderPositionChanged(object sender, SliderPositionChangedEventArgs e){
if (null != SliderPositionChanged)
{
SliderPositionChanged(sender, e);
}
}
#endregion
28. Now, we are so ready to raise our event when we feel we need to. So, according to our logic, we need to fire/raise the event when any of our sliders moves. So, the question here is how we can know if a slider is moved??? Yesssss, we can know that by binding to one of the "trackbar" control built-in events which will be raised by the sliders when they are moved
29. Me myself, i chose to use the "MouseUp" event of the "trackbar" controls for simplicity and to not cause our event to fire for every minor step in the sliders but for the final step when the user release the mouse click when dragging the sliders. Anyway, it is your choice and it depends on your desired logic and design
30. So, I bound to the "MouseUp" events of the three sliders and got three empty methods where i should raise our event passing our custom eventargs class
31, So, inside each of these methods I created an instance of our custom eventargs class "SliderPositionChangedEventArgs", set its properties (moved slider name, value of slider 1, value of slider 2, value of slider 3), called the method responsible for firing the events "OnSliderPositionChanged" passing it the input parameters
32. Doing so, the code looked like this
#region Control Events
private void trackBar1_MouseUp(object sender, MouseEventArgs e){
SliderPositionChangedEventArgs args = new SliderPositionChangedEventArgs(SliderName.Slider1, trackBar1.Value, trackBar2.Value, trackBar3.Value);
OnSliderPositionChanged(this, args);
}
private void trackBar2_MouseUp(object sender, MouseEventArgs e)
{
SliderPositionChangedEventArgs args = new SliderPositionChangedEventArgs(SliderName.Slider2, trackBar1.Value, trackBar2.Value, trackBar3.Value);
OnSliderPositionChanged(this, args);
}
private void trackBar3_MouseUp(object sender, MouseEventArgs e)
{
SliderPositionChangedEventArgs args = new SliderPositionChangedEventArgs(SliderName.Slider3, trackBar1.Value, trackBar2.Value, trackBar3.Value);
OnSliderPositionChanged(this, args);
}
#endregion
33. Now, our control is completed and ready for usage
34. To test our control, I created a test windows forms application project to use our control
35. Create new windows forms application project with the name "TestApp1"
36. Drag and drop our control on the form so that the solution looks like the picture below
37. In the form load event I bound our control instance event "SliderPositionChanged" using the "+=" operator and wrote some code in the generated empty method. This code is responsible for changing the form background color according to the values of the 3 sliders in our control
38. So, the code looked like this
using System;
using System.Collections.Generic;using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace TestApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
tripleSliders1.SliderPositionChanged += new WinFormsControlsProject.SliderPositionChangedEventHandler(tripleSliders1_SliderPositionChanged);
}
void tripleSliders1_SliderPositionChanged(object sender, WinFormsControlsProject.SliderPositionChangedEventArgs e)
{
this.BackColor = Color.FromArgb(e.Slider1Value, e.Slider2Value, e.Slider3Value);
}
}
}
39. So, when running our test application, here is what happens
At last, i wish this helped you understand how to use Events & Delegates in C#. I encourage you to practice using them more and more till you fully understand the main concept.
Good Luck :)