# Thursday, 07 May 2009

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;
       }
   }
}