自定义 WPF 控件可以实现 IsDefault 属性吗

发布于 2024-08-29 03:52:36 字数 6518 浏览 12 评论 0原文

我有一个不是从 Button 派生的自定义按钮控件。我是否可以实现相当于 IsDefault 以便调用与我的控件关联的命令。我希望这是一个附加属性,我可以将其添加到任何控件中,但据我所知,似乎并非如此。如果我的控件不是从 Button 派生的,或者至少有一个合理的解决方法,我会运气不好吗?

更新: 我只是用 Reflector 看了一下 Button 下面是如何完成此操作的,我必须说这不是我见过的最不言自明的代码。看起来至少有 3 个依赖属性(一些自定义类型)只是为了处理默认 Button 的概念。由于似乎没有现有的方法来借用 IsDefault 功能,我想我必须缩小我想要实现的目标,以便我至少可以获得默认焦点并访问关键处理来工作并忽略Button.IsDefault 实现中涉及的复杂性。

更新: 添加了以下代码示例,显示了我尝试 itowlson 的建议的失败尝试。

MyButton.xaml

<UserControl x:Class="IsDefault.MyButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             Height="28"
             Width="117">

    <Grid>
        <Button Click="Button_Click">
            <Button.Template>
                <ControlTemplate>
                    <Border BorderThickness="2"
                            CornerRadius="12"
                            Background="DarkSlateBlue">
                        <TextBlock Foreground="WhiteSmoke"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center">Some Text</TextBlock>
                    </Border>
                </ControlTemplate>
            </Button.Template>
        </Button>
    </Grid>
</UserControl>

MyButton.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace IsDefault
{
    /// <summary>
    /// Interaction logic for MyButton.xaml
    /// </summary>
    public partial class MyButton : UserControl
    {


        // Provide CLR accessors for the event
        public event RoutedEventHandler Click
        {
            add { AddHandler(ClickEvent, value); }
            remove { RemoveHandler(ClickEvent, value); }
        }

        // Using a RoutedEvent
        public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
            "Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButton));



        public bool IsDefault
        {
            get { return (bool)GetValue(IsDefaultProperty); }
            set { SetValue(IsDefaultProperty, value); }
        }

        public static readonly DependencyProperty IsDefaultProperty =
            DependencyProperty.Register(
                "IsDefault", 
                typeof(bool),
                typeof(MyButton),
                new PropertyMetadata(false, IsDefault_PropertyChangedCallback, null));


        public MyButton()
        {
            InitializeComponent();
        }

        protected override void OnAccessKey(AccessKeyEventArgs e)
        {
            base.OnAccessKey(e);

            if (e.Key == "\r")
            {
                if (e.IsMultiple)
                {
                    // There are multiple controls that are currently handling the Enter key
                    MessageBox.Show("there are multiple controls handling the Enter key.");
                }
                else
                {
                    RaiseEvent(new RoutedEventArgs(ClickEvent, this));
                }
            }
        }

        private static void IsDefault_PropertyChangedCallback(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var button = d as MyButton;

            var isDefault = (bool)e.NewValue;

            if (isDefault)
            {
                AccessKeyManager.Register("\r", button);
            }
            else
            {
                AccessKeyManager.Unregister("\r", button);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(ClickEvent));
        }
    }
}

MainWindow.xaml

<Window x:Class="IsDefault.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:my="clr-namespace:IsDefault">
    <Grid>
        <Button Content="Button"
                Height="23"
                HorizontalAlignment="Left"
                Margin="224,24,0,0"
                Name="button1"
                VerticalAlignment="Top"
                Width="75" />
        <TextBox Height="23"
                 HorizontalAlignment="Left"
                 Margin="208,94,0,0"
                 Name="textBox1"
                 VerticalAlignment="Top"
                 Width="120" />
        <my:MyButton Height="28"
                     HorizontalAlignment="Left"
                     Margin="232,154,0,0"
                     x:Name="myButton1"
                     VerticalAlignment="Top"
                     Width="117"
                     Click="myButton1_Click"
                     IsDefault="True"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace IsDefault
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void myButton1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("My button was clicked, yay!");
        }
    }
}

I have a custom button control that does not derive from Button. Is it possible for me to implement the equivalent of IsDefault so that the command associated with my control will be invoked. I was hoping that this was an attached property that I could add to any control but as far as I can tell it doesn't seem to be. Am I out of luck if my control does not derive from Button or is there at least a reasonable workaround?

UPDATE:
I just took a peek with reflector at how this is being done underneath for Button and I must say it isn't the most self explanitory code I've seen. It appears that there are at least 3 dependency properties a few custom types just for the purpose of handling the concept of a Button being default. Since there doesn't seem to be an existing way to borrow the IsDefault functionality I suppose I'll have to narrow down what I'm trying to achieve so that I can at least get default focus and access key handling to work and just ignore the complexity invloved in the Button.IsDefault implementation.

UPDATE:
Added the following code example showing my uncessful attempt at trying itowlson's suggestions.

MyButton.xaml

<UserControl x:Class="IsDefault.MyButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             Height="28"
             Width="117">

    <Grid>
        <Button Click="Button_Click">
            <Button.Template>
                <ControlTemplate>
                    <Border BorderThickness="2"
                            CornerRadius="12"
                            Background="DarkSlateBlue">
                        <TextBlock Foreground="WhiteSmoke"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center">Some Text</TextBlock>
                    </Border>
                </ControlTemplate>
            </Button.Template>
        </Button>
    </Grid>
</UserControl>

