Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Controls / Primitives / ButtonBase.cs / 1 / ButtonBase.cs
using System; using System.Collections; using System.ComponentModel; using System.Windows.Threading; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows; using System.Windows.Input; using System.Windows.Media; using MS.Utility; using MS.Internal.KnownBoxes; using MS.Internal.PresentationFramework; using System.Diagnostics; using System.Security; using System.Security.Permissions; namespace System.Windows.Controls.Primitives { ////// The base class for all buttons /// ////// ButtonBase adds IsPressed state, Click event, and Invoke features to a ContentControl. /// [DefaultEvent("Click")] [Localizability(LocalizationCategory.Button)] public abstract class ButtonBase : ContentControl, ICommandSource { #region Constructors static ButtonBase() { EventManager.RegisterClassHandler(typeof(ButtonBase), AccessKeyManager.AccessKeyPressedEvent, new AccessKeyPressedEventHandler(OnAccessKeyPressed)); KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(typeof(ButtonBase), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); // Disable IME on button. // - key typing should not be eaten by IME. // - when the button has a focus, IME's disabled status should be indicated as // grayed buttons on the language bar. InputMethod.IsInputMethodEnabledProperty.OverrideMetadata(typeof(ButtonBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits)); } ////// Default ButtonBase constructor /// ////// Automatic determination of current Dispatcher. Use alternative constructor /// that accepts a Dispatcher for best performance. /// protected ButtonBase() : base() { } #endregion #region Virtual methods ////// This virtual method is called when button is clicked and it raises the Click event /// protected virtual void OnClick() { RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent, this); RaiseEvent(newEvent); MS.Internal.Commands.CommandHelpers.ExecuteCommandSource(this); } ////// This method is invoked when the IsPressed property changes. /// /// DependencyPropertyChangedEventArgs. protected virtual void OnIsPressedChanged(DependencyPropertyChangedEventArgs e) { } #endregion Virtual methods #region Private helpers private bool IsInMainFocusScope { get { Visual focusScope = FocusManager.GetFocusScope(this) as Visual; return focusScope == null || VisualTreeHelper.GetParent(focusScope) == null; } } ////// This method is called when button is clicked via IInvokeProvider. /// internal void AutomationButtonBaseClick() { OnClick(); } private static bool IsValidClickMode(object o) { ClickMode value = (ClickMode)o; return value == ClickMode.Press || value == ClickMode.Release || value == ClickMode.Hover; } ////// Override for protected internal override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { base.OnRenderSizeChanged(sizeInfo); // *** Workaround *** // We need OnMouseRealyOver Property here // // There is a problem when Button is capturing the Mouse and resizing untill the mouse fall of the Button // During that time, Button and Mouse didn't really move. However, we need to update the IsPressed property // because mouse is no longer over the button. // We migth need a new property called *** IsMouseReallyOver *** property, so we can update IsPressed when // it's changed. (Can't use IsMouseOver or IsMouseDirectlyOver 'coz once Mouse is captured, they're alway 'true'. // // Update IsPressed property if (IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed) && !IsSpaceKeyDown) { // At this point, RenderSize is not updated. We must use finalSize instead. UpdateIsPressed(); } } ////// /// Called when IsPressedProperty is changed on "d." /// private static void OnIsPressedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ButtonBase ctrl = (ButtonBase)d; ctrl.OnIsPressedChanged(e); } private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e) { if (!e.Handled && e.Scope == null && e.Target == null) { e.Target = (UIElement)sender; } } private void UpdateIsPressed() { Point pos = Mouse.PrimaryDevice.GetPosition(this); if ((pos.X >= 0) && (pos.X <= ActualWidth) && (pos.Y >= 0) && (pos.Y <= ActualHeight)) { if (!IsPressed) { SetIsPressed(true); } } else if (IsPressed) { SetIsPressed(false); } } #endregion Private helpers #region Properties and Events ////// Event correspond to left mouse button click /// public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase)); ////// Add / Remove ClickEvent handler /// [Category("Behavior")] public event RoutedEventHandler Click { add { AddHandler(ClickEvent, value); } remove { RemoveHandler(ClickEvent, value); } } ////// The DependencyProperty for RoutedCommand /// [CommonDependencyProperty] public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(ButtonBase), new FrameworkPropertyMetadata((ICommand)null, new PropertyChangedCallback(OnCommandChanged))); ////// The DependencyProperty for the CommandParameter /// [CommonDependencyProperty] public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register( "CommandParameter", typeof(object), typeof(ButtonBase), new FrameworkPropertyMetadata((object) null)); ////// The DependencyProperty for Target property /// Flags: None /// Default Value: null /// [CommonDependencyProperty] public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register( "CommandTarget", typeof(IInputElement), typeof(ButtonBase), new FrameworkPropertyMetadata((IInputElement)null)); ////// The key needed set a read-only property. /// internal static readonly DependencyPropertyKey IsPressedPropertyKey = DependencyProperty.RegisterReadOnly( "IsPressed", typeof(bool), typeof(ButtonBase), new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, new PropertyChangedCallback(OnIsPressedChanged))); ////// The DependencyProperty for the IsPressed property. /// Flags: None /// Default Value: false /// [CommonDependencyProperty] public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty; ////// IsPressed is the state of a button indicates that left mouse button is pressed or space key is pressed over the button. /// [Browsable(false), Category("Appearance"), ReadOnly(true)] public bool IsPressed { get { return (bool) GetValue(IsPressedProperty); } protected set { SetValue(IsPressedPropertyKey, BooleanBoxes.Box(value)); } } private void SetIsPressed(bool pressed) { if (pressed) { SetValue(IsPressedPropertyKey, BooleanBoxes.Box(pressed)); } else { ClearValue(IsPressedPropertyKey); } } ////// Get or set the Command property /// [Bindable(true), Category("Action")] [Localizability(LocalizationCategory.NeverLocalize)] public ICommand Command { get { return (ICommand) GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ButtonBase b = (ButtonBase)d; b.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue); } private void OnCommandChanged(ICommand oldCommand, ICommand newCommand) { if (oldCommand != null) { UnhookCommand(oldCommand); } if (newCommand != null) { HookCommand(newCommand); } } private void UnhookCommand(ICommand command) { EventHandler handler = CanExecuteChangedHandler.GetValue(this); if (handler != null) { command.CanExecuteChanged -= handler; CanExecuteChangedHandler.ClearValue(this); } UpdateCanExecute(); } private void HookCommand(ICommand command) { EventHandler handler = new EventHandler(OnCanExecuteChanged); CanExecuteChangedHandler.SetValue(this, handler); command.CanExecuteChanged += handler; UpdateCanExecute(); } private void OnCanExecuteChanged(object sender, EventArgs e) { UpdateCanExecute(); } private void UpdateCanExecute() { if (Command != null) { CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this); } else { CanExecute = true; } } ////// Fetches the value of the IsEnabled property /// ////// The reason this property is overridden is so that Button /// can infuse the value for CanExecute into it. /// protected override bool IsEnabledCore { get { return base.IsEnabledCore && CanExecute; } } ////// Reflects the parameter to pass to the CommandProperty upon execution. /// [Bindable(true), Category("Action")] [Localizability(LocalizationCategory.NeverLocalize)] public object CommandParameter { get { return GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } ////// The target element on which to fire the command. /// [Bindable(true), Category("Action")] public IInputElement CommandTarget { get { return (IInputElement)GetValue(CommandTargetProperty); } set { SetValue(CommandTargetProperty, value); } } ////// The DependencyProperty for the ClickMode property. /// Flags: None /// Default Value: ClickMode.Release /// public static readonly DependencyProperty ClickModeProperty = DependencyProperty.Register( "ClickMode", typeof(ClickMode), typeof(ButtonBase), new FrameworkPropertyMetadata(ClickMode.Release), new ValidateValueCallback(IsValidClickMode)); ////// ClickMode specify when the Click event should fire /// [Bindable(true), Category("Behavior")] public ClickMode ClickMode { get { return (ClickMode) GetValue(ClickModeProperty); } set { SetValue(ClickModeProperty, value); } } #endregion Properties and Events #region Override methods ////// This is the method that responds to the MouseButtonEvent event. /// /// Event arguments protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { // Ignore when in hover-click mode. if (ClickMode != ClickMode.Hover) { e.Handled = true; // Always set focus on itself // In case ButtonBase is inside a nested focus scope we should restore the focus OnLostMouseCapture Focus(); // It is possible that the mouse state could have changed during all of // the call-outs that have happened so far. if (e.ButtonState == MouseButtonState.Pressed) { // Capture the mouse, and make sure we got it. // WARNING: callout CaptureMouse(); if (IsMouseCaptured) { // Though we have already checked this state, our call to CaptureMouse // could also end up changing the state, so we check it again. if (e.ButtonState == MouseButtonState.Pressed) { if (!IsPressed) { SetIsPressed(true); } } else { // Release capture since we decided not to press the button. ReleaseMouseCapture(); } } } if (ClickMode == ClickMode.Press) { bool exceptionThrown = true; try { OnClick(); exceptionThrown = false; } finally { if (exceptionThrown) { // Cleanup the buttonbase state SetIsPressed(false); ReleaseMouseCapture(); } } } } base.OnMouseLeftButtonDown(e); } ////// This is the method that responds to the MouseButtonEvent event. /// /// Event arguments protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { // Ignore when in hover-click mode. if (ClickMode != ClickMode.Hover) { e.Handled = true; bool shouldClick = !IsSpaceKeyDown && IsPressed && ClickMode == ClickMode.Release; if (IsMouseCaptured && !IsSpaceKeyDown) { ReleaseMouseCapture(); } if (shouldClick) { OnClick(); } } base.OnMouseLeftButtonUp(e); } ////// This is the method that responds to the MouseEvent event. /// /// Event arguments protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if ((ClickMode != ClickMode.Hover) && ((IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed) && !IsSpaceKeyDown))) { UpdateIsPressed(); e.Handled = true; } } ////// Called when this element loses mouse capture. /// /// protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); if ((e.OriginalSource == this) && (ClickMode != ClickMode.Hover) && !IsSpaceKeyDown) { // If we are inside a nested focus scope - we should restore the focus to the main focus scope // This will cover the scenarios like ToolBar buttons if (IsKeyboardFocused && !IsInMainFocusScope) Keyboard.Focus(null); // When we lose capture, the button should not look pressed anymore // -- unless the spacebar is still down, in which case we are still pressed. SetIsPressed(false); } } ////// An event reporting the mouse entered this element. /// /// Event arguments protected override void OnMouseEnter(MouseEventArgs e) { base.OnMouseEnter(e); if (HandleIsMouseOverChanged()) { e.Handled = true; } } ////// An event reporting the mouse left this element. /// /// Event arguments protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); if (HandleIsMouseOverChanged()) { e.Handled = true; } } ////// An event reporting that the IsMouseOver property changed. /// private bool HandleIsMouseOverChanged() { if (ClickMode == ClickMode.Hover) { if (IsMouseOver) { // Hovering over the button will click in the OnHover click mode SetIsPressed(true); OnClick(); } else { SetIsPressed(false); } return true; } return false; } ////// This is the method that responds to the KeyDown event. /// /// Event arguments protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (ClickMode == ClickMode.Hover) { // Ignore when in hover-click mode. return; } if (e.Key == Key.Space) { // Alt+Space should bring up system menu, we shouldn't handle it. if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Alt)) != ModifierKeys.Alt) { if ((!IsMouseCaptured) && (e.OriginalSource == this)) { IsSpaceKeyDown = true; SetIsPressed(true); CaptureMouse(); if (ClickMode == ClickMode.Press) { OnClick(); } e.Handled = true; } } } else if (e.Key == Key.Enter && (bool)GetValue(KeyboardNavigation.AcceptsReturnProperty)) { if (e.OriginalSource == this) { IsSpaceKeyDown = false; SetIsPressed(false); if (IsMouseCaptured) { ReleaseMouseCapture(); } OnClick(); e.Handled = true; } } else { // On any other key we set IsPressed to false only if Space key is pressed if (IsSpaceKeyDown) { SetIsPressed(false); IsSpaceKeyDown = false; if (IsMouseCaptured) { ReleaseMouseCapture(); } } } } ////// This is the method that responds to the KeyUp event. /// /// Event arguments protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (ClickMode == ClickMode.Hover) { // Ignore when in hover-click mode. return; } if ((e.Key == Key.Space) && IsSpaceKeyDown) { // Alt+Space should bring up system menu, we shouldn't handle it. if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Alt)) != ModifierKeys.Alt) { IsSpaceKeyDown = false; if (GetMouseLeftButtonReleased()) { bool shouldClick = IsPressed && ClickMode == ClickMode.Release; // Release mouse capture if left mouse button is not pressed if (IsMouseCaptured) { // OnLostMouseCapture set IsPressed to false ReleaseMouseCapture(); } if (shouldClick) OnClick(); } else { // IsPressed state is updated only if mouse is captured (bugfix 919349) if (IsMouseCaptured) UpdateIsPressed(); } e.Handled = true; } } } ////// An event announcing that the keyboard is no longer focused /// /// Event arguments protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { base.OnLostKeyboardFocus(e); if (ClickMode == ClickMode.Hover) { // Ignore when in hover-click mode. return; } if (e.OriginalSource == this) { if (IsPressed) { SetIsPressed(false); } if (IsMouseCaptured) ReleaseMouseCapture(); IsSpaceKeyDown = false; } } ////// The Access key for this control was invoked. /// protected override void OnAccessKey(AccessKeyEventArgs e) { if (e.IsMultiple) { base.OnAccessKey(e); } else { // Don't call the base b/c we don't want to take focus OnClick(); } } ////// Critical - calling critical InputManager.Current /// Safe - InputManager.Current is not exposed and used temporary to determine the mouse state /// [SecurityCritical, SecurityTreatAsSafe] private bool GetMouseLeftButtonReleased() { return InputManager.Current.PrimaryMouseDevice.LeftButton == MouseButtonState.Released; } private bool IsSpaceKeyDown { get { return ReadControlFlag(ControlBoolFlags.IsSpaceKeyDown); } set { WriteControlFlag(ControlBoolFlags.IsSpaceKeyDown, value); } } private bool CanExecute { get { return !ReadControlFlag(ControlBoolFlags.CommandDisabled); } set { if (value != CanExecute) { WriteControlFlag(ControlBoolFlags.CommandDisabled, !value); CoerceValue(IsEnabledProperty); } } } #endregion #region Data private static readonly UncommonFieldCanExecuteChangedHandler = new UncommonField (); #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections; using System.ComponentModel; using System.Windows.Threading; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows; using System.Windows.Input; using System.Windows.Media; using MS.Utility; using MS.Internal.KnownBoxes; using MS.Internal.PresentationFramework; using System.Diagnostics; using System.Security; using System.Security.Permissions; namespace System.Windows.Controls.Primitives { /// /// The base class for all buttons /// ////// ButtonBase adds IsPressed state, Click event, and Invoke features to a ContentControl. /// [DefaultEvent("Click")] [Localizability(LocalizationCategory.Button)] public abstract class ButtonBase : ContentControl, ICommandSource { #region Constructors static ButtonBase() { EventManager.RegisterClassHandler(typeof(ButtonBase), AccessKeyManager.AccessKeyPressedEvent, new AccessKeyPressedEventHandler(OnAccessKeyPressed)); KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(typeof(ButtonBase), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); // Disable IME on button. // - key typing should not be eaten by IME. // - when the button has a focus, IME's disabled status should be indicated as // grayed buttons on the language bar. InputMethod.IsInputMethodEnabledProperty.OverrideMetadata(typeof(ButtonBase), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits)); } ////// Default ButtonBase constructor /// ////// Automatic determination of current Dispatcher. Use alternative constructor /// that accepts a Dispatcher for best performance. /// protected ButtonBase() : base() { } #endregion #region Virtual methods ////// This virtual method is called when button is clicked and it raises the Click event /// protected virtual void OnClick() { RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent, this); RaiseEvent(newEvent); MS.Internal.Commands.CommandHelpers.ExecuteCommandSource(this); } ////// This method is invoked when the IsPressed property changes. /// /// DependencyPropertyChangedEventArgs. protected virtual void OnIsPressedChanged(DependencyPropertyChangedEventArgs e) { } #endregion Virtual methods #region Private helpers private bool IsInMainFocusScope { get { Visual focusScope = FocusManager.GetFocusScope(this) as Visual; return focusScope == null || VisualTreeHelper.GetParent(focusScope) == null; } } ////// This method is called when button is clicked via IInvokeProvider. /// internal void AutomationButtonBaseClick() { OnClick(); } private static bool IsValidClickMode(object o) { ClickMode value = (ClickMode)o; return value == ClickMode.Press || value == ClickMode.Release || value == ClickMode.Hover; } ////// Override for protected internal override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { base.OnRenderSizeChanged(sizeInfo); // *** Workaround *** // We need OnMouseRealyOver Property here // // There is a problem when Button is capturing the Mouse and resizing untill the mouse fall of the Button // During that time, Button and Mouse didn't really move. However, we need to update the IsPressed property // because mouse is no longer over the button. // We migth need a new property called *** IsMouseReallyOver *** property, so we can update IsPressed when // it's changed. (Can't use IsMouseOver or IsMouseDirectlyOver 'coz once Mouse is captured, they're alway 'true'. // // Update IsPressed property if (IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed) && !IsSpaceKeyDown) { // At this point, RenderSize is not updated. We must use finalSize instead. UpdateIsPressed(); } } ////// /// Called when IsPressedProperty is changed on "d." /// private static void OnIsPressedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ButtonBase ctrl = (ButtonBase)d; ctrl.OnIsPressedChanged(e); } private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e) { if (!e.Handled && e.Scope == null && e.Target == null) { e.Target = (UIElement)sender; } } private void UpdateIsPressed() { Point pos = Mouse.PrimaryDevice.GetPosition(this); if ((pos.X >= 0) && (pos.X <= ActualWidth) && (pos.Y >= 0) && (pos.Y <= ActualHeight)) { if (!IsPressed) { SetIsPressed(true); } } else if (IsPressed) { SetIsPressed(false); } } #endregion Private helpers #region Properties and Events ////// Event correspond to left mouse button click /// public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase)); ////// Add / Remove ClickEvent handler /// [Category("Behavior")] public event RoutedEventHandler Click { add { AddHandler(ClickEvent, value); } remove { RemoveHandler(ClickEvent, value); } } ////// The DependencyProperty for RoutedCommand /// [CommonDependencyProperty] public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(ButtonBase), new FrameworkPropertyMetadata((ICommand)null, new PropertyChangedCallback(OnCommandChanged))); ////// The DependencyProperty for the CommandParameter /// [CommonDependencyProperty] public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register( "CommandParameter", typeof(object), typeof(ButtonBase), new FrameworkPropertyMetadata((object) null)); ////// The DependencyProperty for Target property /// Flags: None /// Default Value: null /// [CommonDependencyProperty] public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register( "CommandTarget", typeof(IInputElement), typeof(ButtonBase), new FrameworkPropertyMetadata((IInputElement)null)); ////// The key needed set a read-only property. /// internal static readonly DependencyPropertyKey IsPressedPropertyKey = DependencyProperty.RegisterReadOnly( "IsPressed", typeof(bool), typeof(ButtonBase), new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, new PropertyChangedCallback(OnIsPressedChanged))); ////// The DependencyProperty for the IsPressed property. /// Flags: None /// Default Value: false /// [CommonDependencyProperty] public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty; ////// IsPressed is the state of a button indicates that left mouse button is pressed or space key is pressed over the button. /// [Browsable(false), Category("Appearance"), ReadOnly(true)] public bool IsPressed { get { return (bool) GetValue(IsPressedProperty); } protected set { SetValue(IsPressedPropertyKey, BooleanBoxes.Box(value)); } } private void SetIsPressed(bool pressed) { if (pressed) { SetValue(IsPressedPropertyKey, BooleanBoxes.Box(pressed)); } else { ClearValue(IsPressedPropertyKey); } } ////// Get or set the Command property /// [Bindable(true), Category("Action")] [Localizability(LocalizationCategory.NeverLocalize)] public ICommand Command { get { return (ICommand) GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ButtonBase b = (ButtonBase)d; b.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue); } private void OnCommandChanged(ICommand oldCommand, ICommand newCommand) { if (oldCommand != null) { UnhookCommand(oldCommand); } if (newCommand != null) { HookCommand(newCommand); } } private void UnhookCommand(ICommand command) { EventHandler handler = CanExecuteChangedHandler.GetValue(this); if (handler != null) { command.CanExecuteChanged -= handler; CanExecuteChangedHandler.ClearValue(this); } UpdateCanExecute(); } private void HookCommand(ICommand command) { EventHandler handler = new EventHandler(OnCanExecuteChanged); CanExecuteChangedHandler.SetValue(this, handler); command.CanExecuteChanged += handler; UpdateCanExecute(); } private void OnCanExecuteChanged(object sender, EventArgs e) { UpdateCanExecute(); } private void UpdateCanExecute() { if (Command != null) { CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this); } else { CanExecute = true; } } ////// Fetches the value of the IsEnabled property /// ////// The reason this property is overridden is so that Button /// can infuse the value for CanExecute into it. /// protected override bool IsEnabledCore { get { return base.IsEnabledCore && CanExecute; } } ////// Reflects the parameter to pass to the CommandProperty upon execution. /// [Bindable(true), Category("Action")] [Localizability(LocalizationCategory.NeverLocalize)] public object CommandParameter { get { return GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } ////// The target element on which to fire the command. /// [Bindable(true), Category("Action")] public IInputElement CommandTarget { get { return (IInputElement)GetValue(CommandTargetProperty); } set { SetValue(CommandTargetProperty, value); } } ////// The DependencyProperty for the ClickMode property. /// Flags: None /// Default Value: ClickMode.Release /// public static readonly DependencyProperty ClickModeProperty = DependencyProperty.Register( "ClickMode", typeof(ClickMode), typeof(ButtonBase), new FrameworkPropertyMetadata(ClickMode.Release), new ValidateValueCallback(IsValidClickMode)); ////// ClickMode specify when the Click event should fire /// [Bindable(true), Category("Behavior")] public ClickMode ClickMode { get { return (ClickMode) GetValue(ClickModeProperty); } set { SetValue(ClickModeProperty, value); } } #endregion Properties and Events #region Override methods ////// This is the method that responds to the MouseButtonEvent event. /// /// Event arguments protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { // Ignore when in hover-click mode. if (ClickMode != ClickMode.Hover) { e.Handled = true; // Always set focus on itself // In case ButtonBase is inside a nested focus scope we should restore the focus OnLostMouseCapture Focus(); // It is possible that the mouse state could have changed during all of // the call-outs that have happened so far. if (e.ButtonState == MouseButtonState.Pressed) { // Capture the mouse, and make sure we got it. // WARNING: callout CaptureMouse(); if (IsMouseCaptured) { // Though we have already checked this state, our call to CaptureMouse // could also end up changing the state, so we check it again. if (e.ButtonState == MouseButtonState.Pressed) { if (!IsPressed) { SetIsPressed(true); } } else { // Release capture since we decided not to press the button. ReleaseMouseCapture(); } } } if (ClickMode == ClickMode.Press) { bool exceptionThrown = true; try { OnClick(); exceptionThrown = false; } finally { if (exceptionThrown) { // Cleanup the buttonbase state SetIsPressed(false); ReleaseMouseCapture(); } } } } base.OnMouseLeftButtonDown(e); } ////// This is the method that responds to the MouseButtonEvent event. /// /// Event arguments protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { // Ignore when in hover-click mode. if (ClickMode != ClickMode.Hover) { e.Handled = true; bool shouldClick = !IsSpaceKeyDown && IsPressed && ClickMode == ClickMode.Release; if (IsMouseCaptured && !IsSpaceKeyDown) { ReleaseMouseCapture(); } if (shouldClick) { OnClick(); } } base.OnMouseLeftButtonUp(e); } ////// This is the method that responds to the MouseEvent event. /// /// Event arguments protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if ((ClickMode != ClickMode.Hover) && ((IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed) && !IsSpaceKeyDown))) { UpdateIsPressed(); e.Handled = true; } } ////// Called when this element loses mouse capture. /// /// protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); if ((e.OriginalSource == this) && (ClickMode != ClickMode.Hover) && !IsSpaceKeyDown) { // If we are inside a nested focus scope - we should restore the focus to the main focus scope // This will cover the scenarios like ToolBar buttons if (IsKeyboardFocused && !IsInMainFocusScope) Keyboard.Focus(null); // When we lose capture, the button should not look pressed anymore // -- unless the spacebar is still down, in which case we are still pressed. SetIsPressed(false); } } ////// An event reporting the mouse entered this element. /// /// Event arguments protected override void OnMouseEnter(MouseEventArgs e) { base.OnMouseEnter(e); if (HandleIsMouseOverChanged()) { e.Handled = true; } } ////// An event reporting the mouse left this element. /// /// Event arguments protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); if (HandleIsMouseOverChanged()) { e.Handled = true; } } ////// An event reporting that the IsMouseOver property changed. /// private bool HandleIsMouseOverChanged() { if (ClickMode == ClickMode.Hover) { if (IsMouseOver) { // Hovering over the button will click in the OnHover click mode SetIsPressed(true); OnClick(); } else { SetIsPressed(false); } return true; } return false; } ////// This is the method that responds to the KeyDown event. /// /// Event arguments protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (ClickMode == ClickMode.Hover) { // Ignore when in hover-click mode. return; } if (e.Key == Key.Space) { // Alt+Space should bring up system menu, we shouldn't handle it. if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Alt)) != ModifierKeys.Alt) { if ((!IsMouseCaptured) && (e.OriginalSource == this)) { IsSpaceKeyDown = true; SetIsPressed(true); CaptureMouse(); if (ClickMode == ClickMode.Press) { OnClick(); } e.Handled = true; } } } else if (e.Key == Key.Enter && (bool)GetValue(KeyboardNavigation.AcceptsReturnProperty)) { if (e.OriginalSource == this) { IsSpaceKeyDown = false; SetIsPressed(false); if (IsMouseCaptured) { ReleaseMouseCapture(); } OnClick(); e.Handled = true; } } else { // On any other key we set IsPressed to false only if Space key is pressed if (IsSpaceKeyDown) { SetIsPressed(false); IsSpaceKeyDown = false; if (IsMouseCaptured) { ReleaseMouseCapture(); } } } } ////// This is the method that responds to the KeyUp event. /// /// Event arguments protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (ClickMode == ClickMode.Hover) { // Ignore when in hover-click mode. return; } if ((e.Key == Key.Space) && IsSpaceKeyDown) { // Alt+Space should bring up system menu, we shouldn't handle it. if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Alt)) != ModifierKeys.Alt) { IsSpaceKeyDown = false; if (GetMouseLeftButtonReleased()) { bool shouldClick = IsPressed && ClickMode == ClickMode.Release; // Release mouse capture if left mouse button is not pressed if (IsMouseCaptured) { // OnLostMouseCapture set IsPressed to false ReleaseMouseCapture(); } if (shouldClick) OnClick(); } else { // IsPressed state is updated only if mouse is captured (bugfix 919349) if (IsMouseCaptured) UpdateIsPressed(); } e.Handled = true; } } } ////// An event announcing that the keyboard is no longer focused /// /// Event arguments protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { base.OnLostKeyboardFocus(e); if (ClickMode == ClickMode.Hover) { // Ignore when in hover-click mode. return; } if (e.OriginalSource == this) { if (IsPressed) { SetIsPressed(false); } if (IsMouseCaptured) ReleaseMouseCapture(); IsSpaceKeyDown = false; } } ////// The Access key for this control was invoked. /// protected override void OnAccessKey(AccessKeyEventArgs e) { if (e.IsMultiple) { base.OnAccessKey(e); } else { // Don't call the base b/c we don't want to take focus OnClick(); } } ////// Critical - calling critical InputManager.Current /// Safe - InputManager.Current is not exposed and used temporary to determine the mouse state /// [SecurityCritical, SecurityTreatAsSafe] private bool GetMouseLeftButtonReleased() { return InputManager.Current.PrimaryMouseDevice.LeftButton == MouseButtonState.Released; } private bool IsSpaceKeyDown { get { return ReadControlFlag(ControlBoolFlags.IsSpaceKeyDown); } set { WriteControlFlag(ControlBoolFlags.IsSpaceKeyDown, value); } } private bool CanExecute { get { return !ReadControlFlag(ControlBoolFlags.CommandDisabled); } set { if (value != CanExecute) { WriteControlFlag(ControlBoolFlags.CommandDisabled, !value); CoerceValue(IsEnabledProperty); } } } #endregion #region Data private static readonly UncommonFieldCanExecuteChangedHandler = new UncommonField (); #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ComplexBindingPropertiesAttribute.cs
- RenderingEventArgs.cs
- FixedSOMTextRun.cs
- X509SubjectKeyIdentifierClause.cs
- FixedSOMSemanticBox.cs
- DrawingAttributes.cs
- WindowInteropHelper.cs
- EventLogPermissionAttribute.cs
- LinearQuaternionKeyFrame.cs
- TiffBitmapEncoder.cs
- WorkflowMessageEventHandler.cs
- ChildTable.cs
- RIPEMD160.cs
- ImageBrush.cs
- DataStreams.cs
- odbcmetadatacollectionnames.cs
- Cursors.cs
- DbProviderSpecificTypePropertyAttribute.cs
- WindowsListViewItem.cs
- COM2PropertyDescriptor.cs
- OneToOneMappingSerializer.cs
- XmlNodeChangedEventArgs.cs
- ReadWriteSpinLock.cs
- SqlDataSourceCommandParser.cs
- complextypematerializer.cs
- Timeline.cs
- XmlCodeExporter.cs
- PrintPreviewGraphics.cs
- DataBindingCollection.cs
- SettingsPropertyIsReadOnlyException.cs
- ControlPersister.cs
- KeyedCollection.cs
- RawUIStateInputReport.cs
- AutoSizeToolBoxItem.cs
- CalendarAutoFormatDialog.cs
- _NegotiateClient.cs
- DefaultPropertyAttribute.cs
- XmlSchemaAny.cs
- _NativeSSPI.cs
- GuidTagList.cs
- ResourceCategoryAttribute.cs
- BypassElement.cs
- sqlinternaltransaction.cs
- InputMethodStateTypeInfo.cs
- HWStack.cs
- TreeNodeBinding.cs
- CodeStatementCollection.cs
- Boolean.cs
- SafeProcessHandle.cs
- ServiceRouteHandler.cs
- DataSourceXmlClassAttribute.cs
- HttpException.cs
- XmlHierarchicalDataSourceView.cs
- HttpListenerException.cs
- TranslateTransform3D.cs
- DataSourceCache.cs
- TextEndOfParagraph.cs
- OdbcUtils.cs
- ItemCheckedEvent.cs
- AsmxEndpointPickerExtension.cs
- CompiledScopeCriteria.cs
- ContractComponent.cs
- JsonWriter.cs
- TypeForwardedToAttribute.cs
- NavigateEvent.cs
- Matrix3DConverter.cs
- Setter.cs
- StorageEntityContainerMapping.cs
- DataGridViewSelectedCellCollection.cs
- ValidationError.cs
- MappingModelBuildProvider.cs
- OdbcUtils.cs
- SessionStateUtil.cs
- SocketException.cs
- TimeSpanSecondsOrInfiniteConverter.cs
- Compiler.cs
- KeySplineConverter.cs
- SplashScreen.cs
- Environment.cs
- ProjectionAnalyzer.cs
- FontWeightConverter.cs
- EntityUtil.cs
- InternalBase.cs
- DesignerActionItemCollection.cs
- CheckBoxAutomationPeer.cs
- XmlWriterTraceListener.cs
- HttpCachePolicy.cs
- CryptoStream.cs
- UnmanagedMemoryStreamWrapper.cs
- TimersDescriptionAttribute.cs
- SqlBinder.cs
- QueueAccessMode.cs
- DataGridColumnEventArgs.cs
- SQLString.cs
- XmlComplianceUtil.cs
- StrongNamePublicKeyBlob.cs
- SafeCryptContextHandle.cs
- DbParameterCollectionHelper.cs
- ParameterElementCollection.cs
- CalendarModeChangedEventArgs.cs