views:

221

answers:

1

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 peak 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!");
        }
    }
}
+2  A: 

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.)
itowlson
One more consideration: In OnAccessKey you may also want to check AccessKeyEventArgs.IsMultiple. If it is true, multiple controls have registered the Enter key as their access key. You may choose to respond differently in this case, such as just setting focus.
Ray Burns
Hmmm, this doesn't appear to be working for me. I think I followed all of the suggestions but nothing happens when I hit enter.
jpierson
Could you post your code (or a simple repro if your real code is too lengthy)?
itowlson
I've added an example of the code that I've tried without success.
jpierson