Use a symbiote class to let the user graphically select a range of hours in C#

The example Let the user graphically select a range of hours in C# shows how to use a PictureBox to let the user select a range of hours. If you want to use let the user select more than one range, you could turn this into a custom control. You could place a PictureBox on a UserControl, then compile it, and put as many of these as you like on a form.

One drawback to that method is that the new control must be compiled before you can use it or see it in the toolbox. That's not the end of the world and works pretty well if you're distributing compiled programs, but I only post source code, never compiled code. If I post a project that contains a non-compiled custom control, when you first load the project any form that uses the control cannot display it. You need to compile the project before you can see it.

An alternative is to make what I call a symbiote class. A symbiote class is a class that is used to add features to another class. In this case, a SelectHoursSymbiote object adds features to aq PictureBox to make it behave like the control used in the previous example. This gives you almost all of the benefits of a compiled custom control but you don't need to compile it separately. The drawback is that it doesn't appear in the toolbox. You create the PictureBox control and then attach the symbiote to it at run time.

The SelectHoursSymbiote class contains all of the code used by the previous example to work with the PictureBox control plus a little extra code to manage events and such.

The following code shows how the class declares its HoursScrolled and HoursChanged events.

// Declare events.
public event EventHandler HoursScrolled;
public event EventHandler HoursChanged;

These events tell the main program when the symbiote's hours change.

The following code shows the class's StartHour and StopHour properties.

// The selected hours.
private int _StartHour = 0;
private int _StopHour = 0;
public int StartHour
{
    get { return _StartHour; }
    set
    {
        _StartHour = value;
        // Raise the HoursChanged event.
        if (HoursChanged != null) HoursChanged(this, null);
    }
}
public int StopHour
{
    get { return _StopHour; }
    set
    {
        _StopHour = value;
        // Raise the HoursChanged event.
        if (HoursChanged != null) HoursChanged(this, null);
    }
}

These are fairly straightforward properties. You could implement them as public fields (if you're willing to ignore the purists who say you should never expose fields publicly), but I wanted the code to raise the HoursChanged event when these values changed.

The following code shows the class's only constructor.

// The PictureBox we manage.
private PictureBox Pic;

// Constructor.
public SelectHoursSymbiote(PictureBox pic)
{
    Pic = pic;

    // Add event handlers.
    Pic.Paint += pic_Paint;
    Pic.MouseDown += pic_MouseDown;
    Pic.MouseMove += pic_MouseMove;
    Pic.MouseUp += pic_MouseUp;
}

The Pic variable holds a reference to the PictureBox to which the symbiote is attached. The constructor saves a reference to the PictureBox. It then gives the PictureBox event handlers to catch the Paint event and the mouse events it needs to let the user select hours. These event handlers are almost exactly the same as those used by the previous example except they work with the Pic control instead of the specific picHours control. See the previous example for information about how the mouse events and the Paint event work.

The only real differences are where the MouseMove and MouseUp event handlers raise events. After it calculates the newly selected hours, the MouseMove event handler uses the following code to raise the symbiote's HoursScrolled event.

// Raise the HoursScrolled event.
if (HoursScrolled != null) HoursScrolled(this, null);

The MouseUp event handler uses the following code to raise the HoursChanged event.

// Raise the HoursChanged event.
if (HoursChanged != null) HoursChanged(this, null);

The main program uses the following code to attach symbiotes to its PictureBoxes.

// The symbiotes.
private SelectHoursSymbiote Symbiote1, Symbiote2;

private void Form1_Load(object sender, EventArgs e)
{
    this.ResizeRedraw = true;

    // Create the symbiotes.
    Symbiote1 = new SelectHoursSymbiote(picHours1);
    Symbiote1.HoursChanged += pic_HoursChanged;
    Symbiote1.HoursScrolled += pic_HoursChanged;
    Symbiote1.StartHour = 6;
    Symbiote1.StopHour = 14;

    Symbiote2 = new SelectHoursSymbiote(picHours2);
    Symbiote2.HoursChanged += pic_HoursChanged;
    Symbiote2.HoursScrolled += pic_HoursChanged;
    Symbiote2.StartHour = 9;
    Symbiote2.StopHour = 17;
}

The code creates the new symbiotes, passing their constructors the PictureBoxes to which they should be attached. It registers event handlers for the symbiotes' HoursScrolled and HoursChanged events and gives them initial StartHour and StopHour values.

The following code shows how the program responds when it receives an HoursScrolled or HoursChanged event.

// Show the times in the TextBoxes.
private void pic_HoursChanged(object sender, EventArgs e)
{
    SelectHoursSymbiote symbiote = sender as SelectHoursSymbiote;
    DateTime start_time = new DateTime(2000, 1, 1, symbiote.StartHour, 0, 0);
    DateTime stop_time = new DateTime(2000, 1, 1, symbiote.StopHour, 0, 0);

    if (symbiote == Symbiote1)
    {
        txtStartTime1.Text = start_time.ToShortTimeString();
        txtStopTime1.Text = stop_time.ToShortTimeString();
    }
    else
    {
        txtStartTime2.Text = start_time.ToShortTimeString();
        txtStopTime2.Text = stop_time.ToShortTimeString();
    }
}

This code converts the symbiote's hours into DateTime objects and then displays them as short times in the appropriate TextBoxes.

That's all there is to it. Creating and using symbiote classes is remarkably easy. Usually it only takes a few minutes to convert code inside a form into a symbiote. (Writing it all up in a post takes longer.)

   

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments

  • 11/20/2012 6:37 AM Richard Moss wrote:
    I remember when VB4 (16bit!) was released and I went wild writing tons of classes to turn PictureBoxes into weird and wonderful controls (mostly to replace flaky Mabry VBX's). That went out the window with VB5 and it's UserControl class but it was great back then. Never really used that technique since though.

    Good memories ;)
    Reply to this
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.