【问题标题】:In WPF, how to add an EventHandler for a FrameworkElement designed in Template?在 WPF 中,如何为 Template 中设计的 FrameworkElement 添加 EventHandler?
【发布时间】:2011-07-03 23:06:03
【问题描述】:

我为外部资源字典中的ListBox 项定义了以下DataTemplate

<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>

我需要为 Slider 的ValueChanged 事件提供事件处理方法。我不知道我应该在哪里编写该代码,因为在模板中为控件指定事件处理程序是不切实际的。

我一直在搜索解决方案,发现我应该在 OnApplyTemplate() 方法的覆盖中添加事件处理程序。我的猜测是它应该看起来像这样或类似的东西:

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);
}

但是我应该在哪里写这个方法呢? OnApplyTemplate 覆盖是否仅适用于 ControlTemplates 还是我的方案也包括在内?我应该提供 ControlTemplate 而不是 DataTemplate 吗?我提供的方法的主体是否正确?

请帮忙。谢谢。

【问题讨论】:

    标签: wpf events wpf-controls frameworkelement


    【解决方案1】:

    【讨论】:

    • Erno,谢谢你的回答。我将等待一个我能够更容易理解的解决方案。我以前从未使用过命令,我不理解提供的解释。
    • 我添加了一个很好的教程来解释和展示标准命令和自定义命令。
    • 感谢 Erno,我略读了一遍(因为我现在太忙了),看起来合乎逻辑。感谢您帮助我理解 WPF 中命令的概念。
    【解决方案2】:

    如果您正在使用ControlTemplate 作为控件,则使用OnApplyTemplate 方法将起作用。例如,如果您将 TextBox 子类化,您可以这样做

    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)
        {
            //...
        }
    }
    

    但是,我认为这种方法不适用于您的情况。您可以将 Loaded 事件用于 ListBoxItem,并在事件处理程序的可视化树中找到 Slider

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

    背后的代码

    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;
    }
    

    【讨论】:

    • Loaded 事件并不总是有用的,例如当 Visibility 被折叠并且稍后才设置为 Visible 时。在这种情况下,Loaded 在没有应用模板的情况下被提升。一旦控件变得可见并且应用了模板,loaded 就不会再次触发。
    【解决方案3】:

    鲜为人知的事实是 ResourceDictionaries 也可以保存 CodeBehind..

    作为一般经验法则,我认为将 DataTemplates 放入 ResourceDictionaries 不是一个好主意(您的问题是其中一个原因的示例),这就是您可以解决的方法:

    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>
    

    和后面的代码:

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

    无论如何,正如 Meleak 在我上面所说的那样 - OnApplyTemplate 仅与控制模板相关,而不与数据模板相关。

    【讨论】:

    • 谢谢埃拉德。我明白你要表达的观点。现在一切看起来都清楚多了,我想我会开始开发ControlTemplate :)
    • 我想了一下,我不喜欢它。 ResourceDictionary 上的 ValueChanged 方法不合适。很高兴知道资源字典有一个代码隐藏,但这很奇怪。
    • 我非常同意.. 我什至更进一步地说 Imo 数据模板完全不属于资源字典
    【解决方案4】:

    您可以在设置模板的样式中使用EventSetter

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

    【讨论】:

      【解决方案5】:

      方法一: 使用继承自Slider的自己的控件:

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

      方法 2:使用 System.Windows.Interactivity.dll 程序集中的行为(可通过 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)
          {
              // ...
          }
      }
      

      这是如何附加它的:

      ...
      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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-27
        • 1970-01-01
        • 2011-08-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多