WPF - 处理自定义控件上的自定义附加事件

7

我有一个声明为路由事件的事件(名称已更改以保护无辜者):

public class DragHelper : DependencyObject {
    public static readonly RoutedEvent DragCompleteEvent = EventManager.RegisterRoutedEvent(
        "DragComplete",
        RoutingStrategy.Bubble,
        typeof(DragRoutedEventHandler),
        typeof(DragHelper)
    );

    public static void AddDragCompleteHandler( DependencyObject dependencyObject, DragRoutedEventHandler handler ) {
        UIElement element = dependencyObject as UIElement;
        if (element != null) {
            element.AddHandler(DragCompleteEvent, handler);
        }
    }

    public static void RemoveDragCompleteHandler( DependencyObject dependencyObject, DragRoutedEventHandler handler ) {
        UIElement element = dependencyObject as UIElement;
        if (element != null) {
            element.RemoveHandler(DragCompleteEvent, handler);
        }
    }

非常标准的内容。在XAML中,我有一个包含单个自定义控件的DataTemplate。我试图将这个事件(以及其他一些附加属性)附加到控件上:

<DataTemplate ...>
    <My:CustomControl
        My:DragHelper.IsDragSource="True"
        My:DragHelper.DragComplete="DragCompleteHandler" />
</DataTemplate>

这无法产生期望的结果。具体来说,虽然调用DragComplete事件的RaiseEvent()代码被调用,但处理程序从未被调用。实际上,在此XAML文件中其他任何已连接的自定义路由事件的处理程序也没有被调用。

我尝试更改路由事件的名称,并尝试将数据模板从带有DataType的模板更改为带有x:Key的模板。但是,行为在外观上没有发生变化。

但是,如果我将My:CustomControl更改为任何内置的WPF控件,例如TextBlock,则事件会按照我所期望的精确触发。同样,如果我将自定义控件替换为项目中的任何其他自定义UserControl子类,则行为会恢复到破损的状态,即似乎永远不会处理任何事件。

这对我来说毫无意义。我需要做一些特定的事情才能使此场景正常工作吗?看起来应该不重要。我想可能是我在所有自定义控件中都做了某些特定的事情导致事件处理出现问题,但到目前为止,我还没有看到任何共同点。

1个回答

1

你没有发布所有的代码,所以我不得不推断并组合自己的版本。它对我来说可以工作。也许与你的代码进行比较和对照:

Window1.xaml.cs:

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void DragCompleteHandler(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("YEP");
        }
    }

    public class CustomControl : TextBox
    {
    }

    public class DragHelper : DependencyObject
    {
        public static readonly DependencyProperty IsDragSourceProperty = DependencyProperty.RegisterAttached("IsDragSource",
            typeof(bool),
            typeof(DragHelper),
            new FrameworkPropertyMetadata(OnIsDragSourceChanged));

        public static bool GetIsDragSource(DependencyObject depO)
        {
            return (bool)depO.GetValue(IsDragSourceProperty);
        }

        public static void SetIsDragSource(DependencyObject depO, bool ids)
        {
            depO.SetValue(IsDragSourceProperty, ids);
        }

        public static readonly RoutedEvent DragCompleteEvent = EventManager.RegisterRoutedEvent(
            "DragComplete",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(DragHelper)
        );

        public static void AddDragCompleteHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            UIElement element = dependencyObject as UIElement;
            if (element != null)
            {
                element.AddHandler(DragCompleteEvent, handler);
            }
        }

        public static void RemoveDragCompleteHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            UIElement element = dependencyObject as UIElement;
            if (element != null)
            {
                element.RemoveHandler(DragCompleteEvent, handler);
            }
        }

        private static void OnIsDragSourceChanged(DependencyObject depO, DependencyPropertyChangedEventArgs e)
        {
            (depO as TextBox).TextChanged += delegate
            {
                (depO as TextBox).RaiseEvent(new RoutedEventArgs(DragCompleteEvent, null));
            };
        }
    }
}

Window1.xaml:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate x:Key="Test">
            <local:CustomControl
                local:DragHelper.IsDragSource="True"
                local:DragHelper.DragComplete="DragCompleteHandler" />
        </DataTemplate>
    </Window.Resources>

    <ContentControl ContentTemplate="{StaticResource Test}"/>
</Window>

我的自定义控件是从UserControl派生的,但即使像你的一样切换到TextBox,我仍然遇到了问题。我省略了其余的代码,因为它们基本上与你的相同,除了一些命名空间的更改。然而,它是一个更大项目的一部分,所以可能会与某些全局样式或资源之类的交互有关,这可能会影响问题。我会尽力解决。 - jmp
1
好的,所以我已经稍微调试了一下。 从您的完整示例开始,这对我有效,我尝试了各种方法将其转换为我在更大项目中看到的非功能性案例。过了一会儿,我偶然发现了一个非常简单的更改,可以重现问题。 如果我采取DragHelper实现并将其移动到由主应用程序引用的不同程序集中,并相应地修改Window1.xaml(添加xmlns:other并使用other:而不是local :)),则当我在文本框中键入时,我不再看到消息框被触发。 :| - jmp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接