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

后端 未结 5 1371
被撕碎了的回忆
被撕碎了的回忆 2021-01-06 11:12

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



        
相关标签:
5条回答
  • 2021-01-06 11:56

    Method 1: Use your own control inherited from Slider:

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

    Method 2: use behaviors from System.Windows.Interactivity.dll assembly (available through the 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)
        {
            // ...
        }
    }
    

    This is how to attach 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 讨论(0)
  • 2021-01-06 12:00

    You can use EventSetter in the style you are setting the template with:

    <Style TargetType="{x:Type ListBoxItem}">
          <EventSetter Event="MouseWheel" Handler="GroupListBox_MouseWheel" />
          <Setter Property="Template" ... />
    </Style>
    
    0 讨论(0)
  • 2021-01-06 12:06

    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;
    }
    
    0 讨论(0)
  • 2021-01-06 12:13

    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.

    0 讨论(0)
  • 2021-01-06 12:13

    Have a look at this question and answer.

    I think that using Commands is the best option.

    EDIT A good tutorial

    0 讨论(0)
提交回复
热议问题