File: System.Activities.Presentation\System\Activities\Presentation\Base\Core\Internal\PropertyEditing\FromExpression\Framework\ValueEditors\StringEditor.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
// -------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -------------------------------------------------------------------
//From \\authoring\Sparkle\Source\1.0.1083.0\Common\Source\Framework\ValueEditors
namespace System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.ValueEditors
{
    using System;
    using System.Windows;
    using System.Windows.Automation;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Threading;
    using System.Activities.Presentation.View;
 
    internal class StringEditor : TextBox
    {
 
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(StringEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(StringEditor.ValueChanged), null, false, UpdateSourceTrigger.PropertyChanged));
        public static readonly DependencyProperty AutomationNameProperty = DependencyProperty.Register("AutomationName", typeof(string), typeof(StringEditor));
        public static readonly DependencyProperty IsNinchedProperty = DependencyProperty.Register("IsNinched", typeof(bool), typeof(StringEditor), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(StringEditor.IsNinchedChanged)));
        public static readonly DependencyProperty IsEditingProperty = DependencyProperty.Register("IsEditing", typeof(bool), typeof(StringEditor), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(StringEditor.IsEditingChanged)));
        
        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(double), typeof(StringEditor), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.None));
        public static readonly DependencyProperty BorderWidthProperty = DependencyProperty.Register("BorderWidth", typeof(double), typeof(StringEditor), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.None));
        
        public static readonly DependencyProperty BeginCommandProperty = DependencyProperty.Register("BeginCommand", typeof(ICommand), typeof(StringEditor), new PropertyMetadata(null));
        public static readonly DependencyProperty CommitCommandProperty = DependencyProperty.Register("CommitCommand", typeof(ICommand), typeof(StringEditor), new PropertyMetadata(null));
        public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register("CancelCommand", typeof(ICommand), typeof(StringEditor), new PropertyMetadata(null));
        public static readonly DependencyProperty FinishEditingCommandProperty = DependencyProperty.Register("FinishEditingCommand", typeof(ICommand), typeof(StringEditor), new PropertyMetadata(null));
        public static readonly DependencyProperty LostFocusCommandProperty = DependencyProperty.Register("LostFocusCommand", typeof(ICommand), typeof(StringEditor), new PropertyMetadata(null));
 
        private LostFocusAction lostFocusAction = LostFocusAction.None;
        private bool ignoreTextChanges = false;
 
        public StringEditor()
        {
            this.CommandBindings.Add(new CommandBinding( DesignerView.CommitCommand, OnDesignerViewCommitExecute));
            this.Loaded += new RoutedEventHandler(OnLoaded);
        }
 
        public string Value
        {
            get { return (string)this.GetValue(StringEditor.ValueProperty); }
            set { this.SetValue(StringEditor.ValueProperty, value); }
        }
 
        public bool IsNinched
        {
            get { return (bool)this.GetValue(StringEditor.IsNinchedProperty); }
            set { this.SetValue(StringEditor.IsNinchedProperty, value); }
        }
 
        public string AutomationName
        {
            get { return (string)this.GetValue(StringEditor.AutomationNameProperty); }
            set { this.SetValue(StringEditor.AutomationNameProperty, value); }
        }
 
        public bool IsEditing
        {
            get { return (bool)this.GetValue(StringEditor.IsEditingProperty); }
            set { this.SetValue(StringEditor.IsEditingProperty, value); }
        }
 
        public double CornerRadius
        {
            get { return (double)this.GetValue(StringEditor.CornerRadiusProperty); }
            set { this.SetValue(StringEditor.CornerRadiusProperty, value); }
        }
 
        public double BorderWidth
        {
            get { return (double)this.GetValue(StringEditor.BorderWidthProperty); }
            set { this.SetValue(StringEditor.BorderWidthProperty, value); }
        }
 
        public ICommand BeginCommand
        {
            get { return (ICommand)this.GetValue(StringEditor.BeginCommandProperty); }
            set { this.SetValue(StringEditor.BeginCommandProperty, value); }
        }
 
        public ICommand CommitCommand
        {
            get { return (ICommand)this.GetValue(StringEditor.CommitCommandProperty); }
            set { this.SetValue(StringEditor.CommitCommandProperty, value); }
        }
 
        public ICommand CancelCommand
        {
            get { return (ICommand)this.GetValue(StringEditor.CancelCommandProperty); }
            set { this.SetValue(StringEditor.CancelCommandProperty, value); }
        }
 
        public ICommand FinishEditingCommand
        {
            get { return (ICommand)this.GetValue(StringEditor.FinishEditingCommandProperty); }
            set { this.SetValue(StringEditor.FinishEditingCommandProperty, value); }
        }
 
        public ICommand LostFocusCommand
        {
            get { return (ICommand)this.GetValue(StringEditor.LostFocusCommandProperty); }
            set { this.SetValue(StringEditor.LostFocusCommandProperty, value); }
        }
 
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (!LocalAppContextSwitches.UseLegacyAccessibilityFeatures && !String.IsNullOrEmpty(AutomationName))
            {
                this.SetValue(AutomationProperties.NameProperty, AutomationName);
            }
        }
 
        private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            StringEditor editor = d as StringEditor;
            if (editor != null)
            {
                editor.UpdateTextFromValue();
            }
        }
 
        private static void IsNinchedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            StringEditor editor = d as StringEditor;
            if (editor != null)
            {
                editor.UpdateTextFromValue();
            }
        }
 
        private static void IsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            StringEditor editor = d as StringEditor;
            if (editor != null)
            {
                bool isNowEditing = (bool)e.NewValue;
                if (isNowEditing)
                {
                    if (editor.IsInitialized)
                    {
                        editor.Focus();
                    }
                    else
                    {
                        editor.PostFocusCallback();
                    }
                }
            }
        }
 
        // ###################################################
        // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
        // ###################################################
        // StringEditor should select text only when it gets the logical focus and nowhere else.
        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            this.SelectAll();
        }
        // ###################################################
        // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
        // ###################################################
 
 
        protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            if (!this.IsReadOnly)
            {
                this.IsEditing = true;
            }
            base.OnGotKeyboardFocus(e);
        }
 
        protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            // We have to commit on _preview_ lost focus because the ---- tab control does
            // work before we loose focus which causes keyframing stuff to happen out of order.
            base.OnPreviewLostKeyboardFocus(e);
            InternalLostFocus();
        }
 
        protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            base.OnLostKeyboardFocus(e);
            // We have to commit on lost focus as well, since when WPF application
            // loosing focus to another Win32 window, e.g. the MFC Artboard in Acrylic,
            // the PreviewLostKeyboardFocus event is not triggered.
            InternalLostFocus();
        }
 
        private void InternalLostFocus()
        {
            LostFocusAction savedLostFocusAction = this.lostFocusAction;
            // Set this to none here so that we will not commit twice when re-entrant (preview lost keyboard focus, then
            // lost keyboard focus in the same callstack)
            this.lostFocusAction = LostFocusAction.None;
            if (savedLostFocusAction == LostFocusAction.Commit)
            {
                this.CommitChange();
            }
            else if (savedLostFocusAction == LostFocusAction.Cancel)
            {
                this.CancelChange();
            }
        }
 
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            this.IsEditing = false;
            ValueEditorUtils.ExecuteCommand(this.LostFocusCommand, this, null);
            e.Handled = true;
        }
 
        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            base.OnTextChanged(e);
 
            if (this.ignoreTextChanges)
            {
                return;
            }
 
            if (this.IsEditing)
            {
                // If we get any text change default to commit if focus goes away
                this.lostFocusAction = LostFocusAction.Commit;
            }
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            bool markHandled = ValueEditorUtils.GetHandlesCommitKeys(this);
            if (e.Key == Key.Return || e.Key == Key.Enter)
            {
                LostFocusAction savedAction = this.lostFocusAction;
                this.lostFocusAction = LostFocusAction.None;
                if (savedAction == LostFocusAction.Commit)
                {
                    this.CommitChange();
                }
 
                if ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == 0)
                {
                    this.OnFinishEditing();
                }
 
                e.Handled |= markHandled;
            }
            else if (e.Key == Key.Escape && this.IsEditing)
            {
                LostFocusAction savedAction = this.lostFocusAction;
                this.lostFocusAction = LostFocusAction.None;
                if (savedAction != LostFocusAction.None)
                {
                    this.CancelChange();
                }
 
                this.OnFinishEditing();
 
                e.Handled |= markHandled;
            }
            base.OnKeyDown(e);
        }
 
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
        }
 
        protected override void OnRender(DrawingContext drawingContext)
        {
            Pen strokePen = null;
            Double borderWidth = this.BorderWidth;
            Brush borderBrush = this.BorderBrush;
            if (borderWidth > 0d && borderBrush != null)
            {
                strokePen = new Pen(borderBrush, borderWidth);
            }
 
            RenderUtils.DrawInscribedRoundedRect(drawingContext, this.Background, strokePen, new Rect(0d, 0d, this.ActualWidth, this.ActualHeight), this.CornerRadius);
 
            base.OnRender(drawingContext);
        }
 
        private void UpdateTextFromValue()
        {
            this.ignoreTextChanges = true;
 
            if (!this.IsNinched)
            {
                this.Text = this.Value;
            }
            else
            {
                this.Text = null;
            }
 
            // ###################################################
            // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
            // ###################################################
 
            // Whenever we set the value of a StringEditor programmatically
            // (either because the contained property value changed somehow
            // or because the user changed the value, hit enter, and we
            // are looping back to see what the new value is now), make sure
            // to clear the TextBox's UndoManager's buffer.  That way, the control
            // will not be able to perform Undo and the command will be forwarded
            // to the host.
 
            // TextBoxBase does not provide any API to reset its Undo stack,
            // but we can hack around it by resetting the UndoLimit, which
            // accomplishes the same thing.
            //
            int originalUndoLimit = this.UndoLimit;
            this.UndoLimit = 0;
            this.UndoLimit = originalUndoLimit;
 
            // ###################################################
            // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
            // ###################################################
 
            this.ignoreTextChanges = false;
        }
 
        private void CommitChange()
        {
 
            ValueEditorUtils.ExecuteCommand(this.BeginCommand, this, null);
            this.Value = this.Text;
            ValueEditorUtils.ExecuteCommand(this.CommitCommand, this, null);
            ValueEditorUtils.UpdateBinding(this, StringEditor.ValueProperty, UpdateBindingType.Target);
            // Now update the text value in case the model or a binding has reformated
            this.UpdateTextFromValue();
        }
 
        void OnDesignerViewCommitExecute(object sender, ExecutedRoutedEventArgs e)
        {
            this.InternalLostFocus();
            e.Handled = true;
        }
 
        private void CancelChange()
        {
            ValueEditorUtils.ExecuteCommand(this.BeginCommand, this, null);
            ValueEditorUtils.UpdateBinding(this, StringEditor.ValueProperty, false);
            ValueEditorUtils.ExecuteCommand(this.CancelCommand, this, null);
            ValueEditorUtils.UpdateBinding(this, StringEditor.ValueProperty, UpdateBindingType.Target);
            this.UpdateTextFromValue();
        }
 
        private void OnFinishEditing()
        {
            ICommand finishedEditingCommand = this.FinishEditingCommand;
            if (finishedEditingCommand != null)
            {
                ValueEditorUtils.ExecuteCommand(finishedEditingCommand, this, null);
            }
            else
            {
                Keyboard.Focus(null);
            }
        }
 
        private void PostFocusCallback()
        {
            UIThreadDispatcher.Instance.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(this.SetFocus), null);
        }
 
        private object SetFocus(object o)
        {
            if (this.Visibility == Visibility.Visible)
            {
                this.Focus();
            }
 
            return null;
        }
        private enum LostFocusAction
        {
            None,
            Commit,
            Cancel
        }
 
    }
}