2 // System.Windows.Forms.Design.ParentControlDesigner
5 // Ivan N. Zlatev (contact i-nZ.net)
7 // (C) 2006 Ivan N. Zlatev
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.ComponentModel
;
32 using System
.ComponentModel
.Design
;
33 using System
.Windows
.Forms
;
35 using System
.Drawing
.Drawing2D
;
36 using System
.Drawing
.Design
;
37 using System
.Collections
;
38 using System
.Windows
.Forms
.Design
.Behavior
;
40 namespace System
.Windows
.Forms
.Design
44 public class ParentControlDesigner
: ControlDesigner
47 public ParentControlDesigner ()
52 #region Initialization
53 // Settings paths taken from the example at:
54 // http://msdn2.microsoft.com/en-us/library/system.componentmodel.design.idesigneroptionservice.aspx
56 public override void Initialize (IComponent component
)
58 base.Initialize (component
);
60 this.Control
.AllowDrop
= true;
62 // Initialize the default values of the Design-Time properties.
64 _defaultDrawGrid
= true;
65 _defaultSnapToGrid
= true;
66 _defaultGridSize
= new Size (8, 8);
68 // If the parent Control of the designed one has a ParentDesigner then inherit the values
69 // from it's designer.
71 if (this.Control
.Parent
!= null) {
72 ParentControlDesigner parentDesigner
= GetParentControlDesignerOf (Control
.Parent
);
73 if (parentDesigner
!= null) {
74 _defaultDrawGrid
= (bool) GetValue (parentDesigner
.Component
, "DrawGrid");
75 _defaultSnapToGrid
= (bool) GetValue (parentDesigner
.Component
, "SnapToGrid");
76 _defaultGridSize
= (Size
) GetValue (parentDesigner
.Component
, "GridSize");
80 // Else retrieve them through the IDesignerOptionService (if available)
82 IDesignerOptionService options
= GetService (typeof (IDesignerOptionService
)) as
83 IDesignerOptionService
;
84 if (options
!= null) {
86 value = options
.GetOptionValue (@"WindowsFormsDesigner\General", "DrawGrid");
88 _defaultDrawGrid
= (bool) value;
90 value = options
.GetOptionValue (@"WindowsFormsDesigner\General", "SnapToGrid");
92 _defaultSnapToGrid
= (bool) value;
94 value = options
.GetOptionValue (@"WindowsFormsDesigner\General", "GridSize");
96 _defaultGridSize
= (Size
) value;
100 IComponentChangeService componentChangeSvc
= GetService (typeof (IComponentChangeService
)) as IComponentChangeService
;
101 if (componentChangeSvc
!= null) {
102 componentChangeSvc
.ComponentRemoving
+= new ComponentEventHandler (OnComponentRemoving
);
103 componentChangeSvc
.ComponentRemoved
+= new ComponentEventHandler (OnComponentRemoved
);
106 // At the end set whatever we've managed to get
108 _drawGrid
= _defaultDrawGrid
;
109 _snapToGrid
= _defaultSnapToGrid
;
110 _gridSize
= _defaultGridSize
;
113 protected override void Dispose (bool disposing
)
116 EnableDragDrop (false);
117 OnMouseDragEnd (true);
119 base.Dispose (disposing
);
124 #region IToolboxService Related
126 // This is the code that is executed when you drop a tool from the Toolbox in the designer.
129 protected static void InvokeCreateTool (ParentControlDesigner toInvoke
, ToolboxItem tool
)
131 if (toInvoke
!= null)
132 toInvoke
.CreateTool (tool
);
135 protected void CreateTool (ToolboxItem tool
)
137 CreateToolCore (tool
, DefaultControlLocation
.X
, DefaultControlLocation
.Y
, 0, 0, true, false);
140 protected void CreateTool (ToolboxItem tool
, Point location
)
142 CreateToolCore (tool
, location
.X
, location
.Y
, 0, 0, true, false);
145 protected void CreateTool (ToolboxItem tool
, Rectangle bounds
)
147 CreateToolCore (tool
, bounds
.X
, bounds
.Y
, bounds
.Width
, bounds
.Width
, true, true);
150 // Creates a component from a ToolboxItem, sets its location and size if available and snaps it's
151 // location to the grid.
153 protected virtual IComponent
[] CreateToolCore (ToolboxItem tool
, int x
, int y
, int width
, int height
,
154 bool hasLocation
, bool hasSize
)
157 throw new ArgumentNullException ("tool");
159 IDesignerHost host
= GetService (typeof (IDesignerHost
)) as IDesignerHost
;
160 DesignerTransaction transaction
= host
.CreateTransaction ("Create components in tool '" + tool
.DisplayName
+ "'");
161 IComponent
[] components
= tool
.CreateComponents (host
);
163 foreach (IComponent component
in components
)
165 ControlDesigner controlDesigner
= host
.GetDesigner (component
) as ControlDesigner
;
166 if (controlDesigner
== null) { // not a Control, but e.g. a plain Component
168 } else if (!this.CanParent (controlDesigner
)) {
169 host
.DestroyComponent (component
);
173 Control control
= component
as Control
;
174 if (control
!= null) {
175 this.Control
.SuspendLayout ();
176 // set parent instead of controls.Add so that it gets serialized for Undo/Redo
177 TypeDescriptor
.GetProperties (control
)["Parent"].SetValue (control
, this.Control
);
178 this.Control
.SuspendLayout ();
181 base.SetValue (component
, "Location", this.SnapPointToGrid (new Point (x
, y
)));
183 base.SetValue (component
, "Location", this.SnapPointToGrid (this.DefaultControlLocation
));
186 base.SetValue (component
, "Size", new Size (width
, height
));
188 this.Control
.Refresh ();
191 ISelectionService selectionServ
= this.GetService (typeof (ISelectionService
)) as ISelectionService
;
192 if (selectionServ
!= null)
193 selectionServ
.SetSelectedComponents (components
, SelectionTypes
.Replace
);
194 transaction
.Commit ();
201 #region Drag and Drop
203 // If the control is not already parented return true
205 public virtual bool CanParent (Control control
)
208 return !control
.Contains (this.Control
);
213 public virtual bool CanParent (ControlDesigner controlDesigner
)
215 return CanParent (controlDesigner
.Control
);
218 protected override void OnDragDrop (DragEventArgs de
)
220 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
221 if (selectionServ
!= null) {
222 // once this is fired the parent control (parentcontroldesigner) will start getting dragover events.
224 Point location
= this.SnapPointToGrid (this.Control
.PointToClient (new Point (de
.X
, de
.Y
)));
225 selectionServ
.DragDrop (false, this.Control
, location
.X
, location
.Y
);
229 protected override void OnDragEnter (DragEventArgs de
)
231 this.Control
.Refresh ();
234 protected override void OnDragLeave (EventArgs e
)
236 this.Control
.Refresh ();
239 protected override void OnDragOver (DragEventArgs de
)
241 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
242 if (selectionServ
!= null) {
243 // once ControlDesigner.MouseDragBegin is called this will start getting dragover events.
245 Point location
= this.SnapPointToGrid (this.Control
.PointToClient (new Point (de
.X
, de
.Y
)));
246 selectionServ
.DragOver (this.Control
, location
.X
, location
.Y
);
248 de
.Effect
= DragDropEffects
.Move
;
254 // The default location where a control is placed, when added to the designer
256 protected virtual Point DefaultControlLocation
{
257 get { return new Point (0, 0); }
261 protected override bool EnableDragRect
{
266 #region ComponentChange
268 private void OnComponentRemoving (object sender
, ComponentEventArgs args
)
270 IComponentChangeService componentChangeSvc
= GetService (typeof (IComponentChangeService
)) as IComponentChangeService
;
271 Control control
= args
.Component
as Control
;
272 if (control
!= null && control
.Parent
== this.Control
&& componentChangeSvc
!= null)
273 componentChangeSvc
.OnComponentChanging (args
.Component
, TypeDescriptor
.GetProperties (args
.Component
)["Parent"]);
276 private void OnComponentRemoved (object sender
, ComponentEventArgs args
)
278 IComponentChangeService componentChangeSvc
= GetService (typeof (IComponentChangeService
)) as IComponentChangeService
;
279 Control control
= args
.Component
as Control
;
280 if (control
!= null && control
.Parent
== this.Control
&& componentChangeSvc
!= null) {
281 control
.Parent
= null;
282 componentChangeSvc
.OnComponentChanged (args
.Component
,
283 TypeDescriptor
.GetProperties (args
.Component
)["Parent"],
289 #region Design-Time Properties
291 private bool _defaultDrawGrid
;
292 private bool _defaultSnapToGrid
;
293 private Size _defaultGridSize
;
294 private bool _drawGrid
;
295 private bool _snapToGrid
;
296 private Size _gridSize
;
298 //This method adds the following design-time browsable properties:
299 // "DrawGrid", "SnapToGrid", and "GridSize".
301 protected override void PreFilterProperties (IDictionary properties
)
303 base.PreFilterProperties (properties
);
305 properties
["DrawGrid"] = TypeDescriptor
.CreateProperty (typeof (ParentControlDesigner
),
309 BrowsableAttribute
.Yes
,
310 DesignOnlyAttribute
.Yes
,
311 new DescriptionAttribute (
312 "Indicates whether or not to draw the positioning grid."),
313 CategoryAttribute
.Design
316 properties
["SnapToGrid"] = TypeDescriptor
.CreateProperty (typeof (ParentControlDesigner
),
320 BrowsableAttribute
.Yes
,
321 DesignOnlyAttribute
.Yes
,
322 new DescriptionAttribute (
323 "Determines if controls should snap to the positioning grid."),
324 CategoryAttribute
.Design
327 properties
["GridSize"] = TypeDescriptor
.CreateProperty (typeof (ParentControlDesigner
),
331 BrowsableAttribute
.Yes
,
332 DesignOnlyAttribute
.Yes
,
333 new DescriptionAttribute (
334 "Determines the size of the positioning grid."),
335 CategoryAttribute
.Design
341 // Informs all children controls' ParentControlDesigners that the grid properties
342 // have changed and passes them
344 private void PopulateGridProperties ()
346 // Control.Invalidate (true) will redraw the control and it's children
347 // this will cause a WM_PAINT message to be send and the ControlDesigenr will raise
348 // the OnPaintAdornments, where the grid drawing takes place.
350 // Note that this should be called *after* the grid properties have changed :-)
352 this.Control
.Invalidate (false);
354 if (this.Control
!= null) {
355 ParentControlDesigner designer
= null;
356 foreach (Control control
in this.Control
.Controls
) {
357 designer
= this.GetParentControlDesignerOf (control
);
358 if (designer
!= null)
359 designer
.OnParentGridPropertiesChanged (this);
364 // Called by the parent ParentControlDesigner when it is populating the grid-related
365 // design-time properties changes
367 private void OnParentGridPropertiesChanged (ParentControlDesigner parentDesigner
)
369 SetValue (this.Component
, "DrawGrid", (bool) GetValue (parentDesigner
.Component
, "DrawGrid"));
370 SetValue (this.Component
, "SnapToGrid", (bool) GetValue (parentDesigner
.Component
, "SnapToGrid"));
371 SetValue (this.Component
, "GridSize", (Size
) GetValue (parentDesigner
.Component
, "GridSize"));
373 // Set also the default values to be those, because we should
374 // match the parent ParentControlDesigner values.
375 // called recursivly, so I will rather go for slower, but no stack-overflowable code
377 _defaultDrawGrid
= (bool) GetValue (parentDesigner
.Component
, "DrawGrid");
378 _defaultSnapToGrid
= (bool) GetValue (parentDesigner
.Component
, "SnapToGrid");
379 _defaultGridSize
= (Size
) GetValue (parentDesigner
.Component
, "GridSize");
381 this.PopulateGridProperties ();
385 // Retrieves the ParentControlDesigner of the specified control if available,
386 // else returns null.
388 private ParentControlDesigner
GetParentControlDesignerOf (Control control
)
390 if (control
!= null) {
391 IDesignerHost designerHost
= GetService (typeof (IDesignerHost
)) as IDesignerHost
;
392 if (designerHost
!= null) {
393 ParentControlDesigner designer
= null;
394 designer
= designerHost
.GetDesigner (this.Control
.Parent
) as ParentControlDesigner
;
395 if (designer
!= null)
402 protected virtual bool DrawGrid
{
403 get { return _drawGrid; }
408 SetValue (this.Component
, "SnapToGrid", false);
410 PopulateGridProperties ();
414 private bool SnapToGrid
{
415 get { return _snapToGrid; }
418 PopulateGridProperties ();
422 protected Size GridSize
{
423 get { return _gridSize; }
426 PopulateGridProperties ();
430 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconshouldpersistresetmethods.asp
432 // The ShouldSerializerPROPERTYNAME determines whether a property has changed from
433 // the default value and should get serialized.
435 // The ResetPROPERTYNAME resets the property to it's default value (used when
436 // one right clicks on a property in the property grid and clicks on "Reset".
439 private bool ShouldSerializeDrawGrid ()
441 return DrawGrid
!= _defaultDrawGrid
;
444 private void ResetDrawGrid ()
446 this.DrawGrid
= _defaultDrawGrid
;
449 private bool ShouldSerializeSnapToGrid ()
451 return _drawGrid
!= _defaultDrawGrid
;
454 private void ResetSnapToGrid ()
456 this.SnapToGrid
= _defaultSnapToGrid
;
459 private bool ShouldSerializeGridSize ()
461 return GridSize
!= _defaultGridSize
;
464 private void ResetGridSize ()
466 this.GridSize
= _defaultGridSize
;
471 #region Design-Time Mouse Drag and Drop
472 protected override void OnMouseDragBegin (int x
, int y
)
474 // do not call base here because the behaviour is specific for the ControlDesgner (does IUISelectionService.DragBegin)
477 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
478 if (selectionServ
!= null) {
479 // once ControlDesigner.MouseDragBegin is fired this will start getting dragover events.
481 Point location
= new Point (x
, y
);
482 IDesignerHost host
= GetService (typeof (IDesignerHost
)) as IDesignerHost
;
483 if (base.MouseButtonDown
== MouseButtons
.Middle
&& host
!= null && host
.RootComponent
!= this.Control
) {
484 location
= this.Control
.Parent
.PointToClient (this.Control
.PointToScreen (new Point (x
, y
)));
485 // I have to do this, because I get DragOver events fired for the control I am actually dragging
487 this.Control
.AllowDrop
= false;
488 selectionServ
.DragBegin ();
491 selectionServ
.MouseDragBegin (this.Control
, location
.X
, location
.Y
);
496 protected override void OnMouseDragMove (int x
, int y
)
498 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
499 if (selectionServ
!= null) {
500 Point location
= new Point (x
, y
);
501 if (!selectionServ
.SelectionInProgress
)
502 location
= this.SnapPointToGrid (new Point (x
, y
));
504 selectionServ
.MouseDragMove (location
.X
, location
.Y
);
508 protected override void OnMouseDragEnd (bool cancel
)
510 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
511 if (selectionServ
!= null) {
512 // If there is a Toolbox component seleted then create it instead of finishing the selection
513 IToolboxService toolBoxService
= this.GetService (typeof (IToolboxService
)) as IToolboxService
;
514 if (!cancel
&& toolBoxService
!= null && toolBoxService
.GetSelectedToolboxItem () != null) {
515 if (selectionServ
.SelectionInProgress
) {
516 bool hasSize
= selectionServ
.SelectionBounds
.Width
> 0 &&
517 selectionServ
.SelectionBounds
.Height
> 0;
518 CreateToolCore (toolBoxService
.GetSelectedToolboxItem (),
519 selectionServ
.SelectionBounds
.X
,
520 selectionServ
.SelectionBounds
.Y
,
521 selectionServ
.SelectionBounds
.Width
,
522 selectionServ
.SelectionBounds
.Height
,
524 toolBoxService
.SelectedToolboxItemUsed ();
526 } else if (!selectionServ
.SelectionInProgress
&&
527 !selectionServ
.ResizeInProgress
&& !selectionServ
.DragDropInProgress
){
528 CreateTool (toolBoxService
.GetSelectedToolboxItem (), _mouseDownPoint
);
529 toolBoxService
.SelectedToolboxItemUsed ();
534 if (selectionServ
.SelectionInProgress
|| selectionServ
.ResizeInProgress
)
535 selectionServ
.MouseDragEnd (cancel
);
539 protected override void OnDragComplete (DragEventArgs de
)
541 base.OnDragComplete (de
);
544 Point _mouseDownPoint
= Point
.Empty
;
546 internal override void OnMouseDown (int x
, int y
)
548 _mouseDownPoint
.X
= x
;
549 _mouseDownPoint
.Y
= y
;
550 base.OnMouseDown (x
, y
);
553 internal override void OnMouseUp ()
556 if (!this.Control
.AllowDrop
) // check MouseDragBegin for the reason of having this
557 this.Control
.AllowDrop
= true;
558 _mouseDownPoint
= Point
.Empty
;
561 internal override void OnMouseMove (int x
, int y
)
563 IUISelectionService uiSelection
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
564 if (uiSelection
!= null)
565 uiSelection
.SetCursor (x
, y
);
567 base.OnMouseMove (x
, y
);
570 // Align the point to the grid
572 private Point
SnapPointToGrid (Point location
)
574 Rectangle gridSurface
= this.Control
.Bounds
;
575 Size gridSize
= (Size
)GetValue (this.Component
, "GridSize");
577 if ((bool)GetValue (this.Component
, "SnapToGrid")) {
578 int x
= location
.X
+ (gridSize
.Width
- (location
.X
% gridSize
.Width
));
579 if (x
> gridSurface
.Width
)
580 x
= gridSurface
.Width
- gridSize
.Width
;
584 int y
= location
.Y
+ (gridSize
.Height
- (location
.Y
% gridSize
.Height
));
585 if (y
> gridSurface
.Height
)
586 y
= gridSurface
.Height
- gridSize
.Height
;
596 #region WndProc and Misc Message Handlers
598 protected override void OnSetCursor ()
600 if (this.Control
!= null) {
601 IToolboxService tbService
= GetService (typeof (IToolboxService
)) as IToolboxService
;
602 if (tbService
!= null)
603 tbService
.SetCursor ();
609 // Draws the design-time grid if DrawGrid == true
611 protected override void OnPaintAdornments (PaintEventArgs pe
)
613 base.OnPaintAdornments (pe
);
618 // in case WM_PAINT is received before the IDesignerFilter is invoked to add
621 drawGrid
= (bool)GetValue (this.Component
, "DrawGrid");
623 drawGrid
= this.DrawGrid
;
626 gridSize
= (Size
)GetValue (this.Component
, "GridSize");
628 gridSize
= this.GridSize
;
632 GraphicsState state
= pe
.Graphics
.Save ();
633 pe
.Graphics
.TranslateTransform (this.Control
.ClientRectangle
.X
,
634 this.Control
.ClientRectangle
.Y
);
635 ControlPaint
.DrawGrid (pe
.Graphics
, this.Control
.ClientRectangle
, gridSize
, this.Control
.BackColor
);
636 pe
.Graphics
.Restore (state
);
639 IUISelectionService selection
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
640 if (selection
!= null)
641 selection
.PaintAdornments (this.Control
, pe
.Graphics
);
647 protected Control
GetControl (object component
)
649 IComponent comp
= component
as IComponent
;
651 if (comp
!= null && comp
.Site
!= null) {
652 IDesignerHost host
= comp
.Site
.GetService (typeof (IDesignerHost
)) as IDesignerHost
;
654 ControlDesigner designer
= host
.GetDesigner (comp
) as ControlDesigner
;
655 if (designer
!= null)
656 return designer
.Control
;
662 #region NET_2_0 Stubs
664 protected virtual bool AllowControlLasso
{
665 get { return false; }
669 protected virtual bool AllowGenericDragBox
{
670 get { return false; }
674 protected internal virtual bool AllowSetChildIndexOnDrop
{
675 get { return false; }
679 public override IList SnapLines
{
680 get { return new object [0]; }
684 protected ToolboxItem MouseDragTool
{
689 public override void InitializeNewComponent (IDictionary defaultValues
)
691 base.InitializeNewComponent (defaultValues
);
695 protected void AddPaddingSnapLines (ref ArrayList snapLines
)
697 throw new NotImplementedException ();
701 protected virtual Control
GetParentForComponent (IComponent component
)
703 throw new NotImplementedException ();
707 protected override ControlBodyGlyph
GetControlGlyph (GlyphSelectionType selectionType
)
709 return base.GetControlGlyph (selectionType
);
713 public override GlyphCollection
GetGlyphs (GlyphSelectionType selectionType
)
715 return base.GetGlyphs (selectionType
);
719 protected Rectangle
GetUpdatedRect (Rectangle originalRect
, Rectangle dragRect
, bool updateSize
)
721 throw new NotImplementedException ();