SelectionUIHandler.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / WinForms / System / WinForms / Design / SelectionUIHandler.cs / 1 / SelectionUIHandler.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 */ 
namespace System.Windows.Forms.Design {
    using System.Design; 
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Diagnostics;
    using System; 
    using System.ComponentModel.Design;
    using System.Windows.Forms; 
    using System.Drawing; 
    using Microsoft.Win32;
    using System.Windows.Forms.Design; 
    using System.Windows.Forms.ComponentModel;
    using System.Globalization;

    ///  
    /// 
    ///     
    ///       This is an abstract base class that encapsulates a lot of 
    ///       the details of handling selection drags. Just about everyone
    ///       that implements a selection UI handler will extend this. 
    ///    
    /// 
    internal abstract class SelectionUIHandler {
 
        // These handle our drag feedback.  These come into play when we're
        // actually moving components around.  The selection UI service 
        // dictates when this happens. 
        //
        private Rectangle    dragOffset = Rectangle.Empty;        // this gets added to a component's x, y, width, height 
        private Control[]    dragControls;      // the set of controls we're dragging
        private BoundsInfo[] originalCoords;    // the saved coordinates of the components we're dragging.
        private SelectionRules rules;           // the rules of the current drag.
 
        private const int MinControlWidth = 3;
        private const int MinControlHeight = 3; 
        ///  
        /// 
        ///     Begins a drag operation.  A designer should examine the list of components 
        ///     to see if it wants to support the drag.  If it does, it should return
        ///     true.  If it returns true, the designer should provide
        ///     UI feedback about the drag at this time.  Typically, this feedback consists
        ///     of an inverted rectangle for each component, or a caret if the component 
        ///     is text.
        ///  
        public virtual bool BeginDrag(object[] components, SelectionRules rules, int initialX, int initialY) { 
            dragOffset = new Rectangle();
            originalCoords = null; 
            this.rules = rules;

            dragControls = new Control[components.Length];
            for(int i = 0; i < components.Length; i++) { 
                Debug.Assert(components[i] is IComponent, "Selection UI handler only deals with IComponents");
                dragControls[i] = GetControl((IComponent)components[i]); 
                Debug.Assert(dragControls[i] != null, "Everyone must have a control"); 
            }
 
            // allow the cliprect to go just beyond the window by one grid.  This helps with round off
            // problems.  We can only do this if the container itself is not in the selection.  Also,
            // if the container is a form and it has autoscroll turned on, we allow a drag beyond the
            // container boundary on the width and height, but not top and left. 
            //
            bool containerSelected = false; 
            IComponent container = GetComponent(); 
            for (int i = 0; i < components.Length; i++) {
                if (components[i] == container) { 
                    containerSelected = true;
                    break;
                }
            } 

            if (!containerSelected) { 
                Control containerControl = GetControl(); 
                Size snapSize = GetCurrentSnapSize();
                Rectangle containerRect = containerControl.RectangleToScreen(containerControl.ClientRectangle); 
                containerRect.Inflate(snapSize.Width, snapSize.Height);
                ScrollableControl sc = GetControl() as ScrollableControl;
                if (sc != null && sc.AutoScroll) {
                    Rectangle screen = SystemInformation.VirtualScreen; 
                    containerRect.Width = screen.Width;
                    containerRect.Height = screen.Height; 
                } 

            } 
            return true;
        }

        ///  
        /// 
        ///      This is called by MoveControls when the user has requested that the move be 
        ///      cancelled.  This puts all the controls back to where they were. 
        /// 
        private void CancelControlMove(Control[] controls, BoundsInfo[] bounds) { 
            Debug.Assert(bounds != null && controls != null && bounds.Length == controls.Length, "bounds->controls mismatch");

            Rectangle b = new Rectangle();
 
            // Whip through each of the controls.
            // 
            for (int i = 0; i < controls.Length; i++) { 

                Control parent = controls[i].Parent; 

                // Suspend parent layout so that we don't continously re-arrange components
                // while we move.
                // 
                if (parent != null) {
                    parent.SuspendLayout(); 
                } 

                b.X = bounds[i].X; 
                b.Y = bounds[i].Y;
                b.Width = bounds[i].Width;
                b.Height = bounds[i].Height;
 
                controls[i].Bounds = b;
            } 
 
            // And resume their layout
            // 
            for (int i = 0; i < controls.Length; i++) {
                Control parent = controls[i].Parent;
                if (parent != null) {
                    parent.ResumeLayout(); 
                }
            } 
        } 

