SelectionService.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Designer / Host / SelectionService.cs / 1 / SelectionService.cs

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

namespace System.ComponentModel.Design { 
 
    using System;
    using System.Collections; 
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Diagnostics; 
    using System.Diagnostics.CodeAnalysis;
    using System.Windows.Forms; 
    using System.Windows.Forms.Design; 
    using System.Drawing;
 
    /// 
    ///     The selection service handles selection within a form.  There is one selection
    ///     service for each designer host.
    /// 
    ///     A selection consists of an array of objects.  One objects is designated
    ///     the "primary" selection. 
    ///  
    internal sealed class SelectionService : ISelectionService, IDisposable {
 
        // These are the selection types we use for context help.
        //
        private static readonly string[] SelectionKeywords = new string[] {"None", "Single", "Multiple"};
 
        // State flags for the selection service
        // 
        private static readonly int StateTransaction        = BitVector32.CreateMask();                 // Designer is in a transaction 
        private static readonly int StateTransactionChange  = BitVector32.CreateMask(StateTransaction); // Component change occurred while in a transaction
 
        // ISelectionService events
        //
        private static readonly object EventSelectionChanging   = new object();
        private static readonly object EventSelectionChanged    = new object(); 

        // Member variables 
        // 
        private IServiceProvider        _provider;          // The service provider
        private BitVector32             _state;             // state of the selection service 
        private EventHandlerList        _events;            // the events we raise
        private ArrayList               _selection;         // list of selected objects
        private string[]                _contextAttributes; // help context information we have pushed to the help service.
        private short                   _contextKeyword;    // the offset into the selection keywords for the current selection. 
        private StatusCommandUI         _statusCommandUI;   // UI for setting the StatusBar Information..
 
        ///  
        ///     Creates a new selection manager object.  The selection manager manages all
        ///     selection of all designers under the current form file. 
        /// 
        internal SelectionService(IServiceProvider provider) : base() {
            _provider = provider;
            _state = new BitVector32(); 
            _events = new EventHandlerList();
            _statusCommandUI = new StatusCommandUI(provider); 
        } 

 
        /// 
        ///     Adds the given selection to our selection list.
        /// 
        internal void AddSelection(object sel) { 
            if (_selection == null) {
                _selection = new ArrayList(); 
 
                // Now is the opportune time to hook up all of our events
                // 
                IComponentChangeService cs = GetService(typeof(IComponentChangeService)) as IComponentChangeService;
                if (cs != null) {
                    cs.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemove);
                } 

                IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; 
                if (host != null) { 
                    host.TransactionOpened += new EventHandler(this.OnTransactionOpened);
                    host.TransactionClosed += new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); 
                    if (host.InTransaction) {
                        OnTransactionOpened(host, EventArgs.Empty);
                    }
                } 
            }
 
