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
;
39 using System
.Windows
.Forms
.Design
.Behavior
;
42 namespace System
.Windows
.Forms
.Design
46 public class ParentControlDesigner
: ControlDesigner
49 public ParentControlDesigner ()
54 #region Initialization
55 // Settings paths taken from the example at:
56 // http://msdn2.microsoft.com/en-us/library/system.componentmodel.design.idesigneroptionservice.aspx
58 public override void Initialize (IComponent component
)
60 base.Initialize (component
);
62 this.Control
.AllowDrop
= true;
64 // Initialize the default values of the Design-Time properties.
66 _defaultDrawGrid
= true;
67 _defaultSnapToGrid
= true;
68 _defaultGridSize
= new Size (8, 8);
70 // If the parent Control of the designed one has a ParentDesigner then inherit the values
71 // from it's designer.
73 if (this.Control
.Parent
!= null) {
74 ParentControlDesigner parentDesigner
= GetParentControlDesignerOf (Control
.Parent
);
75 if (parentDesigner
!= null) {
76 _defaultDrawGrid
= (bool) GetValue (parentDesigner
.Component
, "DrawGrid");
77 _defaultSnapToGrid
= (bool) GetValue (parentDesigner
.Component
, "SnapToGrid");
78 _defaultGridSize
= (Size
) GetValue (parentDesigner
.Component
, "GridSize");
82 // Else retrieve them through the IDesignerOptionService (if available)
84 IDesignerOptionService options
= GetService (typeof (IDesignerOptionService
)) as
85 IDesignerOptionService
;
86 if (options
!= null) {
88 value = options
.GetOptionValue (@"WindowsFormsDesigner\General", "DrawGrid");
90 _defaultDrawGrid
= (bool) value;
92 value = options
.GetOptionValue (@"WindowsFormsDesigner\General", "SnapToGrid");
94 _defaultSnapToGrid
= (bool) value;
96 value = options
.GetOptionValue (@"WindowsFormsDesigner\General", "GridSize");
98 _defaultGridSize
= (Size
) value;
102 IComponentChangeService componentChangeSvc
= GetService (typeof (IComponentChangeService
)) as IComponentChangeService
;
103 if (componentChangeSvc
!= null) {
104 componentChangeSvc
.ComponentRemoving
+= new ComponentEventHandler (OnComponentRemoving
);
105 componentChangeSvc
.ComponentRemoved
+= new ComponentEventHandler (OnComponentRemoved
);
108 // At the end set whatever we've managed to get
110 _drawGrid
= _defaultDrawGrid
;
111 _snapToGrid
= _defaultSnapToGrid
;
112 _gridSize
= _defaultGridSize
;
115 protected override void Dispose (bool disposing
)
118 EnableDragDrop (false);
119 OnMouseDragEnd (true);
121 base.Dispose (disposing
);
126 #region IToolboxService Related
128 // This is the code that is executed when you drop a tool from the Toolbox in the designer.
131 protected static void InvokeCreateTool (ParentControlDesigner toInvoke
, ToolboxItem tool
)
133 if (toInvoke
!= null)
134 toInvoke
.CreateTool (tool
);
137 protected void CreateTool (ToolboxItem tool
)
139 CreateToolCore (tool
, DefaultControlLocation
.X
, DefaultControlLocation
.Y
, 0, 0, true, false);
142 protected void CreateTool (ToolboxItem tool
, Point location
)
144 CreateToolCore (tool
, location
.X
, location
.Y
, 0, 0, true, false);
147 protected void CreateTool (ToolboxItem tool
, Rectangle bounds
)
149 CreateToolCore (tool
, bounds
.X
, bounds
.Y
, bounds
.Width
, bounds
.Width
, true, true);
152 // Creates a component from a ToolboxItem, sets its location and size if available and snaps it's
153 // location to the grid.
155 protected virtual IComponent
[] CreateToolCore (ToolboxItem tool
, int x
, int y
, int width
, int height
,
156 bool hasLocation
, bool hasSize
)
159 throw new ArgumentNullException ("tool");
161 IDesignerHost host
= GetService (typeof (IDesignerHost
)) as IDesignerHost
;
162 DesignerTransaction transaction
= host
.CreateTransaction ("Create components in tool '" + tool
.DisplayName
+ "'");
163 IComponent
[] components
= tool
.CreateComponents (host
);
165 foreach (IComponent component
in components
)
167 ControlDesigner controlDesigner
= host
.GetDesigner (component
) as ControlDesigner
;
168 if (controlDesigner
== null) { // not a Control, but e.g. a plain Component
170 } else if (!this.CanParent (controlDesigner
)) {
171 host
.DestroyComponent (component
);
175 Control control
= component
as Control
;
176 if (control
!= null) {
177 this.Control
.SuspendLayout ();
178 // set parent instead of controls.Add so that it gets serialized for Undo/Redo
179 TypeDescriptor
.GetProperties (control
)["Parent"].SetValue (control
, this.Control
);
180 this.Control
.SuspendLayout ();
183 base.SetValue (component
, "Location", this.SnapPointToGrid (new Point (x
, y
)));
185 base.SetValue (component
, "Location", this.SnapPointToGrid (this.DefaultControlLocation
));
188 base.SetValue (component
, "Size", new Size (width
, height
));
190 this.Control
.Refresh ();
193 ISelectionService selectionServ
= this.GetService (typeof (ISelectionService
)) as ISelectionService
;
194 if (selectionServ
!= null)
195 selectionServ
.SetSelectedComponents (components
, SelectionTypes
.Replace
);
196 transaction
.Commit ();
203 #region Drag and Drop
205 // If the control is not already parented return true
207 public virtual bool CanParent (Control control
)
210 return !control
.Contains (this.Control
);
215 public virtual bool CanParent (ControlDesigner designer
)
217 return CanParent (designer
.Control
);
220 protected override void OnDragDrop (DragEventArgs e
)
222 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
223 if (selectionServ
!= null) {
224 // once this is fired the parent control (parentcontroldesigner) will start getting dragover events.
226 Point location
= this.SnapPointToGrid (this.Control
.PointToClient (new Point (e
.X
, e
.Y
)));
227 selectionServ
.DragDrop (false, this.Control
, location
.X
, location
.Y
);
231 protected override void OnDragEnter (DragEventArgs e
)
233 this.Control
.Refresh ();
236 protected override void OnDragLeave (EventArgs e
)
238 this.Control
.Refresh ();
241 protected override void OnDragOver (DragEventArgs e
)
243 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
244 if (selectionServ
!= null) {
245 // once ControlDesigner.MouseDragBegin is called this will start getting dragover events.
247 Point location
= this.SnapPointToGrid (this.Control
.PointToClient (new Point (e
.X
, e
.Y
)));
248 selectionServ
.DragOver (this.Control
, location
.X
, location
.Y
);
250 e
.Effect
= DragDropEffects
.Move
;
256 // The default location where a control is placed, when added to the designer
258 protected virtual Point DefaultControlLocation
{
259 get { return new Point (0, 0); }
263 protected override bool EnableDragRect
{
268 #region ComponentChange
270 private void OnComponentRemoving (object sender
, ComponentEventArgs args
)
272 IComponentChangeService componentChangeSvc
= GetService (typeof (IComponentChangeService
)) as IComponentChangeService
;
273 Control control
= args
.Component
as Control
;
274 if (control
!= null && control
.Parent
== this.Control
&& componentChangeSvc
!= null)
275 componentChangeSvc
.OnComponentChanging (args
.Component
, TypeDescriptor
.GetProperties (args
.Component
)["Parent"]);
278 private void OnComponentRemoved (object sender
, ComponentEventArgs args
)
280 IComponentChangeService componentChangeSvc
= GetService (typeof (IComponentChangeService
)) as IComponentChangeService
;
281 Control control
= args
.Component
as Control
;
282 if (control
!= null && control
.Parent
== this.Control
&& componentChangeSvc
!= null) {
283 control
.Parent
= null;
284 componentChangeSvc
.OnComponentChanged (args
.Component
,
285 TypeDescriptor
.GetProperties (args
.Component
)["Parent"],
291 #region Design-Time Properties
293 private bool _defaultDrawGrid
;
294 private bool _defaultSnapToGrid
;
295 private Size _defaultGridSize
;
296 private bool _drawGrid
;
297 private bool _snapToGrid
;
298 private Size _gridSize
;
300 //This method adds the following design-time browsable properties:
301 // "DrawGrid", "SnapToGrid", and "GridSize".
303 protected override void PreFilterProperties (IDictionary properties
)
305 base.PreFilterProperties (properties
);
307 properties
["DrawGrid"] = TypeDescriptor
.CreateProperty (typeof (ParentControlDesigner
),
311 BrowsableAttribute
.Yes
,
312 DesignOnlyAttribute
.Yes
,
313 new DescriptionAttribute (
314 "Indicates whether or not to draw the positioning grid."),
315 CategoryAttribute
.Design
318 properties
["SnapToGrid"] = TypeDescriptor
.CreateProperty (typeof (ParentControlDesigner
),
322 BrowsableAttribute
.Yes
,
323 DesignOnlyAttribute
.Yes
,
324 new DescriptionAttribute (
325 "Determines if controls should snap to the positioning grid."),
326 CategoryAttribute
.Design
329 properties
["GridSize"] = TypeDescriptor
.CreateProperty (typeof (ParentControlDesigner
),
333 BrowsableAttribute
.Yes
,
334 DesignOnlyAttribute
.Yes
,
335 new DescriptionAttribute (
336 "Determines the size of the positioning grid."),
337 CategoryAttribute
.Design
343 // Informs all children controls' ParentControlDesigners that the grid properties
344 // have changed and passes them
346 private void PopulateGridProperties ()
348 // Control.Invalidate (true) will redraw the control and it's children
349 // this will cause a WM_PAINT message to be send and the ControlDesigenr will raise
350 // the OnPaintAdornments, where the grid drawing takes place.
352 // Note that this should be called *after* the grid properties have changed :-)
354 this.Control
.Invalidate (false);
356 if (this.Control
!= null) {
357 ParentControlDesigner designer
= null;
358 foreach (Control control
in this.Control
.Controls
) {
359 designer
= this.GetParentControlDesignerOf (control
);
360 if (designer
!= null)
361 designer
.OnParentGridPropertiesChanged (this);
366 // Called by the parent ParentControlDesigner when it is populating the grid-related
367 // design-time properties changes
369 private void OnParentGridPropertiesChanged (ParentControlDesigner parentDesigner
)
371 SetValue (this.Component
, "DrawGrid", (bool) GetValue (parentDesigner
.Component
, "DrawGrid"));
372 SetValue (this.Component
, "SnapToGrid", (bool) GetValue (parentDesigner
.Component
, "SnapToGrid"));
373 SetValue (this.Component
, "GridSize", (Size
) GetValue (parentDesigner
.Component
, "GridSize"));
375 // Set also the default values to be those, because we should
376 // match the parent ParentControlDesigner values.
377 // called recursivly, so I will rather go for slower, but no stack-overflowable code
379 _defaultDrawGrid
= (bool) GetValue (parentDesigner
.Component
, "DrawGrid");
380 _defaultSnapToGrid
= (bool) GetValue (parentDesigner
.Component
, "SnapToGrid");
381 _defaultGridSize
= (Size
) GetValue (parentDesigner
.Component
, "GridSize");
383 this.PopulateGridProperties ();
387 // Retrieves the ParentControlDesigner of the specified control if available,
388 // else returns null.
390 private ParentControlDesigner
GetParentControlDesignerOf (Control control
)
392 if (control
!= null) {
393 IDesignerHost designerHost
= GetService (typeof (IDesignerHost
)) as IDesignerHost
;
394 if (designerHost
!= null) {
395 ParentControlDesigner designer
= null;
396 designer
= designerHost
.GetDesigner (this.Control
.Parent
) as ParentControlDesigner
;
397 if (designer
!= null)
404 protected virtual bool DrawGrid
{
405 get { return _drawGrid; }
410 SetValue (this.Component
, "SnapToGrid", false);
412 PopulateGridProperties ();
416 private bool SnapToGrid
{
417 get { return _snapToGrid; }
420 PopulateGridProperties ();
424 protected Size GridSize
{
425 get { return _gridSize; }
428 PopulateGridProperties ();
432 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconshouldpersistresetmethods.asp
434 // The ShouldSerializerPROPERTYNAME determines whether a property has changed from
435 // the default value and should get serialized.
437 // The ResetPROPERTYNAME resets the property to it's default value (used when
438 // one right clicks on a property in the property grid and clicks on "Reset".
441 private bool ShouldSerializeDrawGrid ()
443 return DrawGrid
!= _defaultDrawGrid
;
446 private void ResetDrawGrid ()
448 this.DrawGrid
= _defaultDrawGrid
;
451 private bool ShouldSerializeSnapToGrid ()
453 return _drawGrid
!= _defaultDrawGrid
;
456 private void ResetSnapToGrid ()
458 this.SnapToGrid
= _defaultSnapToGrid
;
461 private bool ShouldSerializeGridSize ()
463 return GridSize
!= _defaultGridSize
;
466 private void ResetGridSize ()
468 this.GridSize
= _defaultGridSize
;
473 #region Design-Time Mouse Drag and Drop
474 protected override void OnMouseDragBegin (int x
, int y
)
476 // do not call base here because the behaviour is specific for the ControlDesgner (does IUISelectionService.DragBegin)
479 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
480 if (selectionServ
!= null) {
481 // once ControlDesigner.MouseDragBegin is fired this will start getting dragover events.
483 Point location
= new Point (x
, y
);
484 IDesignerHost host
= GetService (typeof (IDesignerHost
)) as IDesignerHost
;
485 if (base.MouseButtonDown
== MouseButtons
.Middle
&& host
!= null && host
.RootComponent
!= this.Control
) {
486 location
= this.Control
.Parent
.PointToClient (this.Control
.PointToScreen (new Point (x
, y
)));
487 // I have to do this, because I get DragOver events fired for the control I am actually dragging
489 this.Control
.AllowDrop
= false;
490 selectionServ
.DragBegin ();
493 selectionServ
.MouseDragBegin (this.Control
, location
.X
, location
.Y
);
498 protected override void OnMouseDragMove (int x
, int y
)
500 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
501 if (selectionServ
!= null) {
502 Point location
= new Point (x
, y
);
503 if (!selectionServ
.SelectionInProgress
)
504 location
= this.SnapPointToGrid (new Point (x
, y
));
506 selectionServ
.MouseDragMove (location
.X
, location
.Y
);
510 protected override void OnMouseDragEnd (bool cancel
)
512 IUISelectionService selectionServ
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
513 if (selectionServ
!= null) {
514 // If there is a Toolbox component seleted then create it instead of finishing the selection
515 IToolboxService toolBoxService
= this.GetService (typeof (IToolboxService
)) as IToolboxService
;
516 if (!cancel
&& toolBoxService
!= null && toolBoxService
.GetSelectedToolboxItem () != null) {
517 if (selectionServ
.SelectionInProgress
) {
518 bool hasSize
= selectionServ
.SelectionBounds
.Width
> 0 &&
519 selectionServ
.SelectionBounds
.Height
> 0;
520 CreateToolCore (toolBoxService
.GetSelectedToolboxItem (),
521 selectionServ
.SelectionBounds
.X
,
522 selectionServ
.SelectionBounds
.Y
,
523 selectionServ
.SelectionBounds
.Width
,
524 selectionServ
.SelectionBounds
.Height
,
526 toolBoxService
.SelectedToolboxItemUsed ();
528 } else if (!selectionServ
.SelectionInProgress
&&
529 !selectionServ
.ResizeInProgress
&& !selectionServ
.DragDropInProgress
){
530 CreateTool (toolBoxService
.GetSelectedToolboxItem (), _mouseDownPoint
);
531 toolBoxService
.SelectedToolboxItemUsed ();
536 if (selectionServ
.SelectionInProgress
|| selectionServ
.ResizeInProgress
)
537 selectionServ
.MouseDragEnd (cancel
);
542 protected override void OnDragComplete (DragEventArgs de
)
544 base.OnDragComplete (de
);
548 Point _mouseDownPoint
= Point
.Empty
;
550 internal override void OnMouseDown (int x
, int y
)
552 _mouseDownPoint
.X
= x
;
553 _mouseDownPoint
.Y
= y
;
554 base.OnMouseDown (x
, y
);
557 internal override void OnMouseUp ()
560 if (!this.Control
.AllowDrop
) // check MouseDragBegin for the reason of having this
561 this.Control
.AllowDrop
= true;
562 _mouseDownPoint
= Point
.Empty
;
565 internal override void OnMouseMove (int x
, int y
)
567 IUISelectionService uiSelection
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
568 if (uiSelection
!= null)
569 uiSelection
.SetCursor (x
, y
);
571 base.OnMouseMove (x
, y
);
574 // Align the point to the grid
576 private Point
SnapPointToGrid (Point location
)
578 Rectangle gridSurface
= this.Control
.Bounds
;
579 Size gridSize
= (Size
)GetValue (this.Component
, "GridSize");
581 if ((bool)GetValue (this.Component
, "SnapToGrid")) {
582 int x
= location
.X
+ (gridSize
.Width
- (location
.X
% gridSize
.Width
));
583 if (x
> gridSurface
.Width
)
584 x
= gridSurface
.Width
- gridSize
.Width
;
588 int y
= location
.Y
+ (gridSize
.Height
- (location
.Y
% gridSize
.Height
));
589 if (y
> gridSurface
.Height
)
590 y
= gridSurface
.Height
- gridSize
.Height
;
600 #region WndProc and Misc Message Handlers
602 protected override void OnSetCursor ()
604 if (this.Control
!= null) {
605 IToolboxService tbService
= GetService (typeof (IToolboxService
)) as IToolboxService
;
606 if (tbService
!= null)
607 tbService
.SetCursor ();
613 // Draws the design-time grid if DrawGrid == true
615 protected override void OnPaintAdornments (PaintEventArgs pe
)
617 base.OnPaintAdornments (pe
);
622 // in case WM_PAINT is received before the IDesignerFilter is invoked to add
625 drawGrid
= (bool)GetValue (this.Component
, "DrawGrid");
627 drawGrid
= this.DrawGrid
;
630 gridSize
= (Size
)GetValue (this.Component
, "GridSize");
632 gridSize
= this.GridSize
;
636 GraphicsState state
= pe
.Graphics
.Save ();
637 pe
.Graphics
.TranslateTransform (this.Control
.ClientRectangle
.X
,
638 this.Control
.ClientRectangle
.Y
);
639 ControlPaint
.DrawGrid (pe
.Graphics
, this.Control
.ClientRectangle
, gridSize
, this.Control
.BackColor
);
640 pe
.Graphics
.Restore (state
);
643 IUISelectionService selection
= this.GetService (typeof (IUISelectionService
)) as IUISelectionService
;
644 if (selection
!= null)
645 selection
.PaintAdornments (this.Control
, pe
.Graphics
);
651 protected Control
GetControl (object component
)
653 IComponent comp
= component
as IComponent
;
655 if (comp
!= null && comp
.Site
!= null) {
656 IDesignerHost host
= comp
.Site
.GetService (typeof (IDesignerHost
)) as IDesignerHost
;
658 ControlDesigner designer
= host
.GetDesigner (comp
) as ControlDesigner
;
659 if (designer
!= null)
660 return designer
.Control
;
666 #region NET_2_0 Stubs
669 protected virtual bool AllowControlLasso
{
670 get { return false; }
674 protected virtual bool AllowGenericDragBox
{
675 get { return false; }
679 protected internal virtual bool AllowSetChildIndexOnDrop
{
680 get { return false; }
684 public override IList SnapLines
{
685 get { return new object [0]; }
689 protected ToolboxItem MouseDragTool
{
694 public override void InitializeNewComponent (IDictionary defaultValues
)
696 base.InitializeNewComponent (defaultValues
);
700 protected void AddPaddingSnapLines (ref ArrayList snapLines
)
702 throw new NotImplementedException ();
706 protected virtual Control
GetParentForComponent (IComponent component
)
708 throw new NotImplementedException ();
712 protected override ControlBodyGlyph
GetControlGlyph (GlyphSelectionType selectionType
)
714 return base.GetControlGlyph (selectionType
);
718 public override GlyphCollection
GetGlyphs (GlyphSelectionType selectionType
)
720 return base.GetGlyphs (selectionType
);
724 protected Rectangle
GetUpdatedRect (Rectangle originalRect
, Rectangle dragRect
, bool updateSize
)
726 throw new NotImplementedException ();