        ///  
        /// 
        ///     Called when the user has moved the mouse.  This will only be called on
        ///     the designer that returned true from beginDrag.  The designer
        ///     should update its UI feedback here. 
        /// 
        public virtual void DragMoved(object[] components, Rectangle offset) { 
            dragOffset = offset; 
            MoveControls(components, false, false);
            Debug.Assert(originalCoords != null, "We are keying off of originalCoords, but MoveControls didn't set it"); 
        }

        /// 
        ///  
        ///     Called when the user has completed the drag.  The designer should
        ///     remove any UI feedback it may be providing. 
        ///  
        public virtual void EndDrag(object[] components, bool cancel) {
            try { 
                MoveControls(components, cancel, true);
            }
            catch (CheckoutException checkoutEx) {
                if (checkoutEx == CheckoutException.Canceled) { 
                    MoveControls(components, true, false);
                } 
                else { 
                    throw checkoutEx;
                } 
            }
        }

        ///  
        /// 
        ///      Retrieves the base component for the selection handler. 
        ///  
        protected abstract IComponent GetComponent();
 
        /// 
        /// 
        ///      Retrieves the base component's UI control for the selection handler.
        ///  
        protected abstract Control GetControl();
 
        ///  
        /// 
        ///      Retrieves the UI control for the given component. 
        /// 
        protected abstract Control GetControl(IComponent component);

        ///  
        /// 
        ///      Retrieves the current grid snap size we should snap objects 
        ///      to. 
        /// 
        protected abstract Size GetCurrentSnapSize(); 

        /// 
        /// 
        ///      We use this to request often-used services. 
        /// 
        protected abstract object GetService(Type serviceType); 
 
        /// 
        ///  
        ///      Determines if the selection UI handler should attempt to snap
        ///      objects to a grid.
        /// 
        protected abstract bool GetShouldSnapToGrid(); 

        ///  
        ///  
        ///      Given a rectangle, this updates the dimensions of it
        ///      with any grid snaps and returns a new rectangle.  If 
        ///      no changes to the rectangle's size were needed, this
        ///      may return the same rectangle.
        /// 
        public abstract Rectangle GetUpdatedRect(Rectangle orignalRect, Rectangle dragRect, bool updateSize); 

