In WPF, how to add an EventHandler for a FrameworkElement designed in Template?
Solution 1
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;
}
Solution 2
Little know fact is that ResourceDictionaries can hold CodeBehind as well..
As a general rule of thumb I don't think that putting DataTemplates in ResourceDictionaries is a good idea to begin with (your question being an example for one of the reasons), this is how you can solve it:
XAML:
<ResourceDictionary
x:Class="WpfApplication24.Dictionary1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="MyDataTemplate">
<StackPanel>
<TextBlock Text="Hello" />
<Slider ValueChanged="ValueChanged"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
and code behind:
namespace WpfApplication24
{
public partial class Dictionary1 : ResourceDictionary
{
public void ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Debug.Write("Hello");
}
}
}
Anyhow, as Meleak said above me - OnApplyTemplate is only relevant for Control Templates and not Data Templates.
![Boris](https://i.stack.imgur.com/QPEQU.jpg?s=256&g=1)
Boris
Updated on June 09, 2022Comments
-
Boris about 2 years
I have defined the following
DataTemplate
forListBox
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.
-
Boris over 13 yearsErno, thank you for your answer. I will be waiting for a solution which I would be able to understand more easily. I have never used Commands before and I don't understand the explanation provided.
-
Emond over 13 yearsI added a good tutorial that explains and shows standard commands and custom commands.
-
Boris over 13 yearsThanks Erno, I skimmed it (as I am far too busy at the moment) and it appears logical. Thank you for helping me understand the concept of commands in WPF.
-
Boris over 13 yearsThank you Elad. I see the point you're trying to make. It all looks much clearer now and I think I will start developing a
ControlTemplate
:) -
Emond over 13 yearsI thought a bit about this and I do not like it. A ValueChanged method on a ResourceDictionary is out of place. It is nice to know that there is a code-behind for resource dictionaries but this smells.
-
Elad Katz over 13 yearsI very much agree.. I'm even taking it a step forward to say that Imo datatemplates altogether don't belong in resource dictionaries
-
Peter Huber about 4 yearsThe Loaded event is not always useful, for example when Visibility is Collapsed and gets set to Visible only later. In that case Loaded get raised without the template being applied. Once the control gets visible and the template applied, loaded does not fire again.