UndoEngine.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 / Host / UndoEngine.cs / 1 / UndoEngine.cs

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

namespace System.ComponentModel.Design { 
 
    using System;
    using System.Collections; 
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.ComponentModel.Design.Serialization; 
    using System.Design;
    using System.Diagnostics; 
    using System.IO; 
    using System.Reflection;
    using System.Runtime.Serialization; 
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Globalization;

    ///  
    /// 
    ///     The UndoEngine is a class that can be instantiated to support automatic 
    ///     undo.  Normally, to support undo, a developer must create individual 
    ///     undo units that consist of an undo action and a redo action.  This is
    ///     fragile because each and every action the user performs must be wrapped 
    ///     in an undo unit.  Worse, if a user action is not wrapped in an undo unit
    ///     its absence on the undo stack will break undo because each individual
    ///     unit always assumes that the previous unit's state is maintained.  The
    ///     UndoEngine, on the other hand, listens to change events and can create 
    ///     undo and redo actions automatically.  All that is necessary to implement
    ///     undo is to add these actions to an undo/redo stack and instantiate this 
    ///     class. 
    /// 
    public abstract class UndoEngine : IDisposable { 

        private static TraceSwitch traceUndo = new TraceSwitch("UndoEngine", "Trace UndoRedo");

        private IServiceProvider                _provider; 
        private Stack                           _unitStack;        // the stack of active (non-committed) units.
        private UndoUnit                        _executingUnit;    // the unit currently executing an undo. 
        private IDesignerHost                   _host; 
        private ComponentSerializationService   _serializationService;
        private EventHandler                    _undoingEvent; 
        private EventHandler                    _undoneEvent;
        private IComponentChangeService         _componentChangeService;
        private Dictionary> _refToRemovedComponent;
        private bool _enabled; 

        ///  
        ///  
        ///     Creates a new UndoEngine.  UndoEngine requires a service
        ///     provider for access to various services.  The following 
        ///     services must be available or else UndoEngine will
        ///     throw an exception:
        ///
        ///     IDesignerHost 
        ///     IComponentChangeService
        ///     IDesignerSerializationService 
        /// 
        /// 
        protected UndoEngine(IServiceProvider provider) { 
            if (provider == null) {
                throw new ArgumentNullException("provider");
            }
 
            _provider = provider;
            _unitStack = new Stack(); 
            _enabled = true; 

            // Validate that all required services are available.  Because undo 
            // is a passive activity we must know up front if it is going to work
            // or not.
            //
            _host = GetRequiredService(typeof(IDesignerHost)) as IDesignerHost; 
            _componentChangeService = GetRequiredService(typeof(IComponentChangeService)) as IComponentChangeService;
            _serializationService = GetRequiredService(typeof(ComponentSerializationService)) as ComponentSerializationService; 
 
            // We need to listen to a slew of events to determine undo state.
            // 
            _host.TransactionOpening += new EventHandler(this.OnTransactionOpening);
            _host.TransactionClosed += new DesignerTransactionCloseEventHandler(this.OnTransactionClosed);
            _componentChangeService.ComponentAdding += new ComponentEventHandler(this.OnComponentAdding);
            _componentChangeService.ComponentChanging += new ComponentChangingEventHandler(this.OnComponentChanging); 
            _componentChangeService.ComponentRemoving += new ComponentEventHandler(this.OnComponentRemoving);
            _componentChangeService.ComponentAdded += new ComponentEventHandler(this.OnComponentAdded); 
            _componentChangeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged); 
            _componentChangeService.ComponentRemoved += new ComponentEventHandler(this.OnComponentRemoved);
            _componentChangeService.ComponentRename += new ComponentRenameEventHandler(this.OnComponentRename); 
        }

        /// 
        ///     Retrieves the current unit from the stack. 
        /// 
        private UndoUnit CurrentUnit { 
            get { 
                if (_unitStack.Count > 0) {
                    return (UndoUnit)_unitStack.Peek(); 
                }
                return null;
            }
        } 

        ///  
        ///  
        ///     This property indicates if an undo is in progress.
        ///  
        public bool UndoInProgress {
            get {
                return _executingUnit != null;
            } 
        }
 
        ///  
        /// 
        ///    This property returns true if the Undo engine is currently enabled.  When enabled, 
        ///    the undo engine tracks changes made to the designer.  When disabled, changes are
        ///    ignored.  If the UndoEngine is set to disabled while in the middle of processing
        ///    change notifications from the designer, the undo engine will only ignore additional
        ///    changes.  That is, it will finish recording the changes that are in process and only 
        ///    ignore additional changes.
        /// 
        ///    Caution should be used when disabling undo.  If undo is disabled it is easy to 
        ///    make a change that would cause other undo actions to become invalid.  For
        ///    example, if myButton.Text was changed, and then myButton was renamed while 
        ///    undo was disabled, attempting to undo the text change would fail because there
        ///    is no longer a control called myButton.  Generally, you should never make changes
        ///    to components with undo disabled unless you are certain to put the components
        ///    back the way they were before undo was disabled.  An example of this would be 
        ///    to replace one instance of "Button" with another, say "SuperButton", fixing up
        ///    all the property values as you go.  The result is a new component, but because 
        ///    it has the same component name and property values, undo state will still be 
        ///    consistent.
        ///  
        public bool Enabled {
            get {
                return _enabled;
            } 
            set {
                _enabled = value; 
            } 
        }
 
        /// 
        /// 
        ///     This event is raised immediately before an undo action is performed.
        ///  
        public event EventHandler Undoing {
            add { 
                _undoingEvent += value; 
            }
            remove { 
                _undoingEvent -= value;
            }
        }
 
        /// 
        ///  
        ///     This event is raised immediately after an undo action is performed. It 
        ///     will always be raised even if an exception is thrown.
        ///  
        public event EventHandler Undone {
            add {
                _undoneEvent += value;
            } 
            remove {
                _undoneEvent -= value; 
            } 
        }
 
        /// 
        /// 
        ///     Adds the given undo unit into the undo stack.  UndoEngine
        ///     does not maintain its own undo stack, so you must implement 
        ///     this method yourself.
        ///  
        protected abstract void AddUndoUnit(UndoUnit unit); 

        ///  
        ///     This method will check to see if the current undo unit needs to be
        ///     popped from the stack.  If it does, it will pop it and add it
        ///     to the undo stack.  There must be at least one unit on the stack
        ///     to call this method. 
        ///
        ///     When calling CheckPopUnit you must supply a reason for the call. 
        ///     There are three reasons: 
        ///
        ///     Normal 
        ///     ======
        ///     Call with Normal if you are not calling in response to a closing
        ///     transaction.  For normal pop reasons, the unit will be popped
        ///     if there is no current transaction.  If the unit is not empty it 
        ///     will be added to the undo engine.  If there is a transaction in
        ///     progress, this method will do nothing. 
        /// 
        ///     TransactionCommit
        ///     ================= 
        ///     Call with TransactionCommit if you are calling in response to a
        ///     transaction closing, and if that transaction is marked as being
        ///     committed.  CheckPopUnit will pop the unit off of the stack and
        ///     add it to the undo engine if it is not empty. 
        ///
        ///     TransactionCancel 
        ///     ================= 
        ///     Call with TransactionCancel if you are calling in response to a
        ///     transaction closing, and if that transaction is marked as being 
        ///     cancelled.  CheckPopUnit will pop the unit off of the stack.  If
        ///     the unit is not empty Undo will be called on the unit to roll back
        ///     the transaction work.  The unit will never be added to the undo
        ///     engine. 
        /// 
        private void CheckPopUnit(PopUnitReason reason) { 
 
            // The logic in here is subtle.  This code handles both committing
            // and cancelling of nested transactions.  Here's a summary of how 
            // it works:
            //
            // 1.  Each time a transaction is opened, a new unit is pushed onto
            //     the unit stack. 
            //
            // 2.  When a change occurs, the change event checks to see if there 
            //     is a currently executing unit.  It also checks to see if the 
            //     current unit stack is empty.  If there is no executing unit
            //     (meaning that nothing is performing an undo right now), and 
            //     if the unit stack is empty, the change event will create a
            //     new undo unit and push it on the stack.
            //
            // 3.  The change event always runs through all undo units in the 
            //     undo stack and calls the corresponding change method.  In
            //     the normal case of a single transaction or no transaction, 
            //     this will operate on just one unit.  In the case of nested 
            //     transactions there are two possibilities:
            // 
            //          a)  We are adding undo information to a nested transaction.
            //              We want to add the undo information to all levels
            //              of nested transactions.  Why?  Because as a nested
            //              transaction is closed, it is either committed or 
            //              cancelled.  If committed, and if the transaction
            //              is not the top-most transaction, the transaction 
            //              is actually just thrown away because its data is 
            //              redundantly stored in the next transaction on the
            //              stack. 
            //
            //          b)  We are adding undo information to a nested transaction,
            //              but that undo information is being created because
            //              an undo unit is being "undone".  Remember that for 
            //              nested transactions each undo unit higher on the stack
            //              has all the data that the lower units have.  When 
            //              a lower unit is undone, it is popped from the stack 
            //              and all of the changes it makes are recorded on the
            //              higher level units.  This combines the "do" and "undo" 
            //              data into the higher level unit, which in effect
            //              subtracts the undone data from the higher level unit.
            //
            // 4.  When a unit is undone it stores itself in a member variable 
            //     called _executingUnit.  All change events examine this variable
            //     and if it is set they do not create a new unit in response to 
            //     a change.  Instead, they just run through all the existing 
            //     units.  This builds the undo history for a transaction that
            //     is being rolled back. 
            //
            if (reason != PopUnitReason.Normal || !_host.InTransaction) {
                Trace("Popping unit {0}.  Reason: {1}", _unitStack.Peek(), reason);
                UndoUnit unit = (UndoUnit)_unitStack.Pop(); 

                if (!unit.IsEmpty) { 
 
                    unit.Close();
 
                    if (reason == PopUnitReason.TransactionCancel) {
                        unit.Undo();
                        if (_unitStack.Count == 0) {
                            DiscardUndoUnit(unit); 
                        }
                    } 
                    else { 
                        if (_unitStack.Count == 0) {
                            AddUndoUnit(unit); 
                        }
                    }
                }
                else { 
                    if (_unitStack.Count == 0) {
                        DiscardUndoUnit(unit); 
                    } 
                }
            } 
        }

        /// 
        ///  
        ///     This virtual method creates a new instance of an
        ///     UndoUnit class.  The default implementation just returns 
        ///     a new instance of UndoUnit.  Those providing their 
        ///     own UndoEngine can derive from UndoUnit to customize
        ///     the actions it performs.  This is also a handy way 
        ///     to connect UndoEngine into an existing undo stack.
        ///
        ///     If the primary parameter is set to true, the undo unit will
        ///     eventually be passed to either the AddUndoUnit or DiscardUndoUnit 
        ///     methods.  If the primary parameter is false, the undo unit is
        ///     part of a nested transaction and will never be passed to 
        ///     AddUndoUnit or DiscardUndoUnit; only the encompasing unit 
        ///     will be passed, because the undo engine will either include
        ///     or exclude the contents of the nested unit when it is closed. 
        /// 
        protected virtual UndoUnit CreateUndoUnit(string name, bool primary) {
            return new UndoUnit(this, name);
        } 

        internal IComponentChangeService ComponentChangeService { 
            get { 
                return this._componentChangeService;
            } 
        }

        /// 
        ///  
        ///     This method is called instead of AddUndoUnit for undo units that
        ///     have been canceled.  For undo systems that just treat undo as a 
        ///     simple stack of undo units, typically you do not need to override 
        ///     this method.  This method does give you a chance to perform any
        ///     clean-up for a unit 
        /// 
        protected virtual void DiscardUndoUnit(UndoUnit unit) {
        }
 
        /// 
        ///  
        ///     Public dispose method. 
        /// 
        public void Dispose() { 
            Dispose(true);
        }

        ///  
        /// 
        ///     Protected dispose implementation. 
        ///  
        protected virtual void Dispose(bool disposing) {
            if (disposing) { 

                Trace("Disposing undo engine");

                if (_host != null) { 
                    _host.TransactionOpening -= new EventHandler(this.OnTransactionOpening);
                    _host.TransactionClosed -= new DesignerTransactionCloseEventHandler(this.OnTransactionClosed); 
                } 

                if (_componentChangeService != null) { 
                    _componentChangeService.ComponentAdding -= new ComponentEventHandler(this.OnComponentAdding);
                    _componentChangeService.ComponentChanging -= new ComponentChangingEventHandler(this.OnComponentChanging);
                    _componentChangeService.ComponentRemoving -= new ComponentEventHandler(this.OnComponentRemoving);
                    _componentChangeService.ComponentAdded -= new ComponentEventHandler(this.OnComponentAdded); 
                    _componentChangeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
                    _componentChangeService.ComponentRemoved -= new ComponentEventHandler(this.OnComponentRemoved); 
                    _componentChangeService.ComponentRename -= new ComponentRenameEventHandler(this.OnComponentRename); 
                }
 
                _provider = null;
            }
        }
 
        /// 
        ///     Helper function to retrieve the name of an object. 
        ///  
        internal string GetName(object obj, bool generateNew) {
 
            string componentName = null;

            if (obj != null) {
                IReferenceService rs = GetService(typeof(IReferenceService)) as IReferenceService; 
                if (rs != null) {
                    componentName = rs.GetName(obj); 
                } 
                else {
                    IComponent comp = obj as IComponent; 
                    if (comp != null) {
                        ISite site = comp.Site;
                        if (site != null) {
                            componentName = site.Name; 
                        }
                    } 
                } 
            }
 
            if (componentName == null && generateNew) {
                if (obj == null) {
                    componentName = "(null)";
                } 
                else {
                    componentName = obj.GetType().Name; 
                } 
            }
 
            return componentName;
        }

        ///  
        /// 
        ///     Similar to GetService, but this will throw a NotSupportedException if the service 
        ///     is not present. 
        /// 
        protected object GetRequiredService(Type serviceType) { 
            object service = GetService(serviceType);
            if (service == null) {
                Exception ex = new InvalidOperationException(SR.GetString(SR.UndoEngineMissingService, serviceType.Name));
                ex.HelpLink = SR.UndoEngineMissingService; 
                throw ex;
            } 
            return service; 
        }
 
        /// 
        /// 
        ///     This just calls through to the service provider passed into
        ///     the constructor. 
        /// 
        protected object GetService(Type serviceType) { 
 
            if (serviceType == null) {
                throw new ArgumentNullException("serviceType"); 
            }

            if (_provider != null) {
                return _provider.GetService(serviceType); 
            }
 
            return null; 
        }
 
        /// 
        ///     Handles the component added event.
        /// 
        private void OnComponentAdded(object sender, ComponentEventArgs e) { 

            foreach(UndoUnit unit in _unitStack) { 
                unit.ComponentAdded(e); 
            }
 
            if (CurrentUnit != null) {
                CheckPopUnit(PopUnitReason.Normal);
            }
        } 

        ///  
        ///     Handles the component adding event. 
        /// 
        private void OnComponentAdding(object sender, ComponentEventArgs e) { 

            // Open a new unit unless there is already one open or we are
            // currently executing a unit. If we need to create a unit, we
            // will have to fabricate a good name. 
            //
            if (_enabled && _executingUnit == null && _unitStack.Count == 0) { 
                string name; 

                if (e.Component != null) { 
                    name = SR.GetString(SR.UndoEngineComponentAdd1,
                                        GetName(e.Component, true));
                }
                else { 
                    name = SR.GetString(SR.UndoEngineComponentAdd0);
                } 
 
                _unitStack.Push(CreateUndoUnit(name, true));
            } 

            // Now walk all the units and notify them.  We don't
            // care which order the units are notified.
            // 
            foreach (UndoUnit unit in _unitStack) {
                unit.ComponentAdding(e); 
            } 
        }
 
        /// 
        ///     Handles the component changed event.
        /// 
        private void OnComponentChanged(object sender, ComponentChangedEventArgs e) { 

            foreach(UndoUnit unit in _unitStack) { 
                unit.ComponentChanged(e); 
            }
 
            if (CurrentUnit != null) {
                CheckPopUnit(PopUnitReason.Normal);
            }
        } 

        ///  
        ///     Handles the component changing event. 
        /// 
        private void OnComponentChanging(object sender, ComponentChangingEventArgs e) { 

            // Open a new unit unless there is already one open or we are
            // currently executing a unit. If we need to create a unit, we
            // will have to fabricate a good name. 
            //
            if (_enabled && _executingUnit == null && _unitStack.Count == 0) { 
                string name; 

                if (e.Member != null && e.Component != null) { 
                    name = SR.GetString(SR.UndoEngineComponentChange2,
                                        GetName(e.Component, true), e.Member.Name);
                }
                else if (e.Component != null) { 
                    name = SR.GetString(SR.UndoEngineComponentChange1,
                                        GetName(e.Component, true)); 
                } 
                else {
                    name = SR.GetString(SR.UndoEngineComponentChange0); 
                }

                _unitStack.Push(CreateUndoUnit(name, true));
            } 

            // Now walk all the units and notify them.  We don't 
            // care which order the units are notified. 
            //
            foreach(UndoUnit unit in _unitStack) { 
                unit.ComponentChanging(e);
            }
        }
 
        /// 
        ///     Handles the component removed event. 
        ///  
        private void OnComponentRemoved(object sender, ComponentEventArgs e) {
 
            foreach(UndoUnit unit in _unitStack) {
                unit.ComponentRemoved(e);
            }
 
            if (CurrentUnit != null) {
                CheckPopUnit(PopUnitReason.Normal); 
            } 

            // Now we need to raise ComponentChanged events for 
            // every component that had a reference to this removed component
            List propsToUpdate = null;
            if (_refToRemovedComponent != null
                && _refToRemovedComponent.TryGetValue(e.Component, out propsToUpdate) 
                && propsToUpdate != null
                && _componentChangeService != null) { 
 
                foreach (ReferencingComponent ro in propsToUpdate) {
                    _componentChangeService.OnComponentChanged(ro.component, ro.member, null, null); 
                }

                _refToRemovedComponent.Remove(e.Component);
            } 
        }
 
        ///  
        ///     Handles the component removing event.
        ///  
        private void OnComponentRemoving(object sender, ComponentEventArgs e) {

            // Open a new unit unless there is already one open or we are
            // currently executing a unit. If we need to create a unit, we 
            // will have to fabricate a good name.
            // 
            if (_enabled && _executingUnit == null && _unitStack.Count == 0) { 
                string name;
 
                if (e.Component != null) {
                    name = SR.GetString(SR.UndoEngineComponentRemove1,
                                        GetName(e.Component, true));
                } 
                else {
                    name = SR.GetString(SR.UndoEngineComponentRemove0); 
                } 

                _unitStack.Push(CreateUndoUnit(name, true)); 
            }


            // VSWhidbey #312230 
            // We need to keep track of all references in the container to the deleted component so
            // that those references can be fixed up if an undo of this "remove" occurs. 
            // 
            if (_enabled && _host != null && _host.Container != null && _componentChangeService != null) {
 
                List propsToUpdate = null;
                foreach (IComponent comp in _host.Container.Components) {
                    if (comp == e.Component) {
                        continue; 
                    }
                    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(comp); 
                    foreach (PropertyDescriptor prop in props) { 
                        if (prop.PropertyType.IsAssignableFrom(e.Component.GetType()) &&
                            !prop.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden) && 
                            !prop.IsReadOnly)  {
                            object obj = null;
                            try {
                                obj = prop.GetValue(comp); 
                            }
                            catch (TargetInvocationException) { 
                                continue; 
                            }
 
                            if (obj != null  && object.ReferenceEquals(obj, e.Component)) {
                                // found one!
                                if (propsToUpdate == null) {
                                    propsToUpdate = new List(); 

                                    if (_refToRemovedComponent == null) { 
                                        _refToRemovedComponent = new Dictionary>(); 
                                    }
                                    _refToRemovedComponent[e.Component] = propsToUpdate; 
                                }
                                _componentChangeService.OnComponentChanging(comp, prop);
                                propsToUpdate.Add(new ReferencingComponent(comp, prop));
                            } 
                        }
                    } 
                } 
            }
 