        ///  
        ///  
        ///      Called when we need to move the controls on our frame while dragging.  This
        ///      can perform three operations:  It can update the current controls location, 
        ///      it can commit the new controls (final move), and it can roll back a movement
        ///      to the beginning of the operation.
        /// 
        private void MoveControls(object[] components, bool cancel, bool finalMove) { 
            Control[] controls = dragControls;
            Rectangle offset = dragOffset; 
            BoundsInfo[] bounds = originalCoords; 
            Point adjustedLoc = new Point();
 
            // Erase the clipping and other state if this is the final move.
            //
            if (finalMove) {
                Cursor.Clip = Rectangle.Empty; 
                dragOffset = Rectangle.Empty;
                dragControls = null; 
                originalCoords = null; 
            }
 
            // If we haven't started to move yet, there's nothing to do.
            //
            if (offset.IsEmpty) {
                return; 
            }
 
            // Short circuit the work if we didn't move anything. 
            // This will prevent a change notification for just
            // selecting components. 
            //
            if (finalMove && offset.X == 0 && offset.Y == 0 && offset.Width == 0 && offset.Height == 0) {
                return;
            } 

            // If cancel was specified, we must roll all controls back to their original positions. 
            // 
            if (cancel) {
                CancelControlMove(controls, bounds); 
                return;
            }

            // We must keep track of the original coordinates of each control, just in case 
            // the user cancels out of moving them.  So, we create a "BoundsInfo" object for
            // each control that saves this state. 
            // 
            if (originalCoords == null && !finalMove) {
                originalCoords = new BoundsInfo[controls.Length]; 
                for (int i = 0; i < controls.Length; i++) {
                    originalCoords[i] = new BoundsInfo(controls[i]);
                }
                bounds = originalCoords; 
            }
 
            // Two passes here.  First pass suspends all parent layout and updates the 
            // component positions.  Second pass re-enables layout.
            // 
            for (int i = 0; i < controls.Length; i++) {
                Debug.Assert(controls[i] == GetControl((IComponent)components[i]), "Control->Component mapping is out of sync");

                Control parent = controls[i].Parent; 

                // Suspend parent layout so that we don't continously re-arrange components 
                // while we move. 
                //
                if (parent != null) { 
                    parent.SuspendLayout();
                }

                BoundsInfo ctlBounds = bounds[i]; 

                adjustedLoc.X = ctlBounds.lastRequestedX; 
                adjustedLoc.Y = ctlBounds.lastRequestedY; 

                if (!finalMove) { 
                    ctlBounds.lastRequestedX += offset.X;
                    ctlBounds.lastRequestedY += offset.Y;
                    ctlBounds.lastRequestedWidth += offset.Width;
                    ctlBounds.lastRequestedHeight += offset.Height; 
                }
 
                // Our "target" values are the ones we would like to set 
                // the control to.  We may modify them if they would mke the control
                // size negative or zero, however. 
                //
                int targetX = ctlBounds.lastRequestedX;
                int targetY = ctlBounds.lastRequestedY;
                int targetWidth = ctlBounds.lastRequestedWidth; 
                int targetHeight = ctlBounds.lastRequestedHeight;
 
                Rectangle oldBounds = controls[i].Bounds; 

                if ((rules & SelectionRules.Moveable) == 0) { 
                    // We use the minimum size of either a grid snap or 1
                    //
                    Size minSize;
 
                    if (GetShouldSnapToGrid()) {
                        minSize = GetCurrentSnapSize(); 
                    } 
                    else {
                        minSize = new Size(1, 1); 
                    }

                    if (targetWidth < minSize.Width) {
                        targetWidth = minSize.Width; 
                        targetX = oldBounds.X;
                    } 
 
                    if (targetHeight < minSize.Height) {
                        targetHeight = minSize.Height; 
                        targetY = oldBounds.Y;
                    }
                }
 
                //Bug #72905   Form X,Y defaulted to (0,0)
                // 
 
                IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
                Debug.Assert(host != null, "No designer host"); 
                if (controls[i] == host.RootComponent) {
                    targetX = 0;
                    targetY = 0;
                } 

                // Adjust our target dimensions by the grid snaps 
                // 
                Rectangle tempNewBounds = GetUpdatedRect(oldBounds, new Rectangle(targetX, targetY, targetWidth, targetHeight), true);
                Rectangle newBounds = oldBounds; 

                // Now apply the correct values to newBounds -- we only want to apply the new value if our
                // selection rules dictate so.
                // 
                if ((rules & SelectionRules.Moveable) != 0) {
                    newBounds.X = tempNewBounds.X; 
                    newBounds.Y = tempNewBounds.Y; 
                }
                else { 
                    if ((rules & SelectionRules.TopSizeable) != 0) {
                        newBounds.Y = tempNewBounds.Y;
                        newBounds.Height = tempNewBounds.Height;
                    } 
                    if ((rules & SelectionRules.BottomSizeable) != 0) {
                        newBounds.Height = tempNewBounds.Height; 
                    } 
                    if ((rules & SelectionRules.LeftSizeable) != 0) {
                        newBounds.X = tempNewBounds.X; 
                        newBounds.Width = tempNewBounds.Width;
                    }
                    if ((rules & SelectionRules.RightSizeable) != 0) {
                        newBounds.Width = tempNewBounds.Width; 
                    }
                } 
 
                bool locChanged = (offset.X != 0 || offset.Y != 0);
                bool sizeChanged = (offset.Width != 0 || offset.Height != 0); 

                // If both the location and size changed, attempt to update the control in
                // one step.  This will prevent flicker.
                // 
                if (locChanged && sizeChanged) {
 
                    // We shouldn't care if we're directly manipulating the control or not during 
                    // the move.  For perf, just call directly on the control.  For the final
                    // move, however, we must go through the property descriptor. 
                    //
                    PropertyDescriptor boundsProp = TypeDescriptor.GetProperties(components[i])["Bounds"];

                    if (boundsProp != null && !boundsProp.IsReadOnly) { 
                        if (finalMove) {
                            object component = components[i]; 
                            boundsProp.SetValue(component, newBounds); 
                        }
                        else { 
                            controls[i].Bounds = newBounds;
                        }

                        // Now reset the loc and size changed flags so 
                        // we don't try to change again.
                        // 
                        locChanged = sizeChanged = false; 
                    }
                } 

                // Adjust the location property with the new value.  ONLY do this if the offset
                // is nonzero, however.  Otherwise we may get round-off errors that can cause flicker.
                // Plus, why set the property if it shouldn't be? 
                //
                if (locChanged) { 
 
                    adjustedLoc.X = newBounds.X;
                    adjustedLoc.Y = newBounds.Y; 

                    PropertyDescriptor trayProp = TypeDescriptor.GetProperties(components[i])["TrayLocation"];

                    if (trayProp != null && !trayProp.IsReadOnly) { 
                        trayProp.SetValue(components[i], adjustedLoc);
                    } 
                    else { 
                        // We shouldn't care if we're directly manipulating the control or not during
                        // the move.  For perf, just call directly on the control.  For the final 
                        // move, however, we must go through the property descriptor.
                        //
                        PropertyDescriptor leftProp = TypeDescriptor.GetProperties(components[i])["Left"];
                        PropertyDescriptor topProp = TypeDescriptor.GetProperties(components[i])["Top"]; 
                        if (topProp != null && !topProp.IsReadOnly) {
                            if (finalMove) { 
                                object component = components[i]; 
                                topProp.SetValue(component, adjustedLoc.Y);
                            } 
                            else {
                                controls[i].Top = adjustedLoc.Y;
                            }
                        } 

                        if (leftProp != null && !leftProp.IsReadOnly) { 
                            if (finalMove) { 
                                object component = components[i];
                                leftProp.SetValue(component, adjustedLoc.X); 
                            }
                            else {
                                controls[i].Left = adjustedLoc.X;
                            } 
                        }
 
                        if (leftProp == null || topProp == null) { 
                            PropertyDescriptor locationProp = TypeDescriptor.GetProperties(components[i])["Location"];
                            if (locationProp != null && !locationProp.IsReadOnly) { 
                                locationProp.SetValue(components[i], adjustedLoc);
                            }
                        }
                    } 

                } 
 
                // Adjust the size property with the new value.  ONLY do this if the offset
                // is nonzero, however.  Otherwise we may get round-off errors that can cause flicker. 
                // Plus, why set the property if it shouldn't be?
                //
                if (sizeChanged) {
 
                    // If you are tempted to hoist this "new" out of the loop, don't.  The undo
                    // unit below just holds a reference to it. 
                    // 
                    Size size = new Size(Math.Max(MinControlWidth, newBounds.Width), Math.Max(MinControlHeight,newBounds.Height));
 
                    PropertyDescriptor widthProp = TypeDescriptor.GetProperties(components[i])["Width"];
                    PropertyDescriptor heightProp = TypeDescriptor.GetProperties(components[i])["Height"];

                    if (widthProp != null && !widthProp.IsReadOnly && size.Width != (int)widthProp.GetValue(components[i])) { 
                        if (finalMove) {
                            object component = components[i]; 
                            widthProp.SetValue(component, size); 
                        }
                        else { 
                            controls[i].Width = size.Width;
                        }
                    }
 
                    if (heightProp != null && !heightProp.IsReadOnly && size.Height != (int)heightProp.GetValue(components[i])) {
                        if (finalMove) { 
                            object component = components[i]; 
                            heightProp.SetValue(component, size);
                        } 
                        else {
                            controls[i].Height = size.Height;
                        }
                    } 

                } 
            } 

            // Now resume the parent layouts 
            //
            for (int i = 0; i < controls.Length; i++) {
                Control parent = controls[i].Parent;
 
                if (parent != null) {
                    parent.ResumeLayout(); 
                    parent.Update(); 
                }
 
                controls[i].Update();
            }
        }
 
