Introduction
WSSizer
is a .NET control meant to accomplish a task similar to MFC’s CRectTacker()
. It is written in my preferred language, C++/CLI.
Operational overview
The control works in a loop with its parent. The parent creates the control with an initial location and size. The control draws hit test regions, an active reversible frame, provides cursor feedback, and handles mouse events to allow the user to perform one sizing operation. The sizing operation ends with a mouse up after a mouse move. The control initiates an event to return new location and size data. The parent catches the event and updates on the returned data; then deletes the control. The update and delete, causes an Invalidate()
on the rectangle to clean up the controls paint activity and draw updated data in the parent. Then, the control is recreated with new data, to allow the user to continue to size the active object. The loop is repeated until the user does a right mouse event within the bounds of the control. The control then sends a termination event and the parent does a final update, delete and invalidate without recreating WSSizer
. (Of course, an out of bounds mouse event can be caught within the parent and processed to end the loop.)
I make no claims that this is the most efficient way to write this control. I don’t find a loop that creates and deletes a control to be the most pleasant solution. However, it works nicely and I have not found the overhead to be a burden on my user interface. My original design had the initial feedback regions drawn at a static location, while the reversible frame was changed any number of times; thus to provide one return. But, the method was uniquely different from that normally seen. I thought it an unacceptable user interface. Some of that code still lives in this current release. In the end, I found the cooperation of parent and child, in a create and delete loop, to be the best way keep the invalidate area clean.
Some sketchy details
The parent (this example)
The example allows one to create one of two shapes (an ellipse or a triangle chosen through a context menu), using a left mouse drag of a reversible frame. When one of the existing shapes is selected in the left mouse event handler, if sizerCtrl
exists, it is deleted, a shape enum and the encompassing rectangle are stored in a structure and WSSizerHelper()
is called. WSSizerHelper()
is where one creates WSSizer^ sizerCtrl
, sets desired properties, and links to the return event handlers: WSSizedEvent()
and WSTermainateEvent()
. All WSSizer
properties are prefaced with “WS” (for WedgeSoft) so they will be grouped within IntelliSense.
The Control
The control, as outlined above, processes a single sizing operation and initiates either a WSSizedEvent
or a WSTerminateEvent
event. The control is sized to “WSPadding
” larger than the input rectangle. The user’s mouse is restricted to the parent’s client rectangle. But, a move near an edge will draw the reversible frame outside the bounds of the parent. I do not know how to stop this undesired effect. The frame is correctly erased, and the object is properly clipped within the parent window. A left mouse up event sends WSSizedEvent
, and a right mouse up event sends WSTermainateEvent
.
The parent (this example) again
The parent links to WSSizedEvent()
from WSSizerSizedHandler
in WSSizedEvent()
, where a sized event is caught. There it corrects the active shape with data received from the control. It then deletes the control, invalidates the rectangle and calls WSSizerHelper()
once again with updated data. The invalidate cleans up the paint work the sizer control did and refreshes the shape to it’s new location and/or size. WSSizerHelper()
re-creates sizerCtrl
and off we go again, until the user executes a right mouse event. In which case we do all of the above except the call the WSSizerHelper()
.
A few points of interest
I thought to catch an escape within the control. But on further thought, the control could do no more than report it back to the parent; for only the parent can return the shape back to its original size. In the example, see MainForm_Keyup()
.
In my abandoned user interface, mentioned above, I had to tame the eccentricities of DrawReversibleFrame()
. I created, DrawReversibleFrameEx(bool clear)
. When called with the argument “clear
” set false
, it draws a frame and pushes it to a stack. When true
, the function empties the stack of all stored frames. I thought the stack obsolete under the current implementation; but it proved to be the solution needed to clean up of the last visible reversible frame. And, I call it from the destructor.
Early on, I created an override to BackColor
. During development, my control did not hide the parent’s paint work. Then I did some clean up, and I found my control now hid the parent’s paint work. Some research proved that the override to BackColor
was the key and is necessary.
And finally, a little programming tip I picked up during this effort: One can not “set” an existing region equal to an existing rectangle; but one can take the complement of an empty region to obtain the same result.
aRectangle = Rectangle(Point(X,Y),Drawing::Size(Width,Height));
aRegion->MakeEmpty();
aRegion->Complement(aRectangle);
Conclusion
I do not expect WSSizer
is a definitive answer to resize controls. I hope this post will prove useful to some and provide me with feedback on how to improve it and perhaps a post on where other (better) controls of this nature may be found.