MyButton.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace IsDefault
{
    /// <summary>
    /// Interaction logic for MyButton.xaml
    /// </summary>
    public partial class MyButton : UserControl
    {


        // Provide CLR accessors for the event
        public event RoutedEventHandler Click
        {
            add { AddHandler(ClickEvent, value); }
            remove { RemoveHandler(ClickEvent, value); }
        }

        // Using a RoutedEvent
        public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
            "Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButton));



        public bool IsDefault
        {
            get { return (bool)GetValue(IsDefaultProperty); }
            set { SetValue(IsDefaultProperty, value); }
        }

        public static readonly DependencyProperty IsDefaultProperty =
            DependencyProperty.Register(
                "IsDefault", 
                typeof(bool),
                typeof(MyButton),
                new PropertyMetadata(false, IsDefault_PropertyChangedCallback, null));


        public MyButton()
        {
            InitializeComponent();
        }

        protected override void OnAccessKey(AccessKeyEventArgs e)
        {
            base.OnAccessKey(e);

            if (e.Key == "\r")
            {
                if (e.IsMultiple)
                {
                    // There are multiple controls that are currently handling the Enter key
                    MessageBox.Show("there are multiple controls handling the Enter key.");
                }
                else
                {
                    RaiseEvent(new RoutedEventArgs(ClickEvent, this));
                }
            }
        }

        private static void IsDefault_PropertyChangedCallback(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var button = d as MyButton;

            var isDefault = (bool)e.NewValue;

            if (isDefault)
            {
                AccessKeyManager.Register("\r", button);
            }
            else
            {
                AccessKeyManager.Unregister("\r", button);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(ClickEvent));
        }
    }
}

MainWindow.xaml

<Window x:Class="IsDefault.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:my="clr-namespace:IsDefault">
    <Grid>
        <Button Content="Button"
                Height="23"
                HorizontalAlignment="Left"
                Margin="224,24,0,0"
                Name="button1"
                VerticalAlignment="Top"
                Width="75" />
        <TextBox Height="23"
                 HorizontalAlignment="Left"
                 Margin="208,94,0,0"
                 Name="textBox1"
                 VerticalAlignment="Top"
                 Width="120" />
        <my:MyButton Height="28"
                     HorizontalAlignment="Left"
                     Margin="232,154,0,0"
                     x:Name="myButton1"
                     VerticalAlignment="Top"
                     Width="117"
                     Click="myButton1_Click"
                     IsDefault="True"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace IsDefault
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void myButton1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("My button was clicked, yay!");
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

淡莣 2024-09-05 03:52:37

设置 Button.IsDefault 所做的全部工作就是调用 AccessKeyManager.Register("\r", this) (如果设置为 false,则调用 Unregister)。 (实际上,它在焦点管理方面做了一些额外的工作,但这对您来说可能并不重要。)

因此,要自己实现类似的效果:

  • 以通常的方式创建 IsDefault 依赖属性。
  • 在 IsDefault PropertyChangedCallback 中,调用 AccessKeyManager.Register 或 AccessKeyManager.Unregister 根据到新值,传递 "\r" (Enter 字符串)作为键,并将控件实例作为元素。
  • 覆盖 OnAccessKey 以指定控件如何响应输入键。 (例如,ButtonBase 会覆盖此事件以调用 OnClick。您还可以处理 AccessKeyManager.AccessKeyPressed 附加事件,但由于您正在定义自定义控件,因此覆盖 OnAccessKey 会更简洁。)

All that setting Button.IsDefault does is call AccessKeyManager.Register("\r", this) (or Unregister if setting to false). (Actually, it does a little bit of extra work around focus management, but that's probably not crucial for you.)

So to achieve a similar effect yourself:

  • Create an IsDefault dependency property in the usual way.
  • In your IsDefault PropertyChangedCallback, call AccessKeyManager.Register or AccessKeyManager.Unregister according to the new value, passing "\r" (the Enter string) as the key and the control instance as the element.
  • Override OnAccessKey to specify how your control responds to the Enter key. (For example, ButtonBase overrides this to call OnClick. You could also handle the AccessKeyManager.AccessKeyPressed attached event, but since you are defining a custom control, overriding OnAccessKey is neater.)
蓝海似她心 2024-09-05 03:52:37

尽管这是一个较老的问题,但其他人可能仍然像我一样对答案感兴趣。所以,这是我的解决方案。它基于我发现的 Microsoft 参考源的一些逆向工程(按钮ButtonBase )。我还是 WPF 新手,所以可能不需要那么多代码,但它确实有效!

此代码向 UserControl 添加以下功能(它们似乎彼此紧密相连):

  • IsDefault
  • IsCancel
  • Command
  • Click 事件

(代码中的所有注释均由 MS 做出)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.ComponentModel;
using System.Windows.Automation.Peers;
using System.Security;
using System.Diagnostics;

[DefaultEvent("Click")]
public partial class MyButton : UserControl, ICommandSource {

#region "Private Variables"

    // Cache valid bits
    private ControlBoolFlags _ControlBoolField;

#endregion

#region "Constructors"

    static MyButton() 
    {
        EventManager.RegisterClassHandler(
            typeof(MyButton), 
            AccessKeyManager.AccessKeyPressedEvent, 
            new AccessKeyPressedEventHandler(
                OnAccessKeyPressed));

        KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(
            typeof(MyButton), 
            new FrameworkPropertyMetadata(
                true));

        // 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(MyButton), 
            new FrameworkPropertyMetadata(
                false, 
                FrameworkPropertyMetadataOptions.Inherits));
    }

#endregion

#region "AccessKey"

    private static void OnAccessKeyPressed(object sender, 
        AccessKeyPressedEventArgs e)
    {
        if (!e.Handled && e.Scope == null && e.Target == null) {
            e.Target = sender as MyButton;
        }
    }

    /// <summary>
    /// The Access key for this control was invoked.
    /// </summary>
    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();
        }
    }

#endregion