        /// 
        ///  
        ///     Queries to see if a drag operation 
        ///     is valid on this handler for the given set of components.
        ///     If it returns true, BeginDrag will be called immediately after. 
        /// 
        public bool QueryBeginDrag(object[] components, SelectionRules rules, int initialX, int initialY){

            IComponentChangeService cs = (IComponentChangeService)GetService(typeof(IComponentChangeService)); 
            if (cs != null) {
                try { 
                    if (components != null && components.Length > 0) { 
                        foreach (object c in components) {
                            cs.OnComponentChanging(c, TypeDescriptor.GetProperties(c)["Location"]); 
                            PropertyDescriptor sizeProp = TypeDescriptor.GetProperties(c)["Size"];

                            if (sizeProp != null && sizeProp.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden)) {
                                sizeProp = TypeDescriptor.GetProperties(c)["ClientSize"]; 
                            }
                            cs.OnComponentChanging(c, sizeProp); 
                        } 
                    }
                    else { 
                        cs.OnComponentChanging(GetComponent(), null);
                    }
                }
                catch(CheckoutException coEx){ 
                    if (coEx == CheckoutException.Canceled){
                            // cancel the drag. 
                            return false; 
                    }
                    throw coEx; 
                }
                catch (InvalidOperationException) {
                    // If the doc data for this designer is currently locked, we will get an exception here. In this
                    // case, just eat the exception. If this exception was thrown by someone else, that's fine too. 
                    // No point in bubbling exceptions here anyway.
 
                    return false; 
                }
 
            }
            return components != null && components.Length > 0;
        }
 
