In WPF, how to add an EventHandler for the FrameworkElement developed in the template?

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

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

I need to provide an event handler method for the Slider ValueChanged . I do not know where I should write this code, since it is impractical to specify an event handler for the control in the template.

I searched for a solution to the problem and found that I should add an event handler to the OnApplyTemplate() method override. I assume that it should look something like this:

 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? Is OnApplyTemplate overridden only for ControlTemplates or is my script included? Should I provide a ControlTemplate instead of a DataTemplate? Is the body of the method that I gave correct?

Please, help. Thank you

+4
source share
5 answers

Using the OnApplyTemplate approach will work if you are working with a ControlTemplate for a control. For example, if you subclassed a TextBox , you could do this, for example

 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 do not think this approach will work in your situation. You can use the Loaded event for the 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 for

 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; } 
+6
source

Little is known that ResourceDictionaries may also contain CodeBehind.

As a rule, I don’t think that to start using DataTemplates in ResourceDictionaries (your question is an example for one of the reasons), here 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 the code behind:

 namespace WpfApplication24 { public partial class Dictionary1 : ResourceDictionary { public void ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { Debug.Write("Hello"); } } } 

In any case, as Meleak said above me, OnApplyTemplate is applicable only to control templates, and not to data templates.

+3
source

You can use the EventSetter in the style in which you install the template:

 <Style TargetType="{x:Type ListBoxItem}"> <EventSetter Event="MouseWheel" Handler="GroupListBox_MouseWheel" /> <Setter Property="Template" ... /> </Style> 
0
source

Method 1 : Use your own Slider derived control:

 public class SpecialSlider : Slider { public SpecialSlider() { ValueChanged += OnValueChanged; } private void OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { // ... } } 

Method 2 : use the behavior from the System.Windows.Interactivity.dll assembly (available through NuGet):

 public class SpecialSliderBehavior : Behavior<Slider> { protected override void OnAttached() { base.OnAttached(); AssociatedObject.ValueChanged += OnValueChanged; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.ValueChanged -= OnValueChanged; } private void OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { // ... } } 

Here's how to do it:

 ... xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" ... <DataTemplate x:Key="MyListBoxItemTemplate" DataType="{x:Type entities:Track}"> <Slider Name="MySlider"> <i:Interaction.Behaviors> <SpecialSliderBehavior /> </i:Interaction.Behaviors> </Slider> </DataTemplate> 
0
source

Source: https://habr.com/ru/post/1341234/


All Articles