#region "Click"

    /// <summary>
    /// Event correspond to left mouse button click
    /// </summary>
    public static readonly RoutedEvent ClickEvent = 
        EventManager.RegisterRoutedEvent(
        "Click", 
        RoutingStrategy.Bubble, 
        typeof(RoutedEventHandler), 
        typeof(MyButton));

    /// <summary>
    /// Add / Remove ClickEvent handler
    /// </summary>
    [Category("Behavior")]
    public event RoutedEventHandler Click 
    {
        add { 
            AddHandler(ClickEvent, value); 
        }
        remove { 
            RemoveHandler(ClickEvent, value); 
        }
    }

    /// <summary>
    /// This virtual method is called when button is clicked and 
    /// it raises the Click event
    /// </summary>
    private void BaseOnClick() 
    {
        RoutedEventArgs locRoutedEventArgs = new RoutedEventArgs(
            MyButton.ClickEvent, 
            this);
        this.RaiseEvent(locRoutedEventArgs);
        ExecuteCommandSource(this);
    }

    /// <summary>
    /// This method is called when button is clicked.
    /// </summary>
    private void OnClick()
    {
        if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked)) {
            AutomationPeer locPeer = 
                UIElementAutomationPeer.CreatePeerForElement(this);
            if (locPeer != null) {
                locPeer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
            }
        }

        // base.OnClick should be called first. Our default command 
        // for Cancel Button to close dialog should happen after 
        // Button's click event handler has been called. 
        // If there Is excption And it Then 's a Cancel button and 
        // RoutedCommand is null, 
        // we will raise Window.DialogCancelCommand.
        try {
            BaseOnClick();
        } finally {
            // When the Button RoutedCommand is null, if it's a 
            // Cancel Button, 
            // Window.DialogCancelCommand will be the default command. 
            // Do not assign Window.DialogCancelCommand to 
            // Button.Command. 
            // If in Button click handler user nulls the Command, 
            // we still want to provide the default behavior.
            if (Command == null && IsCancel) {
                // Can't invoke Window.DialogCancelCommand directly. 
                // Have to raise event. 
                // Filed bug 936090: Commanding perf issue: can't 
                // directly invoke a command.
                ExecuteCommand(DialogCancelCommand, null, this);
            }
        }
    }

#endregion

#region "ClickMode"

    /// <summary>
    ///     The DependencyProperty for the ClickMode property.
    ///     Flags:              None
    ///     Default Value:      ClickMode.Release
    /// </summary>
    public static readonly DependencyProperty ClickModeProperty = 
        DependencyProperty.Register(
        "ClickMode", 
        typeof(ClickMode), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            ClickMode.Release), 
        new ValidateValueCallback(
            IsValidClickMode));

    /// <summary>
    ///     ClickMode specify when the Click event should fire
    /// </summary>
    [Bindable(true), Category("Behavior")]
    public ClickMode ClickMode 
    {
        get { 
            return (ClickMode)GetValue(ClickModeProperty); 
        }
        set { 
            SetValue(ClickModeProperty, value); 
        }
    }

    private static bool IsValidClickMode(object valClickMode)
    {
        ClickMode locClickMode = (ClickMode)valClickMode;
        return locClickMode == ClickMode.Press 
            || locClickMode == ClickMode.Release 
            || locClickMode == ClickMode.Hover;
    }

#endregion

#region "KeyDown"

    /// <summary>
    /// This is the method that responds to the KeyDown event.
    /// </summary>
    /// <param name="e">Event arguments</param>
    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) && 
                    (object.ReferenceEquals(e.OriginalSource, this))) {
                    IsSpaceKeyDown = true;
                    CaptureMouse();
                    if (ClickMode == ClickMode.Press) {
                        OnClick();
                    }
                    e.Handled = true;
                }
            }
        } else if (e.Key == Key.Enter 
            && Convert.ToBoolean(GetValue(KeyboardNavigation.AcceptsReturnProperty))) {

            if (object.ReferenceEquals(e.OriginalSource, this)) {
                IsSpaceKeyDown = 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) {
                IsSpaceKeyDown = false;
                if (IsMouseCaptured) {
                    ReleaseMouseCapture();
                }
            }
        }
    }

    private bool IsSpaceKeyDown 
    {
        get {
            return ReadControlFlag(ControlBoolFlags.IsSpaceKeyDown); 
        }
        set { 
            WriteControlFlag(ControlBoolFlags.IsSpaceKeyDown, value); 
        }
    }

#endregion

#region "Command"

    /// <summary>
    ///     The DependencyProperty for RoutedCommand
    /// </summary>
    [CommonDependencyProperty()]
    public static readonly DependencyProperty CommandProperty = 
        DependencyProperty.Register(
        "Command", 
        typeof(ICommand), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            (ICommand)null, 
            new PropertyChangedCallback(
                OnCommandChanged)));

    /// <summary>
    /// Get or set the Command property
    /// </summary>
    [Bindable(true), Category("Action")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public ICommand Command 
    {
        get {
            return (ICommand)GetValue(CommandProperty); 
        }
        set { 
            SetValue(CommandProperty, value); 
        }
    }

    private static void OnCommandChanged(
        DependencyObject valTarget, 
        DependencyPropertyChangedEventArgs e) 
    {
        MyButton locMyButton = valTarget as MyButton;
        if (locMyButton != null) {
            locMyButton.OnCommandChanged(
                (ICommand)e.OldValue, 
                (ICommand)e.NewValue);
        }
    }

    private void OnCommandChanged(
        ICommand valOldCommand, 
        ICommand valNewCommand) 
    {
        if (valOldCommand != null) {
            valOldCommand.CanExecuteChanged -= OnCanExecuteChanged;
        }
        if (valNewCommand != null) {
            valNewCommand.CanExecuteChanged += OnCanExecuteChanged;
        }
        UpdateCanExecute();
    }

#endregion

#region "CommandParameter"

    /// <summary>
    /// The DependencyProperty for the CommandParameter
    /// </summary>
    [CommonDependencyProperty()]
    public static readonly DependencyProperty CommandParameterProperty = 
        DependencyProperty.Register(
        "CommandParameter",
        typeof(object), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            (object)null));

    /// <summary>
    /// Reflects the parameter to pass to the CommandProperty 
    /// upon execution.
    /// </summary>
    [Bindable(true), Category("Action")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public object CommandParameter 
    {
        get { 
            return GetValue(CommandParameterProperty); 
        }
        set { 
            SetValue(CommandParameterProperty, value); 
        }
    }

#endregion

#region "CommandTarget"

    /// <summary>
    ///     The DependencyProperty for Target property
    ///     Flags:              None
    ///     Default Value:      null
    /// </summary>
    [CommonDependencyProperty()]
    public static readonly DependencyProperty CommandTargetProperty = 
        DependencyProperty.Register(
        "CommandTarget", 
        typeof(IInputElement), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            (IInputElement)null));

    /// <summary>
    ///     The target element on which to fire the command.
    /// </summary>
    [Bindable(true), Category("Action")]
    public IInputElement CommandTarget 
    {
        get { 
            return (IInputElement)GetValue(CommandTargetProperty);
        }
        set { 
            SetValue(CommandTargetProperty, value); 
        }
    }

#endregion

#region "CanExecute"

    private void OnCanExecuteChanged(object valTarget, EventArgs e) 
    {
        if (valTarget != null) {
            UpdateCanExecute();
        }
    }

    private bool CanExecute 
    {
        get { 
            return !ReadControlFlag(ControlBoolFlags.CommandDisabled);
        }
        set {
            if (value != CanExecute) {
                WriteControlFlag(
                    ControlBoolFlags.CommandDisabled, 
                    !value);
                CoerceValue(IsEnabledProperty);
            }
        }
    }

    private void UpdateCanExecute() 
    {
        if (Command != null) {
            CanExecute = CanExecuteCommandSource(this);
        } else {
            CanExecute = true;
        }
    }

#endregion

#region "IsDefault"

    /// <summary>
    ///     The DependencyProperty for the IsDefault property.
    ///     Flags:              None
    ///     Default Value:      false
    /// </summary>
    public static readonly DependencyProperty IsDefaultProperty = 
        DependencyProperty.RegisterAttached(
        "IsDefault", 
        typeof(bool), 
        typeof(MyButton), 
        new UIPropertyMetadata(
            false, 
            new PropertyChangedCallback(
                OnIsDefaultChanged)));

    /// <summary>
    /// Specifies whether or not this button is the default button.
    /// </summary>
    /// <value></value>
    public bool IsDefault
    {
        get { 
            return (bool)GetValue(IsDefaultProperty); 
        } 
        set { 
            SetValue(IsDefaultProperty, value); 
        }
    }

    private static void OnIsDefaultChanged(
        DependencyObject valTarget, 
        DependencyPropertyChangedEventArgs e)
    {
        MyButton locMyButton = valTarget as MyButton;
        if (locMyButton != null) {
            Window locWindow = Window.GetWindow(locMyButton);
            if (locWindow == null) {
                locWindow = Application.Current.MainWindow;
            }
            if (FocusChangedEventHandler == null) {
                FocusChangedEventHandler = 
                    new KeyboardFocusChangedEventHandler(
                    locMyButton.OnFocusChanged);
            }

            if (locWindow != null) {
                if ((bool)e.NewValue) {
                    AccessKeyManager.Register("\x000D", locMyButton);
                    KeyboardNavigation.SetAcceptsReturn(
                        locMyButton, true);
                    locMyButton.UpdateIsDefaulted(
                        Keyboard.FocusedElement);
                } else {
                    AccessKeyManager.Unregister("\x000D", locMyButton);
                    KeyboardNavigation.SetAcceptsReturn(
                        locMyButton, false);
                    locMyButton.UpdateIsDefaulted(null);
                }
            }
        }
    }


    private static KeyboardFocusChangedEventHandler FocusChangedEventHandler;

    private void OnFocusChanged(object valTarget, KeyboardFocusChangedEventArgs e)
    {
        UpdateIsDefaulted(Keyboard.FocusedElement);
    }

#endregion

#region "IsDefaulted"

    /// <summary>
    ///     The key needed set a read-only property.
    /// </summary>
    private static readonly DependencyPropertyKey IsDefaultedPropertyKey = 
        DependencyProperty.RegisterReadOnly(
        "IsDefaulted", 
        typeof(bool), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            false));

    /// <summary>
    ///     The DependencyProperty for the IsDefaulted property.
    ///     Flags:              None
    ///     Default Value:      false
    /// </summary>
    public static readonly DependencyProperty IsDefaultedProperty =
        IsDefaultedPropertyKey.DependencyProperty;

    /// <summary>
    /// Specifies whether or not this button is the button that 
    /// would be invoked when Enter is pressed.
    /// </summary>
    /// <value></value>
    public bool IsDefaulted
    {
        get { 
            return (bool)GetValue(IsDefaultedProperty); 
        }
    }

    private void UpdateIsDefaulted(IInputElement valFocusElement)
    {
        // If it's not a default button, or nothing is focused, 
        // or it's disabled 
        // then it's not defaulted.
        if (!IsDefault || valFocusElement == null || !IsEnabled) {
            SetValue(IsDefaultedPropertyKey, false);
            return;
        }
        DependencyObject locFocusDependencyObj = 
            valFocusElement as DependencyObject;
        object locThisScope = null;
        object locFocusScope = null;

        // If the focused thing is not in this scope then 
        // IsDefaulted = false
        AccessKeyPressedEventArgs locEventArgs = 
            default(AccessKeyPressedEventArgs);

        bool locIsDefaulted = false;
        try {
            // Step 1: Determine the AccessKey scope from currently 
            // focused element
            locEventArgs = new AccessKeyPressedEventArgs();
            valFocusElement.RaiseEvent(locEventArgs);
            locFocusScope = locEventArgs.Scope;

            // Step 2: Determine the AccessKey scope from this button
            locEventArgs = new AccessKeyPressedEventArgs();
            this.RaiseEvent(locEventArgs);
            locThisScope = locEventArgs.Scope;

            // Step 3: Compare scopes
            if (object.ReferenceEquals(locThisScope, locFocusScope) 
                && (locFocusDependencyObj == null 
                || !(bool)locFocusDependencyObj.GetValue(KeyboardNavigation.AcceptsReturnProperty))) {

                locIsDefaulted = true;
            }
        } finally {
            SetValue(IsDefaultedPropertyKey, locIsDefaulted);
        }
    }

#endregion

#region "IsCancel"

    /// <summary>
    ///     The DependencyProperty for the IsCancel property.
    ///     Flags:              None
    ///     Default Value:      false
    /// </summary>
    public static readonly DependencyProperty IsCancelProperty = 
        DependencyProperty.Register(
        "IsCancel", 
        typeof(bool), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            false, 
            new PropertyChangedCallback(
                OnIsCancelChanged)));

    /// <summary>
    /// Specifies whether or not this button is the cancel button.
    /// </summary>
    /// <value></value>
    public bool IsCancel
    {
        get { 
            return (bool)GetValue(IsCancelProperty); 
        }
        set {
            SetValue(IsCancelProperty, value); 
        }
    }

    private static void OnIsCancelChanged(
        DependencyObject valTarget, 
        DependencyPropertyChangedEventArgs e)
    {
        MyButton locMyButton = valTarget as MyButton;
        if (locMyButton != null) {
            if ((bool)e.NewValue) {
                AccessKeyManager.Register("\x001B", locMyButton);
            } else {
                AccessKeyManager.Unregister("\x001B", locMyButton);
            }
        }
    }

#endregion

#region "Helper Functions"

    /// <summary>
    /// This allows a caller to override its ICommandSource values 
    //// (used by Button and ScrollBar)
    /// </summary>
    static internal void ExecuteCommand(
        ICommand command, 
        object parameter, 
        IInputElement target)
    {
        RoutedCommand routed = command as RoutedCommand;
        if (routed != null) {
            if (routed.CanExecute(parameter, target)) {
                routed.Execute(parameter, target);
            }
        } else if (command.CanExecute(parameter)) {
            command.Execute(parameter);
        }
    }

    static internal bool CanExecuteCommandSource(
        ICommandSource commandSource)
    {
        ICommand command = commandSource.Command;
        if (command != null) {
            object parameter = commandSource.CommandParameter;
            IInputElement target = commandSource.CommandTarget;
            RoutedCommand routed = command as RoutedCommand;
            if (routed != null) {
                if (target == null) {
                    target = commandSource as IInputElement;
                }
                return routed.CanExecute(parameter, target);
            } else {
                return command.CanExecute(parameter);
            }
        }
        return false;
    }

    /// <summary>
    ///     Executes the command on the given command source.
    /// </summary>
    /// <SecurityNote>
    ///     Critical - calls critical function (ExecuteCommandSource). 
    ///     TreatAsSafe - always passes in false for userInitiated,
    ////                  which is safe
    /// </SecurityNote>
    [SecurityCritical(), SecuritySafeCritical()]
    static internal void ExecuteCommandSource(
        ICommandSource commandSource)
    {
        CriticalExecuteCommandSource(commandSource, false);
    }

    /// <summary>
    ///     Executes the command on the given command source.
    /// </summary>
    /// <SecurityNote>
    /// Critical - sets the user initiated bit on a command, 
    ///            which is used for security purposes later. 
    ///            It is important to validate the callers of this, 
    ///            and the implementation to make sure
    ///            that we only call MarkAsUserInitiated in the 
    ///            correct cases.
    /// </SecurityNote>
    [SecurityCritical()]
    static internal void CriticalExecuteCommandSource(
        ICommandSource commandSource, 
        bool userInitiated)
    {
        ICommand command = commandSource.Command;
        if (command != null) {
            object parameter = commandSource.CommandParameter;
            IInputElement target = commandSource.CommandTarget;
            RoutedCommand routed = command as RoutedCommand;
            if (routed != null) {
                if (target == null) {
                    target = commandSource as IInputElement;
                }
                if (routed.CanExecute(parameter, target)) {
                    routed.Execute(parameter, target);
                }
            } else if (command.CanExecute(parameter)) {
                command.Execute(parameter);
            }
        }
    }

    /// <summary>
    /// DialogCancel Command. It closes window if it's dialog and return 
    /// false as the dialog value.
    /// </summary>
    /// <remarks>
    /// Right now this is only used by Cancel Button to close the dialog.
    static internal readonly RoutedCommand DialogCancelCommand = 
        new RoutedCommand(
        "DialogCancel", 
        typeof(Window));

#endregion

#region "ControlFlags"

    internal bool ReadControlFlag(ControlBoolFlags reqFlag)
    {
        return (_ControlBoolField & reqFlag) != 0;
    }

    internal void WriteControlFlag(ControlBoolFlags reqFlag, bool @set)
    {
        if (@set)
        {
            _ControlBoolField = _ControlBoolField | reqFlag;
        }
        else
        {
            _ControlBoolField = _ControlBoolField & (~reqFlag);
        }
    }

    internal enum ControlBoolFlags : ushort
    {
        ContentIsNotLogical = 0x1,
        // used in contentcontrol.cs
        IsSpaceKeyDown = 0x2,
        // used in ButtonBase.cs
        HeaderIsNotLogical = 0x4,
        // used in HeaderedContentControl.cs, HeaderedItemsControl.cs
        CommandDisabled = 0x8,
        // used in ButtonBase.cs, MenuItem.cs
        ContentIsItem = 0x10,
        // used in contentcontrol.cs
        HeaderIsItem = 0x20,
        // used in HeaderedContentControl.cs, HeaderedItemsControl.cs
        ScrollHostValid = 0x40,
        // used in ItemsControl.cs
        ContainsSelection = 0x80,
        // used in TreeViewItem.cs
        VisualStateChangeSuspended = 0x100
        // used in Control.cs
    }

#endregion
}

/// <summary>
///     An attribute that indicates that a DependencyProperty 
///     declaration is common
///     enough to be included in KnownTypes.cs.
/// </summary>
[Conditional("COMMONDPS")]
internal sealed class CommonDependencyPropertyAttribute : Attribute
{
}

Although this is an older question others may still be interested in an answer as I have been. So, here is my solution. It's based on some reverse engineering of Microsoft reference source I've found (Button and ButtonBase). I'm still newby in WPF so much code may be needless, but it works!

This code adds following features to an UserControl (they seem to be closly connected to each other):

  • IsDefault
  • IsCancel
  • Command
  • Click event

(all comments in code are made by MS)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.ComponentModel;
using System.Windows.Automation.Peers;
using System.Security;
using System.Diagnostics;

[DefaultEvent("Click")]
public partial class MyButton : UserControl, ICommandSource {

#region "Private Variables"

    // Cache valid bits
    private ControlBoolFlags _ControlBoolField;

#endregion

#region "Constructors"

    static MyButton() 
    {
        EventManager.RegisterClassHandler(
            typeof(MyButton), 
            AccessKeyManager.AccessKeyPressedEvent, 
            new AccessKeyPressedEventHandler(
                OnAccessKeyPressed));

        KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(
            typeof(MyButton), 
            new FrameworkPropertyMetadata(
                true));

        // 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(MyButton), 
            new FrameworkPropertyMetadata(
                false, 
                FrameworkPropertyMetadataOptions.Inherits));
    }

#endregion