        /// 
        ///  
        ///     Asks the handler to set the appropriate cursor 
        /// 
        public abstract void SetCursor(); 

        public virtual void OleDragEnter(DragEventArgs de){
        }
 
        public virtual void OleDragDrop(DragEventArgs de){
        } 
 
        public virtual void OleDragOver(DragEventArgs de){
        } 

        public virtual void OleDragLeave(){
        }
 
        /// 
        ///  
        ///     This class holds bounds information for controls that are being moved. 
        /// 
        private class BoundsInfo { 
            public int X;                           // Original saved X
            public int Y;                           // Original saved Y
            public int Width;                       // Original saved width
            public int Height;                      // Original saved height 
            public int lastRequestedX = -1;
            public int lastRequestedY = -1; 
            public int lastRequestedWidth = -1; 
            public int lastRequestedHeight = -1;
 
            /// 
            /// 
            ///     Creates and initializes a new BoundsInfo object.
            ///  
            public BoundsInfo(Control control) {
 
                // get the size & loc from the props so the designers can adjust them. 
                //
                PropertyDescriptor sizeProp = TypeDescriptor.GetProperties(control)["Size"]; 
                PropertyDescriptor locProp = TypeDescriptor.GetProperties(control)["Location"];

                Size sz;
                Point loc; 

                if (sizeProp != null) { 
                    sz = (Size)sizeProp.GetValue(control); 
                }
                else { 
                    sz = control.Size;
                }

                if (locProp != null) { 
                    loc = (Point)locProp.GetValue(control);
                } 
                else { 
                    loc = control.Location;
                } 

                X = loc.X;
                Y = loc.Y;
                Width = sz.Width; 
                Height = sz.Height;
                lastRequestedX = X; 
                lastRequestedY = Y; 
                lastRequestedWidth = Width;
                lastRequestedHeight = Height; 
            }

            /// 
            ///  
            ///     Overrides object ToString.
            ///  
            public override string ToString() { 
                return "{X=" + X.ToString(CultureInfo.CurrentCulture) + ", Y=" + Y.ToString(CultureInfo.CurrentCulture) + ", Width=" + Width.ToString(CultureInfo.CurrentCulture) + ", Height=" + Height.ToString(CultureInfo.CurrentCulture) + "}";
            } 
        }
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK