File: src\Framework\System\windows\Documents\UIElementPropertyUndoUnit.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//---------------------------------------------------------------------------
//
// File: UIElementPropertyUndoUnit.cs
//
// Description: Undo unit for property sets on UIElement. 
// It is created for:
//  1. FlowDirectionProperty sets on a TextEditor's UI scope element.
//  2. HorizontalAlignmentProperty sets on UIElement child of BlockUIContainer.
//
// History:  
// 01/13/2006 : pjoshi - Branched from FlowDirectionUndoUnit.cs
//
//---------------------------------------------------------------------------
 
using System;
using System.Windows;
using MS.Internal;
using MS.Internal.Documents;
 
namespace System.Windows.Documents
{
    // 1. Undo unit for FlowDirectionProperty sets on a TextEditor's UI scope element.
    //
    // The TextEditor lets users configure the FlowDirection of text dynamically
    // even when running on TextBoxes (TextEditor.AcceptsRichContent == false).
    // In this case, there's no TextElement to hang property values on, so we must
    // resort to assigning values directly to the TextEditor's UiScope, which is
    // not covered by the TextContainer's undo infrastructure.
    // 
    // This class encapsulates an undo unit used to track FlowDirection changes on
    // plain text documents.
    // 
    // 2. Undo unit for HorizontalAlignmentProperty sets on UIElement child of BlockUIContainer.
    // 
    // When applying TextAlignment property to BlockUIContainer elements in rich text documents, 
    // text alignment must be translated into HorizontalAlignment of its child UIElement. 
    // We must assign this property value directly to the embedded UIElement. 
    // Since TextEditor's undo mechanism for property changes on TextElements in the tree relies 
    // on the OnPropertyChanged event listener, it is unable to track this property change. 
    // Also, it is infeasible to have a OnPropertyChanged listener for all UIElements.
    // So we resort to explicitly adding this special undo unit in such situation.
    // 
    internal class UIElementPropertyUndoUnit : IUndoUnit
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        // Create a new undo unit instance.
        private UIElementPropertyUndoUnit(UIElement uiElement, DependencyProperty property, object oldValue)
        {
            _uiElement = uiElement;
            _property = property;
            _oldValue = oldValue;
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        // Called by the undo manager.  Restores tree state to its condition
        // when the unit was created.
        public void Do()
        {
            if (_oldValue != DependencyProperty.UnsetValue)
            {
                _uiElement.SetValue(_property, _oldValue);
            }
            else
            {
                _uiElement.ClearValue(_property);
            }
        }
 
        // Called by the undo manager.
        public bool Merge(IUndoUnit unit)
        {
            Invariant.Assert(unit != null);
            return false;
        }
 
        #endregion Public Methods        
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        // Note that following strongly typed overloads of Add are meant to restrict the usage of UIElementPropertyUndoUnit 
        // for tracking changes to only a limited set of properties of UIElements.
        // In general, it is not safe to keep a reference to the original property value, however for enum types, it is safe to do so.
 
        // Add a new UIElementPropertyUndoUnit to the undo stack for HorizontalAlignment property.
        internal static void Add(ITextContainer textContainer, UIElement uiElement, DependencyProperty property, HorizontalAlignment newValue)
        {
            AddPrivate(textContainer, uiElement, property, newValue);
        }
 
        // Add a new UIElementPropertyUndoUnit to the undo stack for FlowDirection property.
        internal static void Add(ITextContainer textContainer, UIElement uiElement, DependencyProperty property, FlowDirection newValue)
        {
            AddPrivate(textContainer, uiElement, property, newValue);
        }
 
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Privte Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
        
        // Private helper that adds a new UIElementPropertyUndoUnit to the undo stack.
        private static void AddPrivate(ITextContainer textContainer, UIElement uiElement, DependencyProperty property, object newValue)
        {
            UndoManager undoManager = TextTreeUndo.GetOrClearUndoManager(textContainer);
 
            if (undoManager == null)
            {
                return;
            }
 
            object currentValue = uiElement.ReadLocalValue(property);
 
            if (currentValue is Expression)
            {
                // Can't undo when old value is an expression, so clear the stack.
                if (undoManager.IsEnabled)
                {
                    undoManager.Clear();
                }
                return;
            }
 
            if (currentValue.Equals(newValue))
            {
                // No property change.
                return;
            }
 
            undoManager.Add(new UIElementPropertyUndoUnit(uiElement, property, currentValue));
        }
 
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        // UIElement associated with this undo unit.
        private readonly UIElement _uiElement;
 
        // DependencyProperty of the UIElement associated with this undo unit.
        private readonly DependencyProperty _property;
 
        // Original property value.
        private readonly object _oldValue;
 
        #endregion Private Fields
    }
}