Hey All,
I was working with a client that wanted to have a default button clicked when an enter key is pressed in a textbox (password box to be more specific) within a Silverlight or WPF application. I didn't think much of it. I'll just listen to the event, bind to the button, and click the button on the enter key event. The listening and binding portion is not very tough with an attached property and a little Silverlight 3 element to element binding love. The tricky part came in when I wanted to cause the button click event to be raised. I ended up having to take a page from Josh Smith http://joshsmithonwpf.wordpress.com/2007/03/09/how-to-programmatically-click-a-button/. Thank you, Josh! This may not be the best way to do this, but it works for me.
Here is a simple page showing the usage of the default button attached property:
1 <UserControl x:Class="DefaultButton.MainPage"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6 xmlns:DefaultButton="clr-namespace:DefaultButton"
7 mc:Ignorable="d"
8 d:DesignWidth="640"
9 d:DesignHeight="480">
10 <StackPanel x:Name="LayoutRoot">
11 <TextBox Text="Press Enter"
12 DefaultButton:DefaultButtonService.DefaultButton="{Binding ElementName=button}"
13 Width="200" Height="30"/>
14 <Button x:Name="button" Content="waiting for click..."
15 Click="Button_Click" Width="200" Height="200"/>
16 <Button x:Name="reset" Content="Reset" Click="reset_Click" Width="200"/>
17 </StackPanel>
18 </UserControl>
Here is the code behind that page:
1 #region Namespaces
2
3 using System.Windows;
4 using System.Windows.Controls;
5
6 #endregion
7
8 namespace DefaultButton
9 {
10 public partial class MainPage : UserControl
11 {
12 public MainPage()
13 {
14 InitializeComponent();
15 }
16
17 private void Button_Click(object sender, RoutedEventArgs e)
18 {
19 button.Content = "I was Clicked!";
20 }
21
22 private void reset_Click(object sender, RoutedEventArgs e)
23 {
24 button.Content = "waiting for click...";
25 }
26 }
27 }
Here is the attached property:
1 #region Namespaces
2
3 using System.Windows;
4 using System.Windows.Automation.Peers;
5 using System.Windows.Automation.Provider;
6 using System.Windows.Controls;
7 using System.Windows.Input;
8
9 #endregion
10
11 namespace DefaultButton
12 {
13 public static class DefaultButtonService
14 {
15 public static DependencyProperty DefaultButtonProperty =
16 DependencyProperty.RegisterAttached("DefaultButton",
17 typeof (Button),
18 typeof (DefaultButtonService),
19 new PropertyMetadata(null, DefaultButtonChanged));
20
21 private static void DefaultButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
22 {
23 var uiElement = d as UIElement;
24 var button = e.NewValue as Button;
25 if (uiElement != null && button != null)
26 {
27 uiElement.KeyUp += (sender, arg) =>
28 {
29 if (arg.Key == Key.Enter)
30 {
31 var peer = new ButtonAutomationPeer(button);
32 var invokeProv =
33 peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
34 if (invokeProv != null)
35 invokeProv.Invoke();
36 }
37 };
38 }
39 }
40
41 public static void SetDefaultButton(UIElement obj, Button button)
42 {
43 obj.SetValue(DefaultButtonProperty, button);
44 }
45
46 public static void GetDefaultButton(UIElement obj)
47 {
48 obj.GetValue(DefaultButtonProperty);
49 }
50 }
51 }
All in all, pretty straight forward.
Cheers,
David
UPDATE: I didn't see this, but it looks like Patrick Cauldwell has blogged about a very similar topic in May (in fact the only major thing that separates the code is the binding). You can see his post here: http://www.cauldwell.net/patrick/blog/ALdquodefaultButtonrdquoInSilverlight.aspx.