Let’s say I’ve got to the effort of creating a modal dialog or it’s equivalent in a Silverlight application. What I would like to complete the picture is a “default” button, so that if I hit the Enter key while in a text box, the dialog will be “submitted”. There are probably several ways of achieving this end, but I wanted something that was simple, and encapsulated in an attached property so the consumer wouldn’t have to deal with much code. I wrote the attached property so that I could use it on text boxes like this
<TextBox x:Name="theText" VerticalAlignment="Center" Margin="10,0,10,0"
Grid.Row="0" my:DefaultButtonService.DefaultButton="theButton"/>
where “theButton” here is the default button I want to be pressed when the Enter key happens inside my text box. The downside is that I have to apply this property to every text box on the page, but so be it, that seems like a relatively small price to pay. So I got as far as finding the named button in the visual tree, but the the question was, how to “press” the button. The Button class in Silverlight has a protected OnClick that would do the trick, if it wasn’t protected. I could derive my own control from Button and expose the OnClick method, but ewww. If I did that then every dialog that wanted this behavior would have to remember to use the derived Button class. I tried reflecting over the Button and Invoking OnClick anyway, but turns out you get a security exception. OK. Then, thanks to a presentation Stuart gave us yesterday on Accessibility, I remembered the Automation framework in Silverlight. That turned out to be super easy, just create an instance of ButtonAutmationPeer from the Button, then Invoke it. Cool.
public static class DefaultButtonService
{
public static readonly DependencyProperty DefaultButtonProperty =
DependencyProperty.RegisterAttached("DefaultButton", typeof(string), typeof(DefaultButtonService), new PropertyMetadata(OnDefaultButtonChanged));
public static string GetDefaultButton(DependencyObject d)
{
return (string)d.GetValue(DefaultButtonProperty);
}
/// <summary>
/// Sets the CommandParameter property.
/// </summary>
public static void SetDefaultButton(DependencyObject d, string value)
{
d.SetValue(DefaultButtonProperty, value);
}
private static void OnDefaultButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBox tb = d as TextBox;
if (tb != null)
{
tb.KeyUp += new KeyEventHandler(tb_KeyUp);
}
}
static void tb_KeyUp(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Enter:
string name = (string)((DependencyObject)sender).GetValue(DefaultButtonProperty);
object root = App.Current.RootVisual;
object button = ((FrameworkElement)root).FindName(name);
if (button is Button)
{
ButtonAutomationPeer peer = new ButtonAutomationPeer((Button)button);
IInvokeProvider ip = (IInvokeProvider)peer;
ip.Invoke();
}
break;
}
}
}