            if (!_selection.Contains(sel)) { 
                _selection.Add(sel);
            } 
        }

        /// 
        ///     Called when our visiblity or batch mode changes.  Flushes 
        ///     any pending notifications or updates if possible.
        ///  
        private void FlushSelectionChanges() { 
            if (!_state[StateTransaction] && _state[StateTransactionChange]) {
                _state[StateTransactionChange] = false; 
                OnSelectionChanged();
            }
        }
 
        /// 
        ///     Helper function to retrieve services. 
        ///  
        private object GetService(Type serviceType) {
            if (_provider != null) { 
                return _provider.GetService(serviceType);
            }
            return null;
        } 

        ///  
        ///     called by the formcore when someone has removed a component.  This will 
        ///     remove any selection on the component without disturbing the rest of
        ///     the selection 
        /// 
        private void OnComponentRemove(object sender, ComponentEventArgs ce) {
            if (_selection != null && _selection.Contains(ce.Component)) {
                RemoveSelection(ce.Component); 
                OnSelectionChanged();
            } 
        } 

        ///  
        ///     called anytime the selection has changed.  We update our UI for the selection, and
        ///     then we fire a juicy change event.
        /// 
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] 
        private void OnSelectionChanged() {
            if (_state[StateTransaction]) { 
                _state[StateTransactionChange] = true; 
            }
            else { 
                EventHandler eh = _events[EventSelectionChanging] as EventHandler;
                if (eh != null) {
                    eh(this, EventArgs.Empty);
                } 

                UpdateHelpKeyword(true); 
 
                eh = _events[EventSelectionChanged] as EventHandler;
                if (eh != null) { 
                    try {
                        eh(this, EventArgs.Empty);
                    }
                    catch { 
                        // VSWhidbey 491164
                        // eat exceptions - required for compatibility with Everett. 
                    } 
                }
            } 
        }

        /// 
        ///     Called by the designer host when it is entering or leaving a batch 
        ///     operation.  Here we queue up selection notification and we turn off
        ///     our UI. 
        ///  
        private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) {
            if (e.LastTransaction) { 
                _state[StateTransaction] = false;
                FlushSelectionChanges();
            }
        } 

        ///  
        ///     Called by the designer host when it is entering or leaving a batch 
        ///     operation.  Here we queue up selection notification and we turn off
        ///     our UI. 
        /// 
        private void OnTransactionOpened(object sender, EventArgs e) {
            _state[StateTransaction] = true;
        } 

        ///  
        ///     Removes the given selection from the selection list. 
        /// 
        internal void RemoveSelection(object sel) { 
            if (_selection != null) {
                _selection.Remove(sel);
            }
        } 

        private void ApplicationIdle(object source, EventArgs args) { 
            UpdateHelpKeyword(false); 
            Application.Idle -= new EventHandler(this.ApplicationIdle);
        } 

        /// 
        ///     Pushes the help context into the help service for the
        ///     current set of selected objects. 
        /// 
        private void UpdateHelpKeyword(bool tryLater) { 
            IHelpService helpService = GetService(typeof(IHelpService)) as IHelpService; 

            if (helpService == null) { 
                if(tryLater) {
                    // we don't have an help service YET, we need to wait for it...
                    // hook up to the application.idle event
                    // yes this is UGLY but we don't have a choice, 
                    // vs is always returning a UserContext, so even if we manage to instanciate
                    // the HelpService beforehand and class pushcontext on it (trying to stack up help 
                    // context in the helpservice to be flushed when we get the documentactivation event 
                    // we just don't know if that's going to work or not... so we just wait...) :(((
                    Application.Idle += new EventHandler(this.ApplicationIdle); 
                }
                return;
            }
 
            // If there is an old set of context attributes, remove them.
            // 
            if (_contextAttributes != null) { 

                foreach (string s in _contextAttributes) { 
                    helpService.RemoveContextAttribute("Keyword", s);
                }
                _contextAttributes = null;
            } 

            // Clear the selection keyword 
            // 
            helpService.RemoveContextAttribute("Selection", SelectionKeywords[_contextKeyword]);
 
            // Get a list of unique class names.
            //
            Debug.Assert(_selection != null, "Should be impossible to update the help context before configuring the selection hash");
 
            bool baseComponentSelected = false;
 
            if (_selection.Count == 0) { 
                baseComponentSelected = true;
            } 
            else if (_selection.Count == 1) {
                IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost;
                if (host != null && _selection.Contains(host.RootComponent)) {
                    baseComponentSelected = true; 
                }
            } 
 
             _contextAttributes = new string[_selection.Count];
 
            for (int i = 0; i < _selection.Count; i++) {
                object s = _selection[i];

                string helpContext = TypeDescriptor.GetClassName(s); 

                HelpKeywordAttribute contextAttr = (HelpKeywordAttribute)TypeDescriptor.GetAttributes(s)[typeof(HelpKeywordAttribute)]; 
 
                if (contextAttr != null && !contextAttr.IsDefaultAttribute()) {
                    helpContext = contextAttr.HelpKeyword; 
                }

                _contextAttributes[i] = helpContext;
            } 

            // And push them into the help context as keywords. 
            // 
            HelpKeywordType selectionType = baseComponentSelected ? HelpKeywordType.GeneralKeyword : HelpKeywordType.F1Keyword;
            foreach(string helpContext in _contextAttributes) { 
                helpService.AddContextAttribute("Keyword", helpContext, selectionType);
            }

            // Now add the appropriate selection keyword.  Note that we do not count 
            // the base component as being selected if it is the only thing selected.
            // 
            int count = _selection.Count; 
            if (count == 1 && baseComponentSelected) {
                count--; 
            }
            _contextKeyword = (short)Math.Min(count, SelectionKeywords.Length - 1);
            helpService.AddContextAttribute("Selection", SelectionKeywords[_contextKeyword], HelpKeywordType.FilterKeyword);
        } 

        ///  
        ///     Disposes the entire selection service. 
        /// 
        [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] 
        void IDisposable.Dispose() {

            if (_selection != null) {
                IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost; 
                if (host != null) {
                    host.TransactionOpened -= new EventHandler(this.OnTransactionOpened); 
                    host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); 
                    if (host.InTransaction) {
                        OnTransactionClosed(host, new DesignerTransactionCloseEventArgs(true, true)); 
                    }
                }

                IComponentChangeService cs = GetService(typeof(IComponentChangeService)) as IComponentChangeService; 
                if (cs != null) {
                    cs.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemove); 
                } 

                _selection.Clear(); 
            }
            _statusCommandUI = null;
            _provider = null;
        } 

        ///  
        ///     Retrieves the object that is currently the primary selection.  The 
        ///     primary selection has a slightly different UI look and is used as a
        ///     "key" when an operation is to be done on multiple components. 
        /// 
        object ISelectionService.PrimarySelection {
            get {
                if (_selection != null && _selection.Count > 0) { 
                    return _selection[0];
                } 
 
                return null;
            } 
        }

        /// 
        ///     Retrieves the count of selected objects. 
        /// 
        int ISelectionService.SelectionCount { 
            get { 
                if (_selection != null) {
                    return _selection.Count; 
                }
                return 0;
            }
        } 

        ///  
        ///     
        ///       Adds a  event handler to the selection service.
        ///     
        /// 
        event EventHandler ISelectionService.SelectionChanged {
            add {
                _events.AddHandler(EventSelectionChanged, value); 
            }
            remove { 
                _events.RemoveHandler(EventSelectionChanged, value); 
            }
        } 

        /// 
        ///    
        ///       Occurs whenever the user changes the current list of 
        ///       selected components in the designer.  This event is raised before the actual
        ///       selection changes. 
        ///     
        /// 
        event EventHandler ISelectionService.SelectionChanging { 
            add {
                _events.AddHandler(EventSelectionChanging, value);
            }
            remove { 
                _events.RemoveHandler(EventSelectionChanging, value);
            } 
        } 

        ///  
        ///     Determines if the component is currently selected.  This is faster
        ///     than getting the entire list of selelected components.
        /// 
        bool ISelectionService.GetComponentSelected(object component) { 
            if (component == null) {
                throw new ArgumentNullException("component"); 
            } 

            return (_selection != null && _selection.Contains(component)); 
        }

        /// 
        ///     Retrieves an array of components that are currently part of the 
        ///     user's selection.
        ///  
        ICollection ISelectionService.GetSelectedComponents() { 
            if (_selection != null) {
 
                // Must clone here.  Otherwise the values collection
                // is a live collection and will change when the
                // selection changes.  GetSelectedComponents should
                // be a snapshot. 
                //
                object[] selectedValues = new object[_selection.Count]; 
                _selection.CopyTo(selectedValues, 0); 
                return selectedValues;
            } 

            return new object[0];
        }
 
        /// 
        ///     Changes the user's current set of selected components to the components 
        ///     in the given array.  If the array is null or doesn't contain any 
        ///     components, this will select the top level component in the designer.
        ///  
        void ISelectionService.SetSelectedComponents(ICollection components) {
            ((ISelectionService)this).SetSelectedComponents(components, SelectionTypes.Auto);
        }
 
        /// 
        ///     Changes the user's current set of selected components to the components 
        ///     in the given array.  If the array is null or doesn't contain any 
        ///     components, this will select the top level component in the designer.
        ///  
        void ISelectionService.SetSelectedComponents(ICollection components, SelectionTypes selectionType) {

            bool fToggle  = (selectionType & SelectionTypes.Toggle) == SelectionTypes.Toggle;
            bool fPrimary   = (selectionType & SelectionTypes.Primary) == SelectionTypes.Primary; 
            bool fAdd     = (selectionType & SelectionTypes.Add) == SelectionTypes.Add;
            bool fRemove  = (selectionType & SelectionTypes.Remove) == SelectionTypes.Remove; 
            bool fReplace = (selectionType & SelectionTypes.Replace) == SelectionTypes.Replace; 
            bool fAuto    = !(fToggle | fAdd | fRemove | fReplace);
 
            // We always want to allow NULL arrays coming in.
            //
            if (components == null) {
                components = new object[0]; 
            }
 
            // If toggle, replace, remove or add are not specifically specified, infer them from 
            // the state of the modifer keys.  This creates the "Auto" selection type
            // for us by default. 
            //
            if (fAuto) {
                fToggle = (Control.ModifierKeys & (Keys.Control | Keys.Shift)) > 0;
                fAdd    |= System.Windows.Forms.Control.ModifierKeys == System.Windows.Forms.Keys.Shift; 

                // If we are in auto mode, and if we are toggling or adding new controls, then 
                // cancel out the primary flag. 
                //
                if (fToggle || fAdd) { 
                    fPrimary = false;
                }
            }
 
            // This flag is true if we changed selection and should therefore raise
            // a selection change event. 
            // 
            bool fChanged = false;
 
            // Handle the click case
            //
            object requestedPrimary = null;
            int primaryIndex; 

            if (fPrimary && 1 == components.Count) { 
                foreach(object o in components) { 
                    requestedPrimary = o;
                    if (o == null) { 
                        throw new ArgumentNullException("components");
                    }
                    break;
                } 
            }
 
            if (requestedPrimary != null && _selection != null && (primaryIndex = _selection.IndexOf(requestedPrimary)) != -1) { 
                if (primaryIndex != 0) {
                    object tmp = _selection[0]; 
                    _selection[0] = _selection[primaryIndex];
                    _selection[primaryIndex] = tmp;
                    fChanged = true;
                } 
            }
            else { 
                // If we are replacing the selection, only remove the ones that are not in our new list. 
                // We also handle the special case here of having a singular component selected that's
                // already selected.  In this case we just move it to the primary selection. 
                //
                if (!fToggle && !fAdd && !fRemove) {
                    if (_selection != null) {
 
                        object[] selections = new object[_selection.Count];
                        _selection.CopyTo(selections, 0); 
 
                        // Yucky and N^2, but even with several hundred components this should be fairly fast
                        // 
                        foreach(object item in selections) {
                            bool remove = true;

                            foreach(object comp in components) { 
                                if (comp == null) throw new ArgumentNullException("components");
                                if (object.ReferenceEquals(comp, item)) { 
                                    remove = false; 
                                    break;
                                } 
                            }

                            if (remove) {
                                RemoveSelection(item); 
                                fChanged = true;
                            } 
                        } 
                    }
                } 

                // Now select / toggle the components.
                //
                foreach(object comp in components) { 
                    if (comp == null) throw new ArgumentNullException("components");
 
                    if (_selection != null && _selection.Contains(comp)) { 
                        if (fToggle || fRemove) {
                            RemoveSelection(comp); 
                            fChanged = true;
                        }
                    }
                    else if (!fRemove) { 
                        AddSelection(comp);
                        fChanged = true; 
                    } 
                }
            } 

            // Notify that our selection has changed
            //
            if (fChanged) { 
                //Set the SelectionInformation
                if (_selection.Count > 0) 
                { 
                    _statusCommandUI.SetStatusInformation(_selection[0] as Component);
                } 
                else {
                    _statusCommandUI.SetStatusInformation(Rectangle.Empty);
                }
                OnSelectionChanged(); 
            }
        } 
    } 
}
 

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