//---------------------------------------------------------------- // 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: // // <Style TargetType="{x:Type PropertyEditing:EditModeSwitchButton}" BasedOn="{StaticResource {x:Type Button}}"> // ... // <Setter Property="Internal:EditModeSwitchButtonKeyboardFix.ApplyFix" Value="True" /> // </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(); } } |