Handling Events within Silverlight Control Templates – AutoCompleteBox Example

One of the great features Silverlight offers is the ability to customize controls by using control templates.  If you don’t like how a particular control looks you can modify the template and in many cases be ready to use the new control without writing a single line of C# or VB code.  I’m working on a client application that uses the AutoCompleteBox found in the Silverlight Toolkit and needed a way to change it from a regular TextBox to more of an editable ComboBox.  Fortunately the Silverlight Toolkit samples (for Silverlight 2 and 3) already do something like this as you can see here (once on the page click on AutoCompleteBox to the left and then on the Styling tab at the top of the page to see the sample).

image

The sample modifies the standard AutoCompleteBox to look more like an editable ComboBox by defining a custom control template with a ToggleButton in it (Tim Heuer provides a nice walk through of this type of customization here if you’re interested).  Here’s a simplified version of the Silverlight Toolkit’s sample template that I’m using:

<ControlTemplate TargetType="input:AutoCompleteBox">
    <Grid Margin="{TemplateBinding Padding}">
        <TextBox IsTabStop="True" x:Name="Text" Style="{TemplateBinding TextBoxStyle}" Margin="0" />
        <ToggleButton x:Name="ToggleButton"
            HorizontalAlignment="Right"
            VerticalAlignment="Center"
            Style="{StaticResource ComboToggleButton}"
            Margin="0"
            HorizontalContentAlignment="Center" 
            Background="{TemplateBinding Background}" 
            BorderThickness="0" 
            Height="16" Width="16"
            Click="DropDownToggle_Click">
            <ToggleButton.Content>
                <Path x:Name="BtnArrow" Height="4" Width="8" Stretch="Uniform" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z " 
                      Margin="0,0,6,0" HorizontalAlignment="Right">
                    <Path.Fill>
                        <SolidColorBrush x:Name="BtnArrowColor" Color="#FF333333"/>
                    </Path.Fill>
                </Path>
            </ToggleButton.Content>
        </ToggleButton>
        <Popup x:Name="Popup">
            <Border x:Name="PopupBorder" HorizontalAlignment="Stretch" Opacity="1.0" BorderThickness="0">
                <Border.RenderTransform>
                    <TranslateTransform X="2" Y="2" />
                </Border.RenderTransform>
                <Border.Background>
                    <SolidColorBrush Color="#11000000" />
                </Border.Background>
                        <ListBox x:Name="Selector" ScrollViewer.HorizontalScrollBarVisibility="Auto" 
                             ScrollViewer.VerticalScrollBarVisibility="Auto" 
                             ItemTemplate="{TemplateBinding ItemTemplate}" />
             </Border>
        </Popup>
    </Grid>
</ControlTemplate>


Looking at the template you’ll see that it defines a ToggleButton with a Click event and associated event handler named DropDownToggle_Click.  Here’s what the ToggleButton’s Click event handler looks like:

private void DropDownToggle_Click(object sender, RoutedEventArgs e)
{
    FrameworkElement fe = sender as FrameworkElement;
    AutoCompleteBox acb = null;
    while (fe != null && acb == null)
    {
        fe = VisualTreeHelper.GetParent(fe) as FrameworkElement;
        acb = fe as AutoCompleteBox;
    }
    if (acb != null)
    {
        if (String.IsNullOrEmpty(acb.SearchText))
        {
            acb.Text = String.Empty;
        }
        acb.IsDropDownOpen = !acb.IsDropDownOpen;
    }
}


You can see that the code uses the VisualTreeHelper class to access the parent of the ToggleButton which is the AutoCompleteBox.  Once the AutoCompleteBox parent is found the code handles showing or hiding the Popup control that’s part of the control template by setting the IsDropDownOpen property to true or false.

This works fine if the control template is placed in the same scope as the event handler code such as the page or user control resources section.  However, if you try to move the template code to another resources section that doesn’t have access to the event handler code (DropDownToggle_Click) you’ll run into problems .  What if you want to put the control template in a merged resource dictionary (similar to an external CSS stylesheet to give a web analogy) and can’t hard-code the click event into the control template since you don’t know where the event handler will be defined at that point?  Although you can certainly write a custom control that derives from AutoCompleteBox in this case (which would be recommended if you’ll re-use the control across multiple pages or user controls), another solution is to hook-up the ToggleButton’s Click event when the AutoCompleteBox control first loads as shown next:

 

void SilverlightApplication_Loaded(object sender, RoutedEventArgs e)
{
    HookAutoCompleteBoxEvents();
}

void HookAutoCompleteBoxEvents()
{
    AutoCompleteBox[] boxes = { this.JobIDAutoCompleteBox, this.EmployeeAutoCompleteBox };
    foreach (var box in boxes)
    {
        Grid grid = VisualTreeHelper.GetChild(box, 0) as Grid;
        ToggleButton tb = grid.Children[1] as ToggleButton;
        if (tb != null) tb.Click += DropDownToggle_Click;
    }
}


When the Silverlight application Loaded event is called it calls the HookAutoCompleteBoxEvents() method.  Within HookAutoCompleteBoxEvents() an array of AutoCompleteBox controls is iterated through to locate the ToggleButton for each control and attach a Click event handler to it.  Doing this avoids hard-coding the event handler in the control template so that it can be defined just about anywhere you’d like without running into code scoping issues. 

Note: Keep in mind that if you plan on using the customized AutoCompleteBox control in several places it may be worth the time to create a custom control that derives from AutoCompleteBox to avoid having to put the ToggleButton event handler code and the HookAutoCompleteBoxEvents code into each page or user control. 

 

Logo

For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com/.

comments powered by Disqus

No Comments