#region "AccessKey"

    private static void OnAccessKeyPressed(object sender, 
        AccessKeyPressedEventArgs e)
    {
        if (!e.Handled && e.Scope == null && e.Target == null) {
            e.Target = sender as MyButton;
        }
    }

    /// <summary>
    /// The Access key for this control was invoked.
    /// </summary>
    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();
        }
    }

#endregion

#region "Click"

    /// <summary>
    /// Event correspond to left mouse button click
    /// </summary>
    public static readonly RoutedEvent ClickEvent = 
        EventManager.RegisterRoutedEvent(
        "Click", 
        RoutingStrategy.Bubble, 
        typeof(RoutedEventHandler), 
        typeof(MyButton));

    /// <summary>
    /// Add / Remove ClickEvent handler
    /// </summary>
    [Category("Behavior")]
    public event RoutedEventHandler Click 
    {
        add { 
            AddHandler(ClickEvent, value); 
        }
        remove { 
            RemoveHandler(ClickEvent, value); 
        }
    }

    /// <summary>
    /// This virtual method is called when button is clicked and 
    /// it raises the Click event
    /// </summary>
    private void BaseOnClick() 
    {
        RoutedEventArgs locRoutedEventArgs = new RoutedEventArgs(
            MyButton.ClickEvent, 
            this);
        this.RaiseEvent(locRoutedEventArgs);
        ExecuteCommandSource(this);
    }

    /// <summary>
    /// This method is called when button is clicked.
    /// </summary>
    private void OnClick()
    {
        if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked)) {
            AutomationPeer locPeer = 
                UIElementAutomationPeer.CreatePeerForElement(this);
            if (locPeer != null) {
                locPeer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
            }
        }

        // base.OnClick should be called first. Our default command 
        // for Cancel Button to close dialog should happen after 
        // Button's click event handler has been called. 
        // If there Is excption And it Then 's a Cancel button and 
        // RoutedCommand is null, 
        // we will raise Window.DialogCancelCommand.
        try {
            BaseOnClick();
        } finally {
            // When the Button RoutedCommand is null, if it's a 
            // Cancel Button, 
            // Window.DialogCancelCommand will be the default command. 
            // Do not assign Window.DialogCancelCommand to 
            // Button.Command. 
            // If in Button click handler user nulls the Command, 
            // we still want to provide the default behavior.
            if (Command == null && IsCancel) {
                // Can't invoke Window.DialogCancelCommand directly. 
                // Have to raise event. 
                // Filed bug 936090: Commanding perf issue: can't 
                // directly invoke a command.
                ExecuteCommand(DialogCancelCommand, null, this);
            }
        }
    }

#endregion

#region "ClickMode"

    /// <summary>
    ///     The DependencyProperty for the ClickMode property.
    ///     Flags:              None
    ///     Default Value:      ClickMode.Release
    /// </summary>
    public static readonly DependencyProperty ClickModeProperty = 
        DependencyProperty.Register(
        "ClickMode", 
        typeof(ClickMode), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            ClickMode.Release), 
        new ValidateValueCallback(
            IsValidClickMode));

    /// <summary>
    ///     ClickMode specify when the Click event should fire
    /// </summary>
    [Bindable(true), Category("Behavior")]
    public ClickMode ClickMode 
    {
        get { 
            return (ClickMode)GetValue(ClickModeProperty); 
        }
        set { 
            SetValue(ClickModeProperty, value); 
        }
    }

    private static bool IsValidClickMode(object valClickMode)
    {
        ClickMode locClickMode = (ClickMode)valClickMode;
        return locClickMode == ClickMode.Press 
            || locClickMode == ClickMode.Release 
            || locClickMode == ClickMode.Hover;
    }

#endregion

#region "KeyDown"

    /// <summary>
    /// This is the method that responds to the KeyDown event.
    /// </summary>
    /// <param name="e">Event arguments</param>
    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) && 
                    (object.ReferenceEquals(e.OriginalSource, this))) {
                    IsSpaceKeyDown = true;
                    CaptureMouse();
                    if (ClickMode == ClickMode.Press) {
                        OnClick();
                    }
                    e.Handled = true;
                }
            }
        } else if (e.Key == Key.Enter 
            && Convert.ToBoolean(GetValue(KeyboardNavigation.AcceptsReturnProperty))) {

            if (object.ReferenceEquals(e.OriginalSource, this)) {
                IsSpaceKeyDown = 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) {
                IsSpaceKeyDown = false;
                if (IsMouseCaptured) {
                    ReleaseMouseCapture();
                }
            }
        }
    }

    private bool IsSpaceKeyDown 
    {
        get {
            return ReadControlFlag(ControlBoolFlags.IsSpaceKeyDown); 
        }
        set { 
            WriteControlFlag(ControlBoolFlags.IsSpaceKeyDown, value); 
        }
    }

#endregion

#region "Command"

    /// <summary>
    ///     The DependencyProperty for RoutedCommand
    /// </summary>
    [CommonDependencyProperty()]
    public static readonly DependencyProperty CommandProperty = 
        DependencyProperty.Register(
        "Command", 
        typeof(ICommand), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            (ICommand)null, 
            new PropertyChangedCallback(
                OnCommandChanged)));

    /// <summary>
    /// Get or set the Command property
    /// </summary>
    [Bindable(true), Category("Action")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public ICommand Command 
    {
        get {
            return (ICommand)GetValue(CommandProperty); 
        }
        set { 
            SetValue(CommandProperty, value); 
        }
    }

    private static void OnCommandChanged(
        DependencyObject valTarget, 
        DependencyPropertyChangedEventArgs e) 
    {
        MyButton locMyButton = valTarget as MyButton;
        if (locMyButton != null) {
            locMyButton.OnCommandChanged(
                (ICommand)e.OldValue, 
                (ICommand)e.NewValue);
        }
    }

    private void OnCommandChanged(
        ICommand valOldCommand, 
        ICommand valNewCommand) 
    {
        if (valOldCommand != null) {
            valOldCommand.CanExecuteChanged -= OnCanExecuteChanged;
        }
        if (valNewCommand != null) {
            valNewCommand.CanExecuteChanged += OnCanExecuteChanged;
        }
        UpdateCanExecute();
    }

#endregion

#region "CommandParameter"

    /// <summary>
    /// The DependencyProperty for the CommandParameter
    /// </summary>
    [CommonDependencyProperty()]
    public static readonly DependencyProperty CommandParameterProperty = 
        DependencyProperty.Register(
        "CommandParameter",
        typeof(object), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            (object)null));

    /// <summary>
    /// Reflects the parameter to pass to the CommandProperty 
    /// upon execution.
    /// </summary>
    [Bindable(true), Category("Action")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public object CommandParameter 
    {
        get { 
            return GetValue(CommandParameterProperty); 
        }
        set { 
            SetValue(CommandParameterProperty, value); 
        }
    }

#endregion

#region "CommandTarget"

    /// <summary>
    ///     The DependencyProperty for Target property
    ///     Flags:              None
    ///     Default Value:      null
    /// </summary>
    [CommonDependencyProperty()]
    public static readonly DependencyProperty CommandTargetProperty = 
        DependencyProperty.Register(
        "CommandTarget", 
        typeof(IInputElement), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            (IInputElement)null));

    /// <summary>
    ///     The target element on which to fire the command.
    /// </summary>
    [Bindable(true), Category("Action")]
    public IInputElement CommandTarget 
    {
        get { 
            return (IInputElement)GetValue(CommandTargetProperty);
        }
        set { 
            SetValue(CommandTargetProperty, value); 
        }
    }

