2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Design / System.Windows.Forms.Design / ParentControlDesigner.cs
blobbe8e0e6caf38f241b15dfe3e50a58ebd8abb2fa5
1 //
2 // System.Windows.Forms.Design.ParentControlDesigner
3 //
4 // Authors:
5 // Ivan N. Zlatev (contact i-nZ.net)
6 //
7 // (C) 2006 Ivan N. Zlatev
9 //
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.
30 using System;
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
33 using System.Windows.Forms;
34 using System.Drawing;
35 using System.Drawing.Drawing2D;
36 using System.Drawing.Design;
37 using System.Collections;
38 #if NET_2_0
39 using System.Windows.Forms.Design.Behavior;
40 #endif
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");
81 else {
82 // Else retrieve them through the IDesignerOptionService (if available)
84 IDesignerOptionService options = GetService (typeof (IDesignerOptionService)) as
85 IDesignerOptionService;
86 if (options != null) {
87 object value = null;
88 value = options.GetOptionValue (@"WindowsFormsDesigner\General", "DrawGrid");
89 if (value is bool)
90 _defaultDrawGrid = (bool) value;
92 value = options.GetOptionValue (@"WindowsFormsDesigner\General", "SnapToGrid");
93 if (value is bool)
94 _defaultSnapToGrid = (bool) value;
96 value = options.GetOptionValue (@"WindowsFormsDesigner\General", "GridSize");
97 if (value is Size)
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)
117 if (disposing) {
118 EnableDragDrop (false);
119 OnMouseDragEnd (true);
121 base.Dispose (disposing);
123 #endregion
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)
158 if (tool == null)
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
169 continue;
170 } else if (!this.CanParent (controlDesigner)) {
171 host.DestroyComponent (component);
172 continue;
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 ();
182 if (hasLocation)
183 base.SetValue (component, "Location", this.SnapPointToGrid (new Point (x, y)));
184 else
185 base.SetValue (component, "Location", this.SnapPointToGrid (this.DefaultControlLocation));
187 if (hasSize)
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 ();
197 return components;
200 #endregion
203 #region Drag and Drop
205 // If the control is not already parented return true
207 public virtual bool CanParent (Control control)
209 if (control != null)
210 return !control.Contains (this.Control);
212 return false;
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;
252 #endregion
255 #region Properties
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 {
264 get { return true; }
266 #endregion
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"],
286 this.Control, null);
289 #endregion
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),
308 "DrawGrid",
309 typeof (bool),
310 new Attribute[] {
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),
319 "SnapToGrid",
320 typeof (bool),
321 new Attribute[] {
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),
330 "GridSize",
331 typeof (Size),
332 new Attribute[] {
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)
398 return designer;
401 return null;
404 protected virtual bool DrawGrid {
405 get { return _drawGrid; }
406 set {
407 _drawGrid = value;
409 if (value == false)
410 SetValue (this.Component, "SnapToGrid", false);
412 PopulateGridProperties ();
416 private bool SnapToGrid {
417 get { return _snapToGrid; }
418 set {
419 _snapToGrid = value;
420 PopulateGridProperties ();
424 protected Size GridSize {
425 get { return _gridSize; }
426 set {
427 _gridSize = value;
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;
470 #endregion
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 ();
492 else {
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,
525 true, hasSize);
526 toolBoxService.SelectedToolboxItemUsed ();
527 cancel = true;
528 } else if (!selectionServ.SelectionInProgress &&
529 !selectionServ.ResizeInProgress && !selectionServ.DragDropInProgress){
530 CreateTool (toolBoxService.GetSelectedToolboxItem (), _mouseDownPoint);
531 toolBoxService.SelectedToolboxItemUsed ();
532 cancel = true;
536 if (selectionServ.SelectionInProgress || selectionServ.ResizeInProgress)
537 selectionServ.MouseDragEnd (cancel);
541 #if NET_2_0
542 protected override void OnDragComplete (DragEventArgs de)
544 base.OnDragComplete (de);
546 #endif
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 ()
559 base.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;
586 location.X = x;
588 int y = location.Y + (gridSize.Height - (location.Y % gridSize.Height));
589 if (y > gridSurface.Height)
590 y = gridSurface.Height - gridSize.Height;
592 location.Y = y;
594 return location;
597 #endregion
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 ();
608 else
609 base.OnSetCursor ();
613 // Draws the design-time grid if DrawGrid == true
615 protected override void OnPaintAdornments (PaintEventArgs pe)
617 base.OnPaintAdornments (pe);
619 bool drawGrid;
620 Size gridSize;
622 // in case WM_PAINT is received before the IDesignerFilter is invoked to add
623 // those properties.
624 try {
625 drawGrid = (bool)GetValue (this.Component, "DrawGrid");
626 } catch {
627 drawGrid = this.DrawGrid;
629 try {
630 gridSize = (Size)GetValue (this.Component, "GridSize");
631 } catch {
632 gridSize = this.GridSize;
635 if (drawGrid) {
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);
648 #endregion
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;
657 if (host != null) {
658 ControlDesigner designer = host.GetDesigner (comp) as ControlDesigner;
659 if (designer != null)
660 return designer.Control;
663 return null;
666 #region NET_2_0 Stubs
667 #if NET_2_0
668 [MonoTODO]
669 protected virtual bool AllowControlLasso {
670 get { return false; }
673 [MonoTODO]
674 protected virtual bool AllowGenericDragBox {
675 get { return false; }
678 [MonoTODO]
679 protected internal virtual bool AllowSetChildIndexOnDrop {
680 get { return false; }
683 [MonoTODO]
684 public override IList SnapLines {
685 get { return new object [0]; }
688 [MonoTODO]
689 protected ToolboxItem MouseDragTool {
690 get { return null; }
693 [MonoTODO]
694 public override void InitializeNewComponent (IDictionary defaultValues)
696 base.InitializeNewComponent (defaultValues);
699 [MonoTODO]
700 protected void AddPaddingSnapLines (ref ArrayList snapLines)
702 throw new NotImplementedException ();
705 [MonoTODO]
706 protected virtual Control GetParentForComponent (IComponent component)
708 throw new NotImplementedException ();
711 [MonoTODO]
712 protected override ControlBodyGlyph GetControlGlyph (GlyphSelectionType selectionType)
714 return base.GetControlGlyph (selectionType);
717 [MonoTODO]
718 public override GlyphCollection GetGlyphs (GlyphSelectionType selectionType)
720 return base.GetGlyphs (selectionType);
723 [MonoTODO]
724 protected Rectangle GetUpdatedRect (Rectangle originalRect, Rectangle dragRect, bool updateSize)
726 throw new NotImplementedException ();
728 #endif
729 #endregion