wpfeventswpf-controlsframeworkelement

In WPF, how to add an EventHandler for a FrameworkElement designed in Template?


I have defined the following DataTemplate for ListBox items in an external resource dictionary:

<DataTemplate x:Key="MyListBoxItemTemplate" DataType="{x:Type entities:Track}">
    <StackPanel>       
        <TextBlock Text="Here's the slider:" />
        <Slider Name="MySlider" Height="23" Minimum="0" />
    </StackPanel>
</DataTemplate>

I need to provide an event handler method for Slider's ValueChanged event. I don't know where am I supposed to write that code as it is impractical to specify event handler for a control within a template.

I've been googling for the solution and found that I should add the event handler in the override of the OnApplyTemplate() method. My guess is that it should look something like this or similar:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    // Is the following initialization even going to work!?!?
    Slider MySlider = this.FindName("MySlider") as Slider;
    SeekSlider.ValueChanged += 
        new RoutedPropertyChangedEventHandler<double>(SeekSlider_ValueChanged);
}

But where should I write this method? Does OnApplyTemplate overriding only applies to ControlTemplates or is my scenario included as well? Should I provide ControlTemplate instead of DataTemplate? Is the body of the method I have provided correct?

Please help. Thanks.


Solution

  • Using the OnApplyTemplate approach will work if you if you're working with the ControlTemplate for a Control. For example, if you've subclassed TextBox you could do this like

    public class MyTextBox : TextBox
    {
        public override void OnApplyTemplate()
        {
            MySlider MySlider = GetTemplateChild("MySlider") as MySlider;
            if (MySlider != null)
            {
                MySlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(MySlider_ValueChanged);
            }
            base.OnApplyTemplate();
        }
        void MySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            //...
        }
    }
    

    I don't think this approach will work in your situation however. You could use the Loaded event for ListBoxItem and find the Slider in the visual tree in the event handler

    <ListBox ...>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <EventSetter Event="Loaded" Handler="ListBoxItem_Loaded"/>
            </Style>
        </ListBox.ItemContainerStyle>
        <!--...-->
    </ListBox>
    

    Code behind

    private void ListBoxItem_Loaded(object sender, RoutedEventArgs e)
    {
        ListBoxItem listBoxItem = sender as ListBoxItem;
        Slider MySlider = GetVisualChild<Slider>(listBoxItem);
        MySlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(MySlider_ValueChanged);
    }
    void MySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
    
    }
    

    GetVisualChild

    private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
    {
        T child = default(T);
    
        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
            {
                child = GetVisualChild<T>(v);
            }
            if (child != null)
            {
                break;
            }
        }
        return child;
    }