Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

TdhMarchingAnts - A .NET component to select (and draw) a rectangle on the client-area of an attached control and return the co-ordinates

0.00/5 (No votes)
8 Jan 2007 1  
This article describes a .NET component to draw a rubber-band rectangle as determined by the user mouse activity (which is replaced by marching ants after the selection is complete) on the surface of an attached control and return the rectangle co-ordinates via an event.

Please note: The large size of the downloads is caused by the images included with the demo project. The compiled DLL library is only 44 KB.

Sample Image - TdhMarchingAnts.jpg

Introduction

As a minor part of the image processing sub-system of my project, I needed a way to allow users to select an identifying region on the set of images they are working with. This region for each individual image is then to be shown at magnification (as compared to the full/cropped images shown as thumbnails).

While my need was very specific -- I wanted to know the co-ordinates of a rectangle selected on the surface of a PictureBox control -- I decided to write a more versatile class which should be able to draw a rectangle on any object which inherits from System.Windows.Forms.Control and return the rectangle co-ordinates to the invoking program.

The class I wrote to accomplish this goal is: TdhMarchingAnts_NativeWindow. This class may be used by instantiating it directly via code.

I also wrote a component class, TdhMarchingAnts, to act as a development-environment "wrapper" for TdhMarchingAnts_NativeWindow.

The demo program uses/tests both classes.

Using the Classes -- The Code

To use the TdhMarchingAnts classes as I've written them, add a reference in your project to the class library 'TDHMarchingAnts.dll'. The namespace used in this library is:

using TDHControls.TDHMarchingAnts;

An example of the 'Windows Form Designer generated code' for the TdhMarchingAnts component is:

private void InitializeComponent()
{
    this.tdhAnts 
        = new TDHControls.TDHMarchingAnts.TdhMarchingAnts(this.components);

    //
    // tdhAnts 
    //
    this.tdhAnts.AntAttachedControl = this.pnlDemo;
    this.tdhAnts.AntBackColor = System.Drawing.Color.Black;    // default: .White
    this.tdhAnts.AntForeColor = System.Drawing.Color.White;    // default: .Red
    this.tdhAnts.AntsActive = true;            // Note: this is 'false' by default 
    this.tdhAnts.RegionDeselectEvent += new 
       TDHControls.TDHMarchingAnts.RegionDeselectEventHandler(
       this.tdhAnts_RegionDeselectEvent);
    this.tdhAnts.RegionSelectEvent += new 
        TDHControls.TDHMarchingAnts.RegionSelectEventHandler(
        this.tdhAnts_RegionSelectEvent);
}

Example code for instantiating the TdhMarchingAnts_NativeWindow class independently of the TdhMarchingAnts component is:

private TDHControls.TDHMarchingAnts.TdhMarchingAnts_NativeWindow native_PicBox;
private TDHControls.TDHMarchingAnts.TdhMarchingAnts_NativeWindow native_Panel;
{
    this.native_PicBox 
        = new TDHControls.TDHMarchingAnts.TdhMarchingAnts_NativeWindow(this.pbxImage);
    this.native_PicBox.AntsActive = true;  // Note: this is 'false' by default 
    this.native_PicBox.RegionSelectEvent += new 
        TDHControls.TDHMarchingAnts.RegionSelectEventHandler(
        this._PicBox_RegionSelectEvent);
    this.native_PicBox.RegionDeselectEvent += new
        TDHControls.TDHMarchingAnts.RegionDeselectEventHandler(
        this._PicBox_RegionDeselectEvent);

    // The Constructor also accepts the "left-Ant" fore- and back- colors 
    this.native_Panel 
        = new TDHControls.TDHMarchingAnts.TdhMarchingAnts_NativeWindow
        (this.pnlForSomething, Color.Blue, Color.Yellow);
}

Using the Classes -- The Code (The EventHandlers)

The TdhMarchingAnts_NativeWindow class and the TdhMarchingAnts component raise potentially six events (that is, three each for either mouse button).

