File: System.Activities.Presentation\System\Activities\Presentation\Base\Core\Internal\PropertyEditing\EditModeSwitchButtonKeyboardFix.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Presentation.Internal.PropertyEditing 
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Threading;
    using System.Activities.Presentation.PropertyEditing;
    using System.Runtime;
    using System.Activities.Presentation;
 
    // <summary>
    // This is a fix for a bug in EditModeSwitchButton which lives in System.Activities.Presentation and
    // which has already been locked as an assembly.  EditModeSwitchButton only responds to MouseDown
    // events to make sure that the opening of the popup extended editor works correctly.  However,
    // that means that it will never respond to keyboard or automation events.  To fix it, this class
    // offers up an attached DP that, when used, hooks into events offered by EditModeSwitchButton
    // to correct this issue and still have the button do the right thing even if mouse is not involved
    // in invoking the button.
    //
    // This class is associated with every instance of EditModeSwitchButton using a setter in the
    // EditModeSwitchButton style:
    //
    //   &lt;Style TargetType="{x:Type PropertyEditing:EditModeSwitchButton}" BasedOn="{StaticResource {x:Type Button}}"&gt;
    //     ...
    //     &lt;Setter Property="Internal:EditModeSwitchButtonKeyboardFix.ApplyFix" Value="True" /&gt;
    // </summary>
 
    class EditModeSwitchButtonKeyboardFix 
    {
 
        // <summary>
        // Property used to correct a problem with EditModeSwitchButton.  This property should only be
        // applied to EditModeSwitchButton class instances.
        // </summary>
        public static readonly DependencyProperty ApplyFixProperty = DependencyProperty.RegisterAttached(
            "ApplyFix",
            typeof(bool),
            typeof(EditModeSwitchButtonKeyboardFix),
            new PropertyMetadata(false, new PropertyChangedCallback(OnApplyFixPropertyChanged)));
 
        private bool _clickInitiatedByMouse;
 
        private EditModeSwitchButtonKeyboardFix(EditModeSwitchButton button) 
        {
            button.Click += new RoutedEventHandler(OnEditModeSwitchButtonClick);
            button.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(OnEditModeSwitchButtonPreviewMouseLeftButtonUp);
        }
 
        // <summary>
        // Gets the value of ApplyFix property from the specified DependencyObject.
        // </summary>
        // <param name="obj"></param>
        // <returns></returns>
        public static bool GetApplyFix(DependencyObject obj) 
        {
            if (obj == null)
            {
                throw FxTrace.Exception.ArgumentNull("obj");
            }
 
            return (bool)obj.GetValue(ApplyFixProperty);
        }
 
        // <summary>
        // Sets the value of ApplyFix property onto the specified DependencyObject.  Only
        // instances of EditModeSwitchButton classes should be used as the targets of this property.
        // </summary>
        // <param name="obj"></param>
        // <param name="value"></param>
        public static void SetApplyFix(DependencyObject obj, bool value) 
        {
            if (obj == null)
            {
                throw FxTrace.Exception.ArgumentNull("obj");
            }
 
            obj.SetValue(ApplyFixProperty, value);
        }
 
        [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")]
        private static void OnApplyFixPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
        {
            EditModeSwitchButton button = obj as EditModeSwitchButton;
            if (button == null) 
            {
                Debug.Fail("EditModeSwitchButtonKeyboardFix.ApplyFix fix can only be applied to EditModeSwitchButton instances.");
                return;
            }
            if (object.Equals(e.NewValue, true)) 
            {
                // Instantiating this class will make itself hook into EditModeSwitchButton's events,
                // hence not be chewed up by garbage collector
                new EditModeSwitchButtonKeyboardFix(button);
            }
        }
 
        private void OnEditModeSwitchButtonPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
        {
            _clickInitiatedByMouse = true;
 
            ((DispatcherObject)sender).Dispatcher.BeginInvoke(DispatcherPriority.Input, new MethodInvoker(delegate() 
            {
                _clickInitiatedByMouse = false;
            }));
        }
 
        private void OnEditModeSwitchButtonClick(object sender, RoutedEventArgs e) 
        {
            if (_clickInitiatedByMouse)
            {
                return;
            }
 
            EditModeSwitchButton button = e.OriginalSource as EditModeSwitchButton;
            Fx.Assert(button != null, "Expected to see the EditModeSwitchButton at this point.");
            if (button == null)
            {
                return;
            }
 
            // At this point the click was initiated using the Invoke AutomationPeer pattern or
            // by using the keyboard.  So, make sure that the EditModeSwitchButton.OnMouseDown
            // button still executes.
            // Invoke the appropriate command
            switch (button.TargetEditMode) 
            {
                case PropertyContainerEditMode.Inline:
                    PropertyValueEditorCommands.ShowInlineEditor.Execute(null, button);
                    break;
                case PropertyContainerEditMode.ExtendedPopup:
                    PropertyValueEditorCommands.ShowExtendedPopupEditor.Execute(null, button);
                    break;
                case PropertyContainerEditMode.ExtendedPinned:
                    PropertyValueEditorCommands.ShowExtendedPinnedEditor.Execute(null, button);
                    break;
                case PropertyContainerEditMode.Dialog:
                    PropertyValueEditorCommands.ShowDialogEditor.Execute(null, button);
                    break;
                default:
                    Debug.Fail(string.Format(
                        CultureInfo.CurrentCulture,
                        "EditModeSwitchButtonKeyboardFix does not yet support PropertyContainerEditMode '{0}'.",
                        button.TargetEditMode.ToString()));
                    break;
            }
        }
        private delegate void MethodInvoker();
    }
}