SelectionManager.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 / Behavior / SelectionManager.cs / 1 / SelectionManager.cs

                            namespace System.Windows.Forms.Design.Behavior { 
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design; 
    using System.Design;
    using System.Diagnostics; 
    using System.Drawing; 
    using System.Windows.Forms.Design;
 
    /// 
    /// 
    ///     The SelectionBehavior is pushed onto the BehaviorStack in response to a
    ///     positively hit tested SelectionGlyph.  The SelectionBehavior performs 
    ///     two main tasks: 1) forward messages to the related ControlDesigner, and 2)
    ///     calls upon the SelectionManager to push a potention DragBehavior. 
    ///  
    internal sealed class SelectionManager : IDisposable {
 
        private Adorner             selectionAdorner;//used to provide all selection glyphs
        private Adorner             bodyAdorner;//used to track all body glyphs for each control
        private BehaviorService     behaviorService;//ptr back to our BehaviorService
        private IServiceProvider    serviceProvider;//standard service provider 
        private Hashtable           componentToDesigner;//used for quick look up of designers related to comps
        private Control             rootComponent;//root component being designed 
        private ISelectionService   selSvc;//we cache the selection service for perf. 
        private IDesignerHost       designerHost;//we cache the designerhost for perf.
        private bool                needRefresh;    // do we need to refresh? 
        private Rectangle[]         prevSelectionBounds;//used to only repaint the changing part of the selection
        private object              prevPrimarySelection; //used to check if the primary selection changed
        private Rectangle[]         curSelectionBounds;
        private int                 curCompIndex; 
        private DesignerActionUI    designerActionUI = null; // the "container" for all things related to the designer action (smartags) UI
 
        private bool                selectionChanging; //we dont want the OnSelectionChanged to be recursively called. 

        ///  
        /// 
        ///     Constructor.  Here we query for necessary services and cache them for perf. reasons.
        ///     We also hook to Component Added/Removed/Changed notifications so we can keep in sync
        ///     when the designers' components change.  Also, we create our custom Adorner and add 
        ///     it to the BehaviorService.
        ///  
        public SelectionManager(IServiceProvider serviceProvider, BehaviorService behaviorService) { 
            prevSelectionBounds = null;
            prevPrimarySelection = null; 
            this.behaviorService = behaviorService;
            this.serviceProvider = serviceProvider;

            selSvc = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService)); 
            designerHost = (IDesignerHost)serviceProvider.GetService(typeof(IDesignerHost));
 
            if (designerHost == null || selSvc == null) { 
                Debug.Fail("SelectionManager - Host or SelSvc is null, can't continue");
            } 

            //sync the BehaviorService's begindrag event
            behaviorService.BeginDrag += new BehaviorDragDropEventHandler(this.OnBeginDrag);
 
            //sync the BehaviorService's Synchronize event
            behaviorService.Synchronize += new EventHandler(this.OnSynchronize); 
 

            selSvc.SelectionChanged += new EventHandler(this.OnSelectionChanged); 
            rootComponent = (Control)designerHost.RootComponent;

            //create and add both of our adorners,
            //one for selection, one for bodies 
            selectionAdorner = new Adorner();
            bodyAdorner = new Adorner(); 
            behaviorService.Adorners.Add(bodyAdorner); 
            behaviorService.Adorners.Add(selectionAdorner);//adding this will cause the adorner to get setup with a ptr to the beh.svc.
 
            componentToDesigner = new Hashtable();

            IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService));
            if (cs != null) { 
                cs.ComponentAdded += new ComponentEventHandler(this.OnComponentAdded);
                cs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved); 
                cs.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged); 
            }
 
            designerHost.TransactionClosed += new DesignerTransactionCloseEventHandler(OnTransactionClosed);

            // designeraction UI
            DesignerOptionService options = designerHost.GetService(typeof(DesignerOptionService)) as DesignerOptionService; 
            if (options != null) {
                PropertyDescriptor p = options.Options.Properties["UseSmartTags"]; 
                if (p != null && p.PropertyType == typeof(bool) && (bool)p.GetValue(null)) { 
                    designerActionUI = new DesignerActionUI(serviceProvider, selectionAdorner);
                    behaviorService.DesignerActionUI = designerActionUI; 
                }
            }
        }
 
        /// 
        ///  
        ///     Returns the Adorner that contains all the 
        ///     BodyGlyphs for the current selection state.
        ///  
        internal Adorner BodyGlyphAdorner {
            get {
                return bodyAdorner;
            } 
        }
 
        ///  
        ///    There are certain cases like Adding Item to ToolStrips through InSitu Editor, where there is ParentTransaction that has to be cancelled depending upon the user action
        ///    When this parent transaction is cancelled, there may be no reason to REFRESH the selectionManager which actually clears all the glyphs and readds them 
        ///    This REFRESH causes a lot of flicker and can be avoided by setting this property to false. Since this property is checked in the TransactionClosed, the SelectionManager
        ///    won't REFRESH and hence just eat up the refresh thus avoiding unnecessary flicker.
        /// 
        internal bool NeedRefresh { 
            get {
                return needRefresh; 
            } 
            set {
                needRefresh = value; 
            }
        }

        ///  
        /// 
        ///     Returns the Adorner that contains all the 
        ///     BodyGlyphs for the current selection state. 
        /// 
        internal Adorner SelectionGlyphAdorner { 
            get {
                return selectionAdorner;
            }
        } 

        ///  
        ///     This method fist calls the recursive AddControlGlyphs() method. 
        ///     When finished, we add the final glyph(s) to the root comp.
        ///  
        private void AddAllControlGlyphs(Control parent, ArrayList selComps, object primarySelection) {
            foreach (Control control in parent.Controls) {
                AddAllControlGlyphs(control, selComps, primarySelection);
            } 

            GlyphSelectionType selType = GlyphSelectionType.NotSelected; 
            if (selComps.Contains(parent)) { 
                if (parent.Equals(primarySelection)) {
                    selType = GlyphSelectionType.SelectedPrimary; 
                }
                else {
                    selType = GlyphSelectionType.Selected;
                } 
            }
            AddControlGlyphs(parent, selType); 
        } 

        ///  
        ///     Recursive method that goes through and adds all the glyphs
        ///     of every child to our global Adorner.
        /// 
        private void AddControlGlyphs(Control c, GlyphSelectionType selType) { 

            ControlDesigner cd = (ControlDesigner)componentToDesigner[c]; 
            if (cd != null) { 
                ControlBodyGlyph bodyGlyph = cd.GetControlGlyphInternal(selType);
                if (bodyGlyph != null) { 
                    bodyAdorner.Glyphs.Add(bodyGlyph);
                    if (selType == GlyphSelectionType.SelectedPrimary ||
                        selType == GlyphSelectionType.Selected) {
 
                        if (curSelectionBounds[curCompIndex] == Rectangle.Empty) {
                            curSelectionBounds[curCompIndex] = bodyGlyph.Bounds; 
                        } 
                        else  {
                            curSelectionBounds[curCompIndex] = 
                                Rectangle.Union(curSelectionBounds[curCompIndex], bodyGlyph.Bounds);
                        }
                    }
                } 
                GlyphCollection glyphs = cd.GetGlyphs(selType);
                if (glyphs != null) { 
                    selectionAdorner.Glyphs.AddRange(glyphs); 
                    if (selType == GlyphSelectionType.SelectedPrimary ||
                        selType == GlyphSelectionType.Selected) { 
                        foreach (Glyph glyph in glyphs) {
                            curSelectionBounds[curCompIndex] =
                                Rectangle.Union(curSelectionBounds[curCompIndex], glyph.Bounds);
                        } 
                    }
                } 
            } 

            if (selType == GlyphSelectionType.SelectedPrimary || 
                        selType == GlyphSelectionType.Selected) {
                curCompIndex++;
            }
 
        }
 
        ///  
        /// 
        ///     Unhook all of our event notifications, clear our adorner and remove it from the Beh.Svc. 
        /// 

        // We don't need to Dispose rootComponent.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] 
        public void Dispose() {
 
 
            if (designerHost != null) {
                designerHost.TransactionClosed -= new DesignerTransactionCloseEventHandler(OnTransactionClosed); 
                designerHost = null;
            }

            if (serviceProvider != null) { 
                IComponentChangeService cs = (IComponentChangeService)serviceProvider.GetService(typeof(IComponentChangeService));
                if (cs != null) { 
                    cs.ComponentAdded -= new ComponentEventHandler(this.OnComponentAdded); 
                    cs.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
                    cs.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemoved); 
                }
                if (selSvc != null) {
                    selSvc.SelectionChanged -= new EventHandler(this.OnSelectionChanged);
                    selSvc = null; 
                }
                serviceProvider = null; 
            } 

            if (behaviorService != null) { 
                behaviorService.Adorners.Remove(bodyAdorner);
                behaviorService.Adorners.Remove(selectionAdorner);
                behaviorService.BeginDrag -= new BehaviorDragDropEventHandler(this.OnBeginDrag);
                behaviorService.Synchronize -= new EventHandler(this.OnSynchronize); 
                behaviorService = null;
            } 
 
            if (selectionAdorner != null)
            { 
                selectionAdorner.Glyphs.Clear();
                selectionAdorner = null;
            }
 
            if (bodyAdorner != null)
            { 
                bodyAdorner.Glyphs.Clear(); 
                bodyAdorner = null;
            } 

            if (designerActionUI != null) {
                designerActionUI.Dispose();
                designerActionUI = null; 
            }
        } 
 
        /// 
        ///  
        ///     Refreshes all selection Glyphs.
        /// 
        public void Refresh() {
            NeedRefresh = false; 
            OnSelectionChanged(this, null);
        } 
 
        /// 
        ///     When a component is added, we get the designer and add it 
        ///     to our hashtable for quick lookup.
        /// 
        private void OnComponentAdded(object source, ComponentEventArgs ce) {
            IComponent component = ce.Component; 
            IDesigner designer = designerHost.GetDesigner(component);
 
            if (designer is ControlDesigner) { 
                componentToDesigner.Add(component, designer);
            } 
        }

        /// 
        ///     Before a drag, remove all glyphs that are involved 
        ///     in the drag operation and any that don't allow drops.
        ///  
        private void OnBeginDrag(object source, BehaviorDragDropEventArgs e) { 

            ArrayList dragComps = new ArrayList(e.DragComponents); 
            ArrayList glyphsToRemove = new ArrayList();
            foreach (ControlBodyGlyph g in bodyAdorner.Glyphs) {
                if (g.RelatedComponent is Control)
                { 
                    if (dragComps.Contains(g.RelatedComponent) ||
                        !((Control)g.RelatedComponent).AllowDrop) { 
                        glyphsToRemove.Add(g); 
                    }
                } 
            }

            foreach (Glyph g in glyphsToRemove) {
                bodyAdorner.Glyphs.Remove(g); 
            }
        } 
 
        // Called by the DropSourceBehavior when dragging into a new host
        internal void OnBeginDrag(BehaviorDragDropEventArgs e) { 
            OnBeginDrag(null, e);
        }

        ///  
        ///     When a component is changed - we need to refresh the selection.
        ///  
        private void OnComponentChanged(object source, ComponentChangedEventArgs ce) { 
            if (selSvc.GetComponentSelected(ce.Component)) {
                if (!designerHost.InTransaction) { 
                    Refresh();
                }
                else {
                    NeedRefresh = true; 
                }
            } 
 
        }
 
        /// 
        ///     When a component is removed - we remove the key & value from
        ///     our hashtable.
        ///  
        private void OnComponentRemoved(object source, ComponentEventArgs ce) {
            if (componentToDesigner.Contains(ce.Component)) { 
                componentToDesigner.Remove(ce.Component); 
            }
            //remove the associated designeractionpanel 
            if (designerActionUI != null) {
                designerActionUI.RemoveActionGlyph(ce.Component);
            }
        } 
        /// 
        ///  Computes the region representing the difference between the old 
        ///  selection and the new selection. 
        /// 
        private Region DetermineRegionToRefresh(object primarySelection)  { 
            Region toRefresh = new Region(Rectangle.Empty);
            Rectangle[] larger;
            Rectangle[] smaller;
            if (curSelectionBounds.Length >= prevSelectionBounds.Length) { 
                larger = curSelectionBounds;
                smaller = prevSelectionBounds; 
            } 
            else  {
                larger = prevSelectionBounds; 
                smaller = curSelectionBounds;
            }

            // we need to make sure all of the rects in the smaller array are 
            // accounted for.  Any that don't intersect a rect in the larger
            // array need to be included in the region to repaint. 
            bool[] intersected = new bool[smaller.Length]; 
            for (int i = 0; i < smaller.Length; i++)  {
                intersected[i] = false; 
            }

            // determine which rects in the larger array need to be
            // included in the region to invalidate by intersecting 
            // with rects in the smaller array.
            for(int l = 0; l < larger.Length; l++)  { 
                bool largeIntersected = false; 
                Rectangle large = larger[l];
                for (int s = 0; s < smaller.Length; s++) { 
                    if (large.IntersectsWith(smaller[s]))  {
                        Rectangle small = smaller[s];
                        largeIntersected = true;
                        if (large != small) { 
                            toRefresh.Union(large);
                            toRefresh.Union(small); 
                        } 
                        intersected[s] = true;
                        break; 
                    }
                }
                if (!largeIntersected)  {
                    toRefresh.Union(large); 
                }
            } 
 
            // now add any rects from the smaller array
            // that weren't accounted for 
            for (int k = 0; k < intersected.Length; k++) {
                if (!intersected[k]) {
                    toRefresh.Union(smaller[k]);
                } 
            }
 
            using (Graphics g = behaviorService.AdornerWindowGraphics) { 
                //If all that changed was the primary selection, then the refresh region was empty,
                //but we do need to update the 2 controls. VSWhidbey #269806 
                if (toRefresh.IsEmpty(g) && primarySelection != null && !primarySelection.Equals(prevPrimarySelection)) {
                    for (int i = 0; i < curSelectionBounds.Length; i++) {
                        toRefresh.Union(curSelectionBounds[i]);
                    } 
                }
            } 
            return toRefresh; 
        }
 
        /// 
        ///     Event handler for the behaviorService's Synchronize event
        /// 
        private void OnSynchronize(object sender, EventArgs e) { 
            Refresh();
        } 
 
        /// 
        ///     On every selectionchange, we remove all glyphs, get the newly 
        ///     selected components, and re-add all glyphs back to the Adorner.
        /// 
        private void OnSelectionChanged(object sender, EventArgs e) {
             // Note: selectionChanging would guard against a re-entrant code... 
             // Since we dont want to be in messed up state when adding new Glyphs.
             if (!selectionChanging) 
             { 
                selectionChanging = true;
 
                selectionAdorner.Glyphs.Clear();
                bodyAdorner.Glyphs.Clear();

                ArrayList selComps = new ArrayList(selSvc.GetSelectedComponents()); 
                object primarySelection = selSvc.PrimarySelection;
 
                //add all control glyphs to all controls on rootComp 
                curCompIndex = 0;
                curSelectionBounds = new Rectangle[selComps.Count]; 
                AddAllControlGlyphs(rootComponent, selComps, primarySelection);

                if (prevSelectionBounds != null) {
                     Region toUpdate = DetermineRegionToRefresh(primarySelection); 
                     using (Graphics g = behaviorService.AdornerWindowGraphics) {
                        if (!toUpdate.IsEmpty(g)) 
                        { 
                            selectionAdorner.Invalidate(toUpdate);
                        } 
                     }
                }
                else {
                    // There was no previous selection, so just invalidate 
                    // the current selection
                    if (curSelectionBounds.Length > 0) { 
                        Rectangle toUpdate = curSelectionBounds[0]; 
                        for (int i = 1; i < curSelectionBounds.Length; i++)  {
                            toUpdate = Rectangle.Union(toUpdate, curSelectionBounds[i]); 
                        }
 						if (toUpdate != Rectangle.Empty)
						{
							selectionAdorner.Invalidate(toUpdate); 
						}
                    } 
                    else { 
                        selectionAdorner.Invalidate();
                    } 
                }

                prevPrimarySelection = primarySelection;
                if (curSelectionBounds.Length > 0) { 
                    prevSelectionBounds = new Rectangle[curSelectionBounds.Length];
                    Array.Copy(curSelectionBounds, prevSelectionBounds, curSelectionBounds.Length); 
                } 
                else {
                    prevSelectionBounds = null; 
                }
                selectionChanging = false;
             }
        } 

 
        ///  
        ///     When a transaction that involves one of our components closes,
        ///     refresh to reflect any changes. 
        /// 
        private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) {
            if (e.LastTransaction && NeedRefresh) {
                Refresh(); 
            }
        } 
    } 
}

// 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