Assuming the appropriate boolean properties are true, the event _RegionSelectEvent() (and the corresponding event _RightRegionSelectEvent()) is raised when the left (or right) mouse button is released following a "click-and-drag."

Assuming the appropriate boolean properties are true, the events _ClickEvent() and _RegionDeselectEvent() (and the corresponding events _RightClickEvent() and _RightRegionDeselectEvent()) are raised when the left (or right) mouse button is clicked. Thus, _ClickEvent() (or _RightClickEvent()) may be raised either alone or in conjunction with _RegionDeselectEvent() (or _RightRegionDeselectEvent()).

The signatures of the event handler methods are as follows:

private void _ClickEvent(object sender, System.Drawing.Point ClickedPoint)
{
    // do something (assuming you care about this event)
}

private void _RegionDeselectEvent(object sender, System.Drawing.Point ClickedPoint)
{
    // do something (assuming you care about this event)
}

private void _RegionSelectEvent(object sender, System.Drawing.Rectangle SelectedRect)
{
    // do something (surely you do care about this event!)
}

private void _RightClickEvent(object sender, System.Drawing.Point ClickedPoint)
{
    // do something (assuming you care about this event)
}

private void _RightRegionDeselectEvent(object sender, 
             System.Drawing.Point ClickedPoint)
{
    // do something (assuming you care about this event)
}

private void _RightRegionSelectEvent(object sender, 
             System.Drawing.Rectangle SelectedRect)
{
    // do something (surely you do care about this event!)
}

Please note: With version 1.0.004, the signatures of all the event handler methods have been changed (I apologize if this causes an inconvenience). The event handler parameter-lists should have included "object sender" originally.

Using the Classes -- The Interface

Beginning with version 1.0.002, the TdhMarchingAnts classes may be used with either the left mouse button or the right (or both). The "default" button is the left: that is, the previously named properties, methods, and events are also defined for mouse left-button activity; the corresponding properties, methods, and events defined for mouse right-button activity are preceded with the word "Right_" (for properties and methods) or "Right" (for events).

Both the TdhMarchingAnts component and the TdhMarchingAnts_NativeWindow class have essentially the same public interface.

The public (static) methods limited to the TdhMarchingAnts component class are:

  • public static System.Drawing.Point ReallyEmptyPoint() - This method returns a System.Drawing.Point object with the .X and .Y properties set to -1.
  • public static bool IsReallyEmptyPoint(System.Drawing.Point pt) - This method returns 'true' if the .X and .Y properties of the System.Drawing.Point object 'pt' both equal -1, else it returns 'false'.
  • public static System.Drawing.Rectangle ReallyEmptyRectangle() - This method returns a System.Drawing.Rectangle object with the .X, .Y, .Width, and .Height properties set to -1.
  • public static bool IsReallyEmptyRectangle(System.Drawing.Rectangle rec) - This method returns 'true' if the .X, .Y, .Width, and .Height properties of the System.Drawing.Point object 'rec' all equal -1, else it returns 'false.'

"Global" properties of both classes are:

  • AntAttachedControl - (I'm sorry about the property's name, but I wanted it to be alphabetically first so that the auto-generated code will set it first.) This property is used to attach the instance of TdhMarchingAnts_NativeWindow to a specific control.
  • Enabled - This boolean value determines whether the class as a whole is enabled/active and thus responsive to the user's mouse activity.

"Left-button" properties, methods, and event handler signatures of both classes are:

  • AntsActive - This boolean value determines whether the class is responsive to the user's mouse left-button select and deselect activity.
  • Note: The default value of this property is 'false.' It must be set to 'true' to activate the class for left-button select and deselect activity.

  • AntBackColor - The System.Drawing.Color of the path followed by the "ants" determined by the mouse left-button select activity.
  • Note: In general, set AntBackColor before setting AntForeColor. When AntBackColor is set, the class automatically sets AntForeColor to a (hopefully ideal) contrasting color.

  • AntForeColor - the System.Drawing.Color of the "ants" (themselves) determined by the mouse left-button select activity.
  • Note: To have the class draw a solid line, set AntForeColor to the same value that AntBackColor was previously set. (Alternately, this may be accomplished via the ExterminateAnts() method.)

  • AntsMarch - This boolean value determines whether the "ants" determined by the mouse left-button select activity will be animated.
  • SelectedPointTL - This property returns a System.Drawing.Point representing the top-left co-ordinate of the selected region. If there is not yet a selected region, the .X and .Y values are -1.
  • SelectedPointBR - This property returns a System.Drawing.Point representing the bottom-right co-ordinate of the selected region. If there is not yet a selected region, the .X and .Y values are -1.
  • SelectedRect - This property returns a System.Drawing.Rectangle representing the selected region. If there is not yet a selected region, the .Width and .Height values are -1.
  • public void EraseAntPath() - This method removes the visual representation of the selected region, while retaining the rectangle co-ordinates themselves.
  • public void ForgetAntPath() - This method removes the visual representation of the selected region *and* disposes of the rectangle co-ordinates.
  • public void ExterminateAnts() - This method sets AntForeColor to the current value of AntBackColor -- i.e., it changes the drawn rectangle to a solid line.
  • public void SetAntPath(System.Drawing.Rectangle PathRect) - This method draws a rectangle on the attached control with the co-ordinates of the given rectangle.
  • Note: This method will generate a 'RegionSelectEvent' as though the user had selected the region represented by the rectangle.

  • public delegate void ClickEventHandler(object sender, System.Drawing.Point ClickedPoint) - [If (.Enabled=true)] This event occurs when the user left-clicks on the control attached to the TdhMarchingAnts component.
  • public delegate void RegionDeselectEventHandler(object sender, System.Drawing.Point ClickedPoint) - [If (.Enabled=true) and (.AntsActive=true) and a region is currently selected] This event occurs when the user left-clicks on the control attached to the TdhMarchingAnts component.
  • Note: In this case, both the ClickEvent and RegionDeselectEvent events are raised.

  • public delegate void RegionSelectEventHandler(object sender, System.Drawing.Rectangle SelectedRect) - [If (.Enabled=true) and (.AntsActive=true)] This event is raised when the left mouse button is released following a "click-and-drag."

"Right-button" properties, methods, and event handler signatures of both classes are:

  • Right_AntsActive - This boolean value determines whether the class is responsive to the user's mouse right-button select and deselect activity.
  • Note: The default value of this property is 'false'. It must be set to 'true' to activate the class for right-button select and deselect activity.

  • Right_AntBackColor - The System.Drawing.Color of the path followed by the "ants" determined by the mouse right-button select activity.
  • Note: In general, set AntBackColor before setting AntForeColor. When AntBackColor is set, the class automatically sets AntForeColor to a (hopefully ideal) contrasting color.

  • Right_AntForeColor - the System.Drawing.Color of the "ants" (themselves) determined by the mouse right-button select activity.
  • Note: To have the class draw a solid line, set AntForeColor to the same value that AntBackColor was previously set. (Alternately, this may be accomplished via the ExterminateAnts() method.)

  • Right_AntsMarch - This boolean value determines whether the "ants" determined by the mouse right-button select activity will be animated.
  • Right_SelectedPointTL - This property returns a System.Drawing.Point representing the top-left co-ordinate of the selected region. If there is not yet a selected region, the .X and .Y values are -1.
  • Right_SelectedPointBR - This property returns a System.Drawing.Point representing the bottom-right co-ordinate of the selected region. If there is not yet a selected region, the .X and .Y values are -1.
  • Right_SelectedRect - This property returns a System.Drawing.Rectangle representing the selected region. If there is not yet a selected region, the .Width and .Height values are -1.
  • public void Right_EraseAntPath() - This method removes the visual representation of the selected region, while retaining the rectangle co-ordinates themselves.
  • public void Right_ForgetAntPath() - This method removes the visual representation of the selected region *and* disposes of the rectangle co-ordinates.
  • public void Right_ExterminateAnts() - This method sets Right_AntForeColor to the current value of Right_AntBackColor -- i.e., it changes the drawn rectangle to a solid line.
  • public void Right_SetAntPath(System.Drawing.Rectangle PathRect) - This method draws a rectangle on the attached control with the co-ordinates of the given rectangle.
  • Note: This method will generate a 'RightRegionSelectEvent' as though the user had selected the region represented by the rectangle.

  • public delegate void RightClickEventHandler(object sender, System.Drawing.Point ClickedPoint) - [If (.Enabled=true)] This event occurs when the user right-clicks on the control attached to the TdhMarchingAnts component.
  • public delegate void RightRegionDeselectEventHandler(object sender, System.Drawing.Point ClickedPoint) - [If (.Enabled=true) and (.Right_AntsActive=true) and a region is currently selected] This event occurs when the user right-clicks on the control attached to the TdhMarchingAnts component.
  • Note: In this case, both the RightClickEvent and RightRegionDeselectEvent events are raised.

  • public delegate void RightRegionSelectEventHandler(object sender, System.Drawing.Rectangle SelectedRect) - [If (.Enabled=true) and (.Right_AntsActive=true)] This event is raised when the right mouse button is released following a "click-and-drag."

History

  • 2006 December 08: Submission of TdhMarchingAnts ver. 1.0.001 to The Code Project.
  • 2006 December 12: ver. 1.0.002.
  • Added the following events (and the corresponding properties and methods):

    • ClickEvent(object sender, System.Drawing.Point ClickedPoint)
    • RightClickEvent(object sender, System.Drawing.Point ClickedPoint)
    • RightRegionSelectEvent(object sender, System.Drawing.Rectangle SelectedRect)
    • RightRegionDeselectEvent(object sender, System.Drawing.Point ClickedPoint)
  • 2006 December 13: ver. 1.0.003
    • Moved these methods from the class TdhMarchingAnts_NativeWindow to the class TdhMarchingAnts (and made them 'public'):
      • public static System.Drawing.Point ReallyEmptyPoint()
      • public static bool IsReallyEmptyPoint(System.Drawing.Point pt)

      Created these new public methods in the class TdhMarchingAnts:

      • public static System.Drawing.Rectangle ReallyEmptyRectangle()
      • public static bool IsReallyEmptyRectangle(System.Drawing.Rectangle rec)
  • 2006 December 15: ver. 1.0.004
    • Corrected the logic flaw in the TdhMarchingAnts_NativeWindow.InvalidateAntPaths() method. Due to a timing issue in the this._parentControl.Paint() event which ultimately is raised by the [this._parentControl.Invalidate(true);] statement at the start of the .InvalidateAntPaths() method, it was futile to invoke the .DrawAntPath() method as an attempt to redraw the 'AntPath(s)'. This is because the this._parentControl.Paint() event tends to occur *after* .DrawAntPath().
      • But the serious logic flaw (as contrasted to a mere futility) is that due to the parameter-list of that invocation of the .DrawAntPath() method, 'RegionSelectEvent' was being raised (yet again) following a right-click.
    • Added 'object sender' to the parameter-list of all TDHMarchingAnts.TdhMarchingAnts EventHandlers.
  • 2007 January 8: ver. 1.0.006
    • Added logic to intercept a timing-issue problem occurring when disabling the "marching ants" (which includes disposing the Path). Apparently, a timer-tick event might already have been scheduled, which would lead to an unhandled exception when that method executed, due to the Path having been set to null.
  • 2007 January 11: ver. 1.0.007
    • Added a check for the condition [this._parentControl.Visible] before setting [this._parentControl.Cursor = cursorOriginal;]. Setting the '_parentControl' cursor was causing an excepting under VS2005 when '_parentControl' isn't visible.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here