            // Now walk all the units and notify them.  We don't
            // care which order the units are notified.  By notifying
            // all transactions we automatically support the cancelling
            // of nested transactions. 
            //
            foreach(UndoUnit unit in _unitStack) { 
                unit.ComponentRemoving(e); 
            }
 

        }

        ///  
        ///     Handles the component rename event.
        ///  
        private void OnComponentRename(object sender, ComponentRenameEventArgs e) { 

            // Open a new unit unless there is already one open or we are 
            // currently executing a unit. If we need to create a unit, we
            // will have to fabricate a good name.
            //
            if (_enabled && _executingUnit == null && _unitStack.Count == 0) { 
                string name = SR.GetString(SR.UndoEngineComponentRename, e.OldName,
                                            e.NewName); 
 
                _unitStack.Push(CreateUndoUnit(name, true));
            } 

            // Now walk all the units and notify them.  We don't
            // care which order the units are notified.  By notifying
            // all transactions we automatically support the cancelling 
            // of nested transactions.
            // 
            foreach(UndoUnit unit in _unitStack) { 
                unit.ComponentRename(e);
            } 

        }

        ///  
        ///     Handles the transaction closed event.
        ///  
        private void OnTransactionClosed(object sender, DesignerTransactionCloseEventArgs e) { 
            if (_executingUnit == null && CurrentUnit != null) {
                PopUnitReason reason = e.TransactionCommitted ? PopUnitReason.TransactionCommit : PopUnitReason.TransactionCancel; 
                CheckPopUnit(reason);
            }
        }
 
        /// 
        ///     Handles the transaction opening event. 
        ///  
        private void OnTransactionOpening(object sender, EventArgs e) {
 
            // When a transaction is opened, we always push a new unit unless
            // we're executing a unit.  We can
            // push multiple units onto the stack to handle nested transactions.
            // 
            if (_enabled && _executingUnit == null) {
                _unitStack.Push(CreateUndoUnit(_host.TransactionDescription, _unitStack.Count == 0)); 
            } 
        }
 
        /// 
        /// 
        ///     This event is raised immediately before an undo action is performed.
        ///  
        protected virtual void OnUndoing(EventArgs e) {
            if (_undoingEvent != null) { 
                _undoingEvent(this, e); 
            }
        } 

        /// 
        /// 
        ///     This event is raised immediately after an undo action is performed. It 
        ///     will always be raised even if an exception is thrown.
        ///  
        protected virtual void OnUndone(EventArgs e) { 
            if (_undoneEvent != null) {
                _undoneEvent(this, e); 
            }
        }

        ///  
        ///     Debug tracing code.
        ///  
        [Conditional("DEBUG")] 
        private static void Trace(string text, params object[] values) {
            Debug.WriteLineIf(traceUndo.TraceVerbose, "UndoEngine: " + string.Format(CultureInfo.CurrentCulture, text, values)); 
        }

        /// 
        ///     The reason that CheckPopUnit is being called. 
        /// 
        private enum PopUnitReason { 
            Normal, 
            TransactionCommit,
            TransactionCancel, 
        }

        /// 
        ///     The component that needs to change as a result of another 
        ///     component being deleted.
        /// 
        private struct ReferencingComponent { 
            public IComponent component;
            public MemberDescriptor member; 

            public ReferencingComponent(IComponent component, MemberDescriptor member) {
                this.component = component;
                this.member = member; 
            }
        } 
 
        /// 
        ///  
        ///     This class embodies a unit of undoable work.  The undo
        ///     engine creates an undo unit when a change to the designer
        ///     is about to be made.  The undo unit is responsible for tracking
        ///     changes.  The undo engine will call Close on the unit when 
        ///     it no longer needs to track changes.
        ///  
        protected class UndoUnit { 

            private string      _name;          // the name of the undo unit 
            private UndoEngine  _engine;        // the undo engine we're tied to
            private ArrayList   _events;        // the list of events we've captured
            private ArrayList   _changeEvents;  // the list of change events we're currently capturing.  Only valid until Commit is called.
            private ArrayList   _removeEvents;  // the list of remove events we're currently capturing.  Only valid until a matching Removed is encountered. 
            private ArrayList   _ignoreAddingList;    // the list of objects that are currently being added.  We ignore change events between adding and added.
            private ArrayList   _ignoreAddedList;  // the list of objects that are added. We do not serialize before state for change events that happen in the same transaction 
            private bool        _reverse;       // if true, we walk the events list from the bottom up 
            private Hashtable   _lastSelection; // the selection as it was before we gathered undo info
 
            /// 
            /// 
            ///     Creates a new UndoUnit.
            ///  
            public UndoUnit(UndoEngine engine, string name) {
 
                if (engine == null) { 
                    throw new ArgumentNullException("engine");
                } 

                if (name == null) {
                    Debug.Fail("Null name passed to new undo unit");
                    name = string.Empty; 
                }
 
                UndoEngine.Trace("Creating undo unit '{0}'", name); 

                _name = name; 
                _engine = engine;
                _reverse = true;

                ISelectionService ss = _engine.GetService(typeof(ISelectionService)) as ISelectionService; 
                if (ss != null) {
                    ICollection selection = ss.GetSelectedComponents(); 
                    Hashtable selectedNames = new Hashtable(); 
                    foreach(object sel in selection) {
                        IComponent comp = sel as IComponent; 
                        if (comp != null && comp.Site != null) {
                            selectedNames[comp.Site.Name] = comp.Site.Container;
                        }
                    } 
                    _lastSelection = selectedNames;
                } 
            } 

            ///  
            /// 
            ///     The name of the unit.
            /// 
            public string Name { 
                get {
                    return _name; 
                } 
            }
 
            /// 
            /// 
            ///     This returns true if the undo unit has nothing
            ///     in it to undo.  The unit will be discarded. 
            /// 
            public virtual bool IsEmpty { 
                get { 
                    return _events == null || _events.Count == 0;
                } 
            }

            /// 
            ///  
            ///     The undo engine that was passed into the
            ///     constructor. 
            ///  
            protected UndoEngine UndoEngine {
                get { 
                    return _engine;
                }
            }
 
            /// 
            ///     Adds the given event to our event list. 
            ///  
            private void AddEvent(UndoEvent e) {
                if (_events == null) { 
                    _events = new ArrayList();
                }
                _events.Add(e);
            } 

            ///  
            ///  
            ///     Called by the undo engine when it wants to
            ///     close this unit.  The unit should do any final work 
            ///     it needs to do to close.
            /// 
            public virtual void Close() {
 
                if (_changeEvents != null) {
                    foreach (ChangeUndoEvent e in _changeEvents) { 
                        e.Commit(_engine); 
                    }
                } 

                if (_removeEvents != null) {
                    foreach(AddRemoveUndoEvent e in _removeEvents) {
                        e.Commit(_engine); 
                    }
                } 
 
                // At close time we are done with this list.  All change
                // events were simultaneously added to the _events list. 
                _changeEvents = null;
                _removeEvents = null;
                _ignoreAddingList = null;
                _ignoreAddedList = null; 
            }
 
            ///  
            /// 
            ///     The undo engine will call this on the active undo 
            ///     unit in response to a component added event.
            /// 
            public virtual void ComponentAdded(ComponentEventArgs e) {
                if (e.Component.Site != null && 
                    e.Component.Site.Container is INestedContainer)
                { 
                    // do nothing 
                }
                else 
                    AddEvent(new AddRemoveUndoEvent(_engine, e.Component, true));

                if (_ignoreAddingList != null) {
                    _ignoreAddingList.Remove(e.Component); 
                }
 
                if (_ignoreAddedList == null) 
                {
                    _ignoreAddedList = new ArrayList(); 
                }

                _ignoreAddedList.Add(e.Component);
            } 

            ///  
            ///  
            ///     The undo engine will call this on the active undo
            ///     unit in response to a component adding event. 
            /// 
            public virtual void ComponentAdding(ComponentEventArgs e) {
                if (_ignoreAddingList == null) {
                    _ignoreAddingList = new ArrayList(); 
                }
 
                _ignoreAddingList.Add(e.Component); 
            }
 
            private static bool ChangeEventsSymmetric(ComponentChangingEventArgs changing, ComponentChangedEventArgs changed) {
                if (changing == null || changed == null) {
                    return false;
                } 

                return changing.Component == changed.Component && changing.Member == changed.Member; 
            } 

            private bool CanRepositionEvent(int startIndex, ComponentChangedEventArgs e) { 
                bool containsAdd = false;
                bool containsRename = false;
                bool containsSymmetricChange = false;
 
                for (int i = startIndex + 1; i < _events.Count; i++) {
 
                    AddRemoveUndoEvent addEvt = _events[i] as AddRemoveUndoEvent; 
                    RenameUndoEvent renameEvt = _events[i] as RenameUndoEvent;
                    ChangeUndoEvent changeEvt = _events[i] as ChangeUndoEvent; 

                    if (addEvt != null && !addEvt.NextUndoAdds) {
                        containsAdd = true;
                    } 
                    else if (changeEvt != null && ChangeEventsSymmetric(changeEvt.ComponentChangingEventArgs, e)) {
                        containsSymmetricChange = true; 
                    } 
                    else if (renameEvt != null) {
                        containsRename = true; 
                    }
                }

                return containsAdd && !containsRename && !containsSymmetricChange; 
            }
 
            ///  
            /// 
            ///     The undo engine will call this on the active undo 
            ///     unit in response to a component changed event.
            /// 
            public virtual void ComponentChanged(ComponentChangedEventArgs e) {
                if (_events != null && e != null) { 

                    for (int i = 0; i < _events.Count; i++) { 
 
                        ChangeUndoEvent ce = _events[i] as ChangeUndoEvent;
 
                        // Determine if we've located the UndoEvent which was
                        // created as a result of a corresponding ComponentChanging event.
                        // If so, reposition to the "Changed" spot in the list if the following is true:
                        //          - It must be for a DSV.Content property 
                        //          - There must be a AddEvent between the Changing and Changed
                        //          - There are no renames in between Changing and Changed. 
                        // 
                        // See VSWhidbey 483192 for more info
                        if (ce != null && ChangeEventsSymmetric(ce.ComponentChangingEventArgs, e) && i != _events.Count - 1) { 


                            if (e.Member != null && e.Member.Attributes.Contains(DesignerSerializationVisibilityAttribute.Content) &&
                                CanRepositionEvent(i, e)) { 

                                _events.RemoveAt(i); 
                                _events.Add(ce); 

                            } 
                        }
                    }
                }
            } 

            ///  
            ///  
            ///     The undo engine will call this on the active undo
            ///     unit in response to a component changing event. 
            /// 
            public virtual void ComponentChanging(ComponentChangingEventArgs e) {

                // If we are in the process of adding this component, ignore any 
                // changes to it.  The ending "Added" event will capture the
                // component's state.  This not just an optimization.  If we get 
                // a change during an add, we can have an undo order that specifies 
                // a remove, and then a change to a removed component.
                if (_ignoreAddingList != null && _ignoreAddingList.Contains(e.Component)) { 
                    return;
                }

                if (_changeEvents == null) { 
                    _changeEvents = new ArrayList();
                } 
 
                // The site check here is done because the data team is calling
                // us for components that are not yet sited.  We end up 
                // writing them out as Guid-named locals.  That's fine, except
                // that we cannot capture after state for these types of things
                // so we assert.
                // 
                if (_engine != null && _engine.GetName(e.Component, false) != null) {
                    IComponent comp = e.Component as IComponent; 
                    #if false 
                    if (!(comp != null && comp.Site != null)) {
                        string name = _engine.GetName(e.Component, false); 
                        Debug.Fail("adding event for an unsited component:" + name);
                    }
                    #endif
 
                    // The caller provided us with a component.  This is the common
                    // case.  We will add a new change event provided there is not 
                    // already one open for this component. 
                    //
                    bool hasChange = false; 

                    for(int idx = 0; idx < _changeEvents.Count; idx++) {
                        ChangeUndoEvent ce = (ChangeUndoEvent)_changeEvents[idx];
                        if (ce.OpenComponent == e.Component && ce.ContainsChange(e.Member)) { 
                            hasChange = true;
                            break; 
                        } 
                    }
 
                    if (!hasChange ||
                        (e.Member != null && e.Member.Attributes != null && e.Member.Attributes.Contains(DesignerSerializationVisibilityAttribute.Content))) {
                        #if DEBUG
                        string name = _engine.GetName(e.Component, false); 
                        string memberName = "(none)";
                        if (e.Member != null && e.Member.Name != null) { 
                            memberName = e.Member.Name; 
                        }
                        if (name != null) { 
                            Debug.WriteLineIf(traceUndo.TraceVerbose && hasChange, "Adding second ChangeEvent for " + name + " Member: " + memberName);
                        }
                        else {
                            Debug.Fail("UndoEngine: GetName is failing on successive calls"); 
                        }
                        #endif 
 
                        ChangeUndoEvent changeEvent = null;
 
                        bool serializeBeforeState = true;
                        //perf: if this object was added in this undo unit we do not want to serialize before state for ChangeEvent since undo will remove it anyway
                        if (_ignoreAddedList != null && _ignoreAddedList.Contains(e.Component))
                        { 
                            serializeBeforeState = false;
                        } 
 
                        if (comp != null && comp.Site != null) {
 
                            changeEvent = new ChangeUndoEvent(_engine, e, serializeBeforeState);

                        }
                        else if (e.Component != null) { 
                            IReferenceService rs = GetService(typeof(IReferenceService)) as IReferenceService;
 
                            if (rs != null) { 
                                IComponent owningComp = rs.GetComponent(e.Component);
 
                                if (owningComp != null) {
                                    changeEvent = new ChangeUndoEvent(_engine, new ComponentChangingEventArgs(owningComp, null), serializeBeforeState);
                                }
                            } 
                        }
 
                        if (changeEvent != null) { 
                            AddEvent(changeEvent);
                            _changeEvents.Add(changeEvent); 
                        }
                    }
                }
            } 

            ///  
            ///  
            ///     The undo engine will call this on the active undo
            ///     unit in response to a component removed event. 
            /// 
            public virtual void ComponentRemoved(ComponentEventArgs e) {

                // We should gather undo state in ComponentRemoved, but by 
                // this time the component's designer has been destroyed so
                // it's too late.  Instead, we captured state in the Removing 
                // method.  But, it is possible for there to be component 
                // changes to other objects that happen between removing and removed,
                // so we need to reorder the removing event so it's positioned after 
                // any changes.
                //
                if (_events != null) {
                    ChangeUndoEvent changeEvt = null; 
                    int changeIdx = -1;
                    for (int idx = _events.Count - 1; idx >= 0; idx--) { 
                        AddRemoveUndoEvent evt = _events[idx] as AddRemoveUndoEvent; 

                        if (changeEvt == null) { 
                            changeEvt = _events[idx] as ChangeUndoEvent;
                            changeIdx = idx;
                        }
 
                        if (evt != null && evt.OpenComponent == e.Component) {
                            evt.Commit(_engine); 
 
                            // VSWhidbey 400094 - We should only reorder events if there
                            // are change events coming between OnRemoving and OnRemoved. 
                            // If there are other events (such as AddRemoving), the serialization
                            // done in OnComponentRemoving might refer to components that aren't available.
                            if (idx != _events.Count - 1 && changeEvt != null) {
 
                                // ensure only change change events exist between these two events
                                bool onlyChange = true; 
                                for (int i = idx + 1; i < changeIdx; i++) { 
                                    if (!(_events[i] is ChangeUndoEvent)) {
                                        onlyChange = false; 
                                        break;
                                    }

                                } 

                                if (onlyChange) { 
                                    // reposition event after final ComponentChangingEvent 
                                    _events.RemoveAt(idx);
                                    _events.Insert(changeIdx, evt); 
                                }
                            }
                            break;
                        } 
                    }
                } 
            } 

            ///  
            /// 
            ///     The undo engine will call this on the active undo
            ///     unit in response to a component removing event.
            ///  
            public virtual void ComponentRemoving(ComponentEventArgs e) {
                if (e.Component.Site != null && 
                    e.Component.Site is INestedContainer) { 
                    return;
                } 

                if (_removeEvents == null) {
                    _removeEvents = new ArrayList();
                } 
                try {
                    AddRemoveUndoEvent evt = new AddRemoveUndoEvent(_engine, e.Component, false); 
                    AddEvent(evt); 
                    _removeEvents.Add(evt);
                } 
                catch (TargetInvocationException) { }
            }

            ///  
            /// 
            ///     The undo engine will cal this on the active undo 
            ///     unit in response to a component rename event. 
            /// 
            public virtual void ComponentRename(ComponentRenameEventArgs e) { 
                AddEvent(new RenameUndoEvent(e.OldName, e.NewName));
            }

            ///  
            /// 
            ///     Returns an instance of the rquested service. 
            ///  
            protected object GetService(Type serviceType) {
                return _engine.GetService(serviceType); 
           }

            /// 
            ///  
            ///     Override for object.ToString()
            ///  
            public override string ToString() { 
                return Name;
            } 

            /// 
            /// 
            ///     Either performs undo, or redo, depending on the 
            ///     state of the unit.  UndoUnit initially assumes that
            ///     the undoable work has already been "done", so the first 
            ///     call to undo will undo the work.  The next call will 
            ///     undo the "undo", performing a redo.
            ///  
            public void Undo() {
                UndoEngine.Trace("Performing undo '{0}'", Name);
                UndoUnit savedUnit = _engine._executingUnit;
                _engine._executingUnit = this; 
                DesignerTransaction transaction = null;
                try { 
                    if (savedUnit == null) { 
                        _engine.OnUndoing(EventArgs.Empty);
                    } 

                    // create a transaction here so things that do work
                    // on componentchanged can ignore that while the transaction
                    // is opened...big perf win. 
                    //
                    transaction = _engine._host.CreateTransaction(); 
                    UndoCore(); 
                } catch(CheckoutException) {
                    //if(ex == CheckoutException.Canceled) { 
                        transaction.Cancel();
                        transaction = null;
                        throw;
                    //} 
                }
                finally { 
                    if (transaction != null) { 
                        transaction.Commit();
                    } 

                    _engine._executingUnit = savedUnit;
                    if (savedUnit == null) {
                        _engine.OnUndone(EventArgs.Empty); 
                    }
 
                } 
            }
 
            /// 
            /// 
            ///     The undo method invokes this method to perform the actual
            ///     undo / redo work.  You should never call this method 
            ///     directly; override it if you wish, but always call the
            ///     public Undo method to perform undo work.  Undo notifies 
            ///     the undo engine to suspend undo data gathering until 
            ///     this undo is completed, which prevents new undo units
            ///     from being created in response to this unit doing work. 
            /// 
            protected virtual void UndoCore() {

                if (_events != null) { 

                    if (_reverse) { 
 
                        // How does BeforeUndo work?  You'd think you should just call
                        // this in one pass, and then call Undo in another, but you'd be wrong. 
                        // The complexity arises because there are undo events that have
                        // dependencies on other undo events.  There are also undo events
                        // that have side effects with respect to other events.  Here are examples:
                        // 
                        // Rename is an undo event that other undo events depend on, because they
                        // store names.  It must be performed in the right order and it must be 
                        // performed before any subsequent event's BeforeUndo is called. 
                        //
                        // Property change is an undo event that may have an unknown side effect 
                        // if changing the property results in other property changes (for example,
                        // reparenting a control removes the control from its former parent).  A
                        // property change undo event must have all BeforeUndo methods called
                        // before any Undo method is called. 
                        //
                        // To do this, we have a property on UndoEvent called CausesSideEffects. 
                        // As we run through UndoEvents, consecutive events that return true 
                        // from this property are grouped so that their BeforeUndo methods are
                        // all called before their Undo methods.  For events that do not have 
                        // side effects, their BeforeUndo and Undo are invoked immediately.

                        for (int idx = _events.Count - 1; idx >= 0; idx--) {
 
                            int groupEndIdx = idx;
 
                            for (int groupIdx = idx; groupIdx >= 0; groupIdx--) { 
                                if (((UndoEvent)_events[groupIdx]).CausesSideEffects) {
                                    groupEndIdx = groupIdx; 
                                }
                                else {
                                    break;
                                } 
                            }
 
                            for (int beforeIdx = idx; beforeIdx >= groupEndIdx; beforeIdx--) { 
                                ((UndoEvent)_events[beforeIdx]).BeforeUndo(_engine);
                            } 

                            for (int undoIdx = idx; undoIdx >= groupEndIdx; undoIdx--) {
                                ((UndoEvent)_events[undoIdx]).Undo(_engine);
                            } 

                            Debug.Assert(idx >= groupEndIdx, "We're going backwards"); 
                            idx = groupEndIdx; 
                        }
 
                        // Now, if we have a selection, apply it.
                        //
                        if (_lastSelection != null) {
                            ISelectionService ss = _engine.GetService(typeof(ISelectionService)) as ISelectionService; 
                            if (ss != null) {
                                string[] names = new string[_lastSelection.Keys.Count]; 
                                _lastSelection.Keys.CopyTo(names, 0); 
                                ArrayList list = new ArrayList(names.Length);
                                foreach (string name in names) 
                                {
                                    if (name != null) {
                                        object comp = ((Container)_lastSelection[name]).Components[name];
                                        if (comp != null) { 
                                            list.Add(comp);
                                        } 
                                    } 
                                }
                                ss.SetSelectedComponents(list, SelectionTypes.Replace); 
                            }
                        }
                    }
                    else { 

                        int count = _events.Count; 
                        for (int idx =  0; idx < count; idx++) { 

                            int groupEndIdx = idx; 

                            for (int groupIdx = idx; groupIdx < count; groupIdx++) {
                                if (((UndoEvent)_events[groupIdx]).CausesSideEffects) {
                                    groupEndIdx = groupIdx; 
                                }
                                else { 
                                    break; 
                                }
                            } 

                            for (int beforeIdx = idx; beforeIdx <= groupEndIdx; beforeIdx++) {
                                ((UndoEvent)_events[beforeIdx]).BeforeUndo(_engine);
                            } 

                            for (int undoIdx = idx; undoIdx <= groupEndIdx; undoIdx++) { 
                                ((UndoEvent)_events[undoIdx]).Undo(_engine); 
                            }
 
                            Debug.Assert(idx <= groupEndIdx, "We're going backwards");
                            idx = groupEndIdx;
                        }
                    } 
                }
 
                _reverse = !_reverse; 
            }
 
            /// 
            ///     This undo event handles addition and removal of
            ///     components.
            ///  
            private sealed class AddRemoveUndoEvent : UndoEvent {
 
                private SerializationStore  _serializedData; 
                private string              _componentName;
                private bool                _nextUndoAdds; 
                private bool                _committed;
                private IComponent          _openComponent;

                ///  
                ///     Creates a new object that contains the state of
                ///     the event.  The last parameter, add, determines 
                ///     the initial mode of this event.  If true, it means 
                ///     this event is being created in response to a component
                ///     add.  If false, it is being created in response to 
                ///     a component remove.
                /// 
                public AddRemoveUndoEvent(UndoEngine engine, IComponent component, bool add) {
 
                    _componentName = component.Site.Name;
                    _nextUndoAdds = !add; 
                    _openComponent = component; 

                    UndoEngine.Trace("---> Creating {0} undo event for '{1}'", ( add ? "Add" : "Remove"), _componentName); 

                    using (_serializedData = engine._serializationService.CreateStore()) {
                        engine._serializationService.Serialize(_serializedData, component);
                    } 

                    // For add events, we commit as soon as we receive the event. 
                    _committed = add; 
                }
 
                /// 
                ///     Returns true if the add remove event has been comitted.
                /// 
                internal bool Committed { 
                    get {
                        return _committed; 
                    } 
                }
 
                /// 
                ///     If this add/remove event is still open, OpenCompnent will contain the
                ///     component it is operating on.
                ///  
                internal IComponent OpenComponent {
                    get { 
                        return _openComponent; 
                    }
                } 

                /// 
                ///     Returns true if undoing this event will add a component.
                ///  
                internal bool NextUndoAdds {
                    get { 
                        return _nextUndoAdds; 
                    }
                } 

                /// 
                ///     Commits this event.
                ///  
                internal void Commit(UndoEngine engine) {
                    if (!Committed) { 
                        UndoEngine.Trace("---> Committing remove of '{0}'", _componentName); 
                        _committed = true;
                    } 
                }

                /// 
                ///     Actually performs the undo action. 
                /// 
                public override void Undo(UndoEngine engine) { 
 
                    if (_nextUndoAdds) {
 
                        UndoEngine.Trace("---> Adding '{0}'", _componentName);

                        // We need to add this component.  To add it, we deserialize it and then
                        // we add it to the designer host's container. 
                        //
                        IDesignerHost host = engine.GetRequiredService(typeof(IDesignerHost)) as IDesignerHost; 
 
                        if (host != null) {
                            engine._serializationService.DeserializeTo(_serializedData, host.Container); 
                        }
                    }
                    else {
 
                        UndoEngine.Trace("---> Removing '{0}'", _componentName);
 
                        // We need to remove this component.  Take the name and 
                        // match it to an object, and then ask that object to delete itself.
                        // 
                        IDesignerHost host = engine.GetRequiredService(typeof(IDesignerHost)) as IDesignerHost;

                        IComponent component = host.Container.Components[_componentName];
 
                        //Note: It's ok for the component to be null here.  This could happen
                        //if the parent to this control is disposed first. Ex:SplitContainer 
                        if (component != null) { 
                            host.DestroyComponent(component);
                        } 
                    }
                    _nextUndoAdds = !_nextUndoAdds;
                }
            } 

            ///  
            ///     The base class for all change events.  We actually support 
            ///     three different kinds of change events.
            ///  
            private sealed class ChangeUndoEvent : UndoEvent {

                // This is only valid while the change is still open.  The
                // change is committed. 
                private object             _openComponent;
 
                // Static data we hang onto about this change. 
                private string             _componentName;
                private MemberDescriptor   _member; 

                // Before and after state.  Before state is built in the
                // constructor.  After state is built right before
                // we undo for the first time. 
                private SerializationStore _before;
                private SerializationStore _after; 
                private bool               _savedAfterState; 

 

                /// 
                ///     Creates a new component change undo event.  This event consists of a before and after snapshot
                ///     of a single component.  A snapshot will not be taken if a name for the component cannot be 
                ///     determined.
                ///  
                public ChangeUndoEvent(UndoEngine engine, ComponentChangingEventArgs e, bool serializeBeforeState) { 

                    _componentName = engine.GetName(e.Component, true); 
                    _openComponent = e.Component;
                    _member = e.Member;

                    UndoEngine.Trace("---> Creating change undo event for '{0}'", _componentName); 
                    UndoEngine.Trace("---> Saving before snapshot for change to '{0}'", _componentName);
                    if (serializeBeforeState) 
                    { 
                        _before = Serialize(engine, _openComponent, _member);
                    } 
                }

                public ComponentChangingEventArgs ComponentChangingEventArgs {
                    get { 
                        return new ComponentChangingEventArgs(_openComponent, _member);
                    } 
                } 

                ///  
                ///     Indicates that undoing this event may cause side effects in other objects.
                ///     Chagne events fall into this category because, for example,
                ///     a change involving adding an object to one collection may have
                ///     a side effect of removing it from another collection.  Events 
                ///     with side effects are grouped at undo time so all their
                ///     BeforeUndo methods are called before their Undo methods. 
                ///     Events without side effects have their BeforeUndo called 
                ///     and then their Undo called immediately after.
                ///  
                public override bool CausesSideEffects { get { return true; } }

                /// 
                ///     Returns true if the change event has been comitted. 
                /// 
                public bool Committed { 
                    get { 
                        return _openComponent == null;
                    } 
                }

                /// 
                ///     Returns the component this change event is currently 
                ///     tracking.  This will return null once the change event
                ///     is committed. 
                ///  
                public object OpenComponent {
                    get { 
                        return _openComponent;
                    }
                }
 
                /// 
                ///     Called before Undo is called.  All undo events 
                ///     get their BeforeUndo called, and then they all 
                ///     get their Undo called.  This allows the undo
                ///     event to examine the state of the world before 
                ///     other undo events mess with it.
                /// 
                public override void BeforeUndo(UndoEngine engine) {
                    if (!_savedAfterState) { 
                        _savedAfterState = true;
                        SaveAfterState(engine); 
                    } 
                }
 
                /// 
                ///     Determines if this
                /// 
                public bool ContainsChange(MemberDescriptor desc) { 

                    if (_member == null) { 
                        return true; 
                    }
 
                    if (desc == null) {
                        return false;
                    }
 
                    return desc.Equals(_member);
                } 
 
                /// 
                ///     Commits the unit.  Comitting the unit saves the "after" 
                ///     snapshot of the unit.  If commit is called multiple times
                ///     only the first commit is registered.
                /// 
                public void Commit(UndoEngine engine) { 
                    if (!Committed) {
                        UndoEngine.Trace("---> Committing change to '{0}'", _componentName); 
                        _openComponent = null; 
                    }
                } 

                /// 
                ///     Saves the after state of this undo unit.  This is deferred
                ///     until absolutely necessary. 
                /// 
                private void SaveAfterState(UndoEngine engine) { 
                    Debug.Assert(_after == null, "Change undo saving state twice."); 
                    UndoEngine.Trace("---> Saving after snapshot for change to '{0}'", _componentName);
                    object component = null; 

                    IReferenceService rs = engine.GetService(typeof(IReferenceService)) as IReferenceService;
                    if (rs != null) {
                        component = rs.GetReference(_componentName); 
                    }
                    else { 
                        IDesignerHost host = engine.GetService(typeof(IDesignerHost)) as IDesignerHost; 
                        if (host != null) {
                            component = host.Container.Components[_componentName]; 
                        }
                    }

                    // It is OK for us to not find a component here.  That can happen if our "after" state 
                    // is owned by another change, like an add of the component.
                    if (component != null) { 
                        _after = Serialize(engine, component, _member); 
                    }
                } 

                /// 
                ///     Helper function that serializes the given component into a byte array.
                ///  
                private SerializationStore Serialize(UndoEngine engine, object component, MemberDescriptor member) {
 
                    SerializationStore store; 

                    using (store = engine._serializationService.CreateStore()) { 
                        if (member != null && !(member.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden))) {
                            engine._serializationService.SerializeMemberAbsolute(store, component, member);
                        }
                        else { 
                            engine._serializationService.SerializeAbsolute(store, component);
                        } 
                    } 

                    return store; 
                }

                /// 
                ///     Performs the actual undo.  AFter it finishes 
                ///     it will reverse the role of _before and _after
                ///  
                public override void Undo(UndoEngine engine) { 

                    UndoEngine.Trace("---> Applying changes to '{0}'", _componentName); 
                    Debug.Assert(_savedAfterState, "After state not saved.  BeforeUndo was not called?");

                    if (_before != null) {
 
                        IDesignerHost host = engine.GetService(typeof(IDesignerHost)) as IDesignerHost;
                        if (host != null) { 
                            engine._serializationService.DeserializeTo(_before, host.Container); 
                        }
                    } 

                    SerializationStore temp = _after;
                    _after = _before;
                    _before = temp; 
                }
            } 
 
            /// 
            ///     This class handles the undo / redo for a rename 
            ///     event.
            /// 
            private sealed class RenameUndoEvent : UndoEvent {
 
                private string _before;
                private string _after; 
 
                /// 
                ///     Creates a new rename undo event. 
                /// 
                public RenameUndoEvent(string before, string after) {
                    _before = before;
                    _after = after; 
                    UndoEngine.Trace("---> Creating rename undo event for '{0}'->'{1}'", _before, _after);
                } 
 
                /// 
                ///     Simply undoes a rename by setting the name 
                ///     back to the saved value.
                /// 
                public override void Undo(UndoEngine engine) {
                    UndoEngine.Trace("---> Renaming '{0}'->'{1}'", _after, _before); 
                    IComponent comp = engine._host.Container.Components[_after];
                    if (comp != null) { 
                        engine.ComponentChangeService.OnComponentChanging(comp, null); 
                        comp.Site.Name = _before;
                        string temp = _after; 
                        _after = _before;
                        _before = temp;
                    }
                } 
            }
 
            ///  
            ///     This abstract class is the base of each of our
            ///     undo events.  There are different concrete implementations 
            ///     of an undo event for different types of events.
            ///     For example, a property change event records the
            ///     difference between two property changes, while a
            ///     component add event creates and destroys components. 
            /// 
            private abstract class UndoEvent { 
 
                /// 
                ///     Indicates that undoing this event may cause side effects in other objects. 
                ///     Chagne events fall into this category because, for example,
                ///     a change involving adding an object to one collection may have
                ///     a side effect of removing it from another collection.  Events
                ///     with side effects are grouped at undo time so all their 
                ///     BeforeUndo methods are called before their Undo methods.
                ///     Events without side effects have their BeforeUndo called 
                ///     and then their Undo called immediately after. 
                /// 
                public virtual bool CausesSideEffects { get { return false; } } 

                /// 
                ///     Called before Undo is called.  All undo events
                ///     get their BeforeUndo called, and then they all 
                ///     get their Undo called.  This allows the undo
                ///     event to examine the state of the world before 
                ///     other undo events mess with it. 
                ///
                ///     BeforeUndo returns true if before undo was 
                ///     supported, and false if not.  If before undo is
                ///     not supported, the undo unit should be undone
                ///     immediately.
                ///  
                public virtual void BeforeUndo(UndoEngine engine) {
                } 
 
                /// 
                ///     Called by the undo unit when it wants to 
                ///     undo this bit of work.
                /// 
                public abstract void Undo(UndoEngine engine);
            } 
        }
    } 
} 


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