#endregion

#region "CanExecute"

    private void OnCanExecuteChanged(object valTarget, EventArgs e) 
    {
        if (valTarget != null) {
            UpdateCanExecute();
        }
    }

    private bool CanExecute 
    {
        get { 
            return !ReadControlFlag(ControlBoolFlags.CommandDisabled);
        }
        set {
            if (value != CanExecute) {
                WriteControlFlag(
                    ControlBoolFlags.CommandDisabled, 
                    !value);
                CoerceValue(IsEnabledProperty);
            }
        }
    }

    private void UpdateCanExecute() 
    {
        if (Command != null) {
            CanExecute = CanExecuteCommandSource(this);
        } else {
            CanExecute = true;
        }
    }

#endregion

#region "IsDefault"

    /// <summary>
    ///     The DependencyProperty for the IsDefault property.
    ///     Flags:              None
    ///     Default Value:      false
    /// </summary>
    public static readonly DependencyProperty IsDefaultProperty = 
        DependencyProperty.RegisterAttached(
        "IsDefault", 
        typeof(bool), 
        typeof(MyButton), 
        new UIPropertyMetadata(
            false, 
            new PropertyChangedCallback(
                OnIsDefaultChanged)));

    /// <summary>
    /// Specifies whether or not this button is the default button.
    /// </summary>
    /// <value></value>
    public bool IsDefault
    {
        get { 
            return (bool)GetValue(IsDefaultProperty); 
        } 
        set { 
            SetValue(IsDefaultProperty, value); 
        }
    }

    private static void OnIsDefaultChanged(
        DependencyObject valTarget, 
        DependencyPropertyChangedEventArgs e)
    {
        MyButton locMyButton = valTarget as MyButton;
        if (locMyButton != null) {
            Window locWindow = Window.GetWindow(locMyButton);
            if (locWindow == null) {
                locWindow = Application.Current.MainWindow;
            }
            if (FocusChangedEventHandler == null) {
                FocusChangedEventHandler = 
                    new KeyboardFocusChangedEventHandler(
                    locMyButton.OnFocusChanged);
            }

            if (locWindow != null) {
                if ((bool)e.NewValue) {
                    AccessKeyManager.Register("\x000D", locMyButton);
                    KeyboardNavigation.SetAcceptsReturn(
                        locMyButton, true);
                    locMyButton.UpdateIsDefaulted(
                        Keyboard.FocusedElement);
                } else {
                    AccessKeyManager.Unregister("\x000D", locMyButton);
                    KeyboardNavigation.SetAcceptsReturn(
                        locMyButton, false);
                    locMyButton.UpdateIsDefaulted(null);
                }
            }
        }
    }


    private static KeyboardFocusChangedEventHandler FocusChangedEventHandler;

    private void OnFocusChanged(object valTarget, KeyboardFocusChangedEventArgs e)
    {
        UpdateIsDefaulted(Keyboard.FocusedElement);
    }

#endregion

#region "IsDefaulted"

    /// <summary>
    ///     The key needed set a read-only property.
    /// </summary>
    private static readonly DependencyPropertyKey IsDefaultedPropertyKey = 
        DependencyProperty.RegisterReadOnly(
        "IsDefaulted", 
        typeof(bool), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            false));

    /// <summary>
    ///     The DependencyProperty for the IsDefaulted property.
    ///     Flags:              None
    ///     Default Value:      false
    /// </summary>
    public static readonly DependencyProperty IsDefaultedProperty =
        IsDefaultedPropertyKey.DependencyProperty;

    /// <summary>
    /// Specifies whether or not this button is the button that 
    /// would be invoked when Enter is pressed.
    /// </summary>
    /// <value></value>
    public bool IsDefaulted
    {
        get { 
            return (bool)GetValue(IsDefaultedProperty); 
        }
    }

    private void UpdateIsDefaulted(IInputElement valFocusElement)
    {
        // If it's not a default button, or nothing is focused, 
        // or it's disabled 
        // then it's not defaulted.
        if (!IsDefault || valFocusElement == null || !IsEnabled) {
            SetValue(IsDefaultedPropertyKey, false);
            return;
        }
        DependencyObject locFocusDependencyObj = 
            valFocusElement as DependencyObject;
        object locThisScope = null;
        object locFocusScope = null;

        // If the focused thing is not in this scope then 
        // IsDefaulted = false
        AccessKeyPressedEventArgs locEventArgs = 
            default(AccessKeyPressedEventArgs);

        bool locIsDefaulted = false;
        try {
            // Step 1: Determine the AccessKey scope from currently 
            // focused element
            locEventArgs = new AccessKeyPressedEventArgs();
            valFocusElement.RaiseEvent(locEventArgs);
            locFocusScope = locEventArgs.Scope;

            // Step 2: Determine the AccessKey scope from this button
            locEventArgs = new AccessKeyPressedEventArgs();
            this.RaiseEvent(locEventArgs);
            locThisScope = locEventArgs.Scope;

            // Step 3: Compare scopes
            if (object.ReferenceEquals(locThisScope, locFocusScope) 
                && (locFocusDependencyObj == null 
                || !(bool)locFocusDependencyObj.GetValue(KeyboardNavigation.AcceptsReturnProperty))) {

                locIsDefaulted = true;
            }
        } finally {
            SetValue(IsDefaultedPropertyKey, locIsDefaulted);
        }
    }

#endregion

#region "IsCancel"

    /// <summary>
    ///     The DependencyProperty for the IsCancel property.
    ///     Flags:              None
    ///     Default Value:      false
    /// </summary>
    public static readonly DependencyProperty IsCancelProperty = 
        DependencyProperty.Register(
        "IsCancel", 
        typeof(bool), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(
            false, 
            new PropertyChangedCallback(
                OnIsCancelChanged)));

    /// <summary>
    /// Specifies whether or not this button is the cancel button.
    /// </summary>
    /// <value></value>
    public bool IsCancel
    {
        get { 
            return (bool)GetValue(IsCancelProperty); 
        }
        set {
            SetValue(IsCancelProperty, value); 
        }
    }

    private static void OnIsCancelChanged(
        DependencyObject valTarget, 
        DependencyPropertyChangedEventArgs e)
    {
        MyButton locMyButton = valTarget as MyButton;
        if (locMyButton != null) {
            if ((bool)e.NewValue) {
                AccessKeyManager.Register("\x001B", locMyButton);
            } else {
                AccessKeyManager.Unregister("\x001B", locMyButton);
            }
        }
    }

#endregion

#region "Helper Functions"

    /// <summary>
    /// This allows a caller to override its ICommandSource values 
    //// (used by Button and ScrollBar)
    /// </summary>
    static internal void ExecuteCommand(
        ICommand command, 
        object parameter, 
        IInputElement target)
    {
        RoutedCommand routed = command as RoutedCommand;
        if (routed != null) {
            if (routed.CanExecute(parameter, target)) {
                routed.Execute(parameter, target);
            }
        } else if (command.CanExecute(parameter)) {
            command.Execute(parameter);
        }
    }

    static internal bool CanExecuteCommandSource(
        ICommandSource commandSource)
    {
        ICommand command = commandSource.Command;
        if (command != null) {
            object parameter = commandSource.CommandParameter;
            IInputElement target = commandSource.CommandTarget;
            RoutedCommand routed = command as RoutedCommand;
            if (routed != null) {
                if (target == null) {
                    target = commandSource as IInputElement;
                }
                return routed.CanExecute(parameter, target);
            } else {
                return command.CanExecute(parameter);
            }
        }
        return false;
    }

    /// <summary>
    ///     Executes the command on the given command source.
    /// </summary>
    /// <SecurityNote>
    ///     Critical - calls critical function (ExecuteCommandSource). 
    ///     TreatAsSafe - always passes in false for userInitiated,
    ////                  which is safe
    /// </SecurityNote>
    [SecurityCritical(), SecuritySafeCritical()]
    static internal void ExecuteCommandSource(
        ICommandSource commandSource)
    {
        CriticalExecuteCommandSource(commandSource, false);
    }

    /// <summary>
    ///     Executes the command on the given command source.
    /// </summary>
    /// <SecurityNote>
    /// Critical - sets the user initiated bit on a command, 
    ///            which is used for security purposes later. 
    ///            It is important to validate the callers of this, 
    ///            and the implementation to make sure
    ///            that we only call MarkAsUserInitiated in the 
    ///            correct cases.
    /// </SecurityNote>
    [SecurityCritical()]
    static internal void CriticalExecuteCommandSource(
        ICommandSource commandSource, 
        bool userInitiated)
    {
        ICommand command = commandSource.Command;
        if (command != null) {
            object parameter = commandSource.CommandParameter;
            IInputElement target = commandSource.CommandTarget;
            RoutedCommand routed = command as RoutedCommand;
            if (routed != null) {
                if (target == null) {
                    target = commandSource as IInputElement;
                }
                if (routed.CanExecute(parameter, target)) {
                    routed.Execute(parameter, target);
                }
            } else if (command.CanExecute(parameter)) {
                command.Execute(parameter);
            }
        }
    }

    /// <summary>
    /// DialogCancel Command. It closes window if it's dialog and return 
    /// false as the dialog value.
    /// </summary>
    /// <remarks>
    /// Right now this is only used by Cancel Button to close the dialog.
    static internal readonly RoutedCommand DialogCancelCommand = 
        new RoutedCommand(
        "DialogCancel", 
        typeof(Window));

#endregion

#region "ControlFlags"

    internal bool ReadControlFlag(ControlBoolFlags reqFlag)
    {
        return (_ControlBoolField & reqFlag) != 0;
    }

    internal void WriteControlFlag(ControlBoolFlags reqFlag, bool @set)
    {
        if (@set)
        {
            _ControlBoolField = _ControlBoolField | reqFlag;
        }
        else
        {
            _ControlBoolField = _ControlBoolField & (~reqFlag);
        }
    }

    internal enum ControlBoolFlags : ushort
    {
        ContentIsNotLogical = 0x1,
        // used in contentcontrol.cs
        IsSpaceKeyDown = 0x2,
        // used in ButtonBase.cs
        HeaderIsNotLogical = 0x4,
        // used in HeaderedContentControl.cs, HeaderedItemsControl.cs
        CommandDisabled = 0x8,
        // used in ButtonBase.cs, MenuItem.cs
        ContentIsItem = 0x10,
        // used in contentcontrol.cs
        HeaderIsItem = 0x20,
        // used in HeaderedContentControl.cs, HeaderedItemsControl.cs
        ScrollHostValid = 0x40,
        // used in ItemsControl.cs
        ContainsSelection = 0x80,
        // used in TreeViewItem.cs
        VisualStateChangeSuspended = 0x100
        // used in Control.cs
    }

#endregion
}

/// <summary>
///     An attribute that indicates that a DependencyProperty 
///     declaration is common
///     enough to be included in KnownTypes.cs.
/// </summary>
[Conditional("COMMONDPS")]
internal sealed class CommonDependencyPropertyAttribute : Attribute
{
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文