I am using C#, Winforms, VS 2017 Enterprise, and the full .NET Framework 4.7.2.
[TLDR section at end!]
I have been working extensively with the System.ComponentModel.Design
namespace to create working Visual Studio-like Winforms designer. Setting up the environment for such an end-user forms designer requires deep understanding of objects and interfaces in the aforementioned namespace (and how to wire-up them all) along with toolbox and property grid components. Therefore, it's not possible for me to post a sample of the code. This question requires knowledge of the DesignSurface
class (https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.design.designsurface?view=netframework-4.7.2).
The design surface seems to fire no event when a user drags a selected control across the designer surface. I need to hook into "begin-drag"/"end-drag" events so that I can perform clear and then re-render an information panel that reflects the current position of all controls. (I do not want to use timers to intermittently refresh that information.)
There is an ISelectionService
interface, which I've implemented. But that gives information only about which controls/components are selected. It doesn't help capture the event that fires when a control-drag operation begins or ends.
Details of design-surface events appear here: https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.design?view=netframework-4.7.2
I have tried to leverage IComponentChangeService's
ComponentChanged
event, but that fires only after a control-drag operation ends (and I need to detect when the control-drag operation begins and ends)...
As a last resort, I used Spy++ to see what events fire when a control is selected and dragged across the design surface. Spy++ helped me identify the initial WM.LBUTTONDOWN
message and various mouse-move messages, etc., but leveraging those messages would require a lot of additional coding to ensure that the mouse button was clicked on a designer-surface control, that the control was in fact selected, and that the mouse button remains down, etc.--and even then, I still would have no assurance that the control isn't being resized versus moved. Of course, ideally, I would want to hook into the designer-surface's logic that responds to drag-begin event.
Finally, my requirement is to detect when a single selected control is dragged or when multiple-selected controls are dragged as a group. In both cases, I need to know when the drag starts and when it ends. (To be clear: I'm referring to controls that already are on the design surface--I am not referring to controls on the toolbox that are drag-dropped onto the designer surface...)
TLDR: What I'm seeking is a way to hook into the event that fires as soon as a control or a group of controls already on a designer surface is/are dragged, and a way to hook into the event that fires when that drag operation has ended.
Any thoughts?
To supplement Reza's fine and correct answer (which I accepted as an answer) on this rather specialized topic:
There many possible/viable ways to wire-up the Winforms designer and its dependent objects (toolbox, property grid, etc.), so the sequence/timing of when particular designer services are added to the design surface's service container will differ. Some services must be explicitly added (such as
INameCreationService
orIToolboxService
); others are added by the framework.I don't have not yet concluded exactly when the framework adds the
BehaviorService
to the service container, but I have concluded that this service is added to any working design service (that is, one that lets you drag already-added controls around the design surface using the mouse).Additionally, many objects in the
System.ComponentModel.Design
namespace offer access to the collection of available services by exposing theGetService()
method. (Reza's answer leverages the Site object'sGetService
method.) If you find that theGetService(typeof(BehaviorService))
fails to return a validBehaviorService
object, it usually means that theBehaviorService
hasn't yet been added to the design surface... I say "yet", because dragging controls already placed on the designer surface requires the presence of aBehaviorService
in the host's service container. So if your attempt to get an instance of theBehaviorService
using theGetService
method fails, chances are that theBehaviorService
simply has not yet been added by the designer framework. To solve that problem, you'll usually just need to move your code for hookingBehaviorService
events somewhere downstream.To inspect which services already have been added to the design surface, you can inspect the design surface object's
ServiceContainer
object'sServices
collection, which contains the comprehensive list of services. As Reza suggests, a good place to add the code needed to hook intoBehaviorService
's events is in the base form'sOnHandleCreated
event handler.You can get
BehaviorService
and subscribe to itsBeginDrag
andEndDrag
events.BeginDrag
: Occurs when theBehaviorService
starts a drag-and-drop operation.EndDrag
: Occurs when theBehaviorService
completes a drag operation.Example
You first need to get an instance of
BehaviorService
, for example if you have access to a designer, or a designer host or a site, you can get the behavior service this way:Then subscribe to the events:
and handle the events: