【问题标题】:When deriving a WPF control, can it be guaranteed that the control's event handler handles the event first?派生WPF控件时,能否保证控件的事件处理程序先处理事件?
【发布时间】:2017-03-24 13:31:12
【问题描述】:

我正在派生 WPF TextBox 控件以创建一个仅接受美国货币值作为输入的控件。我知道这已经完成了,并且有我可以使用的现有库,但这更多是一种学习练习,源于尝试使用其中一个现有库控件的失败——它不符合我的要求.

在这样做时,我试图阻止文本框接受不符合美国货币格式的文本(即可选的前导货币符号、十进制数字、可选的组分隔符、可选的小数部分)。我知道有PreviewTextInput 事件。我在谷歌上搜索的许多来源都建议(得到社区的认可)可以简单地处理此事件并通过设置 e.Handled = true 拒绝不需要的输入(暂时搁置这不适用于复制/粘贴文本,更新数据绑定或设计时 XAML 值,仅举几例)。

我一直想知道这种方法是否一直有效。鉴于the order that event handlers are called is not guaranteed,我怎么知道我的控件的事件处理程序首先被调用?换句话说:我怎么知道某人的事件处理程序没有首先运行并使用允许我试图禁止的格式的值做其他事情,然后设置e.Handled = trueOnPreviewTextInput 方法呢?我相信这也有类似的担忧,不是吗?

【问题讨论】:

  • 如果您创建自定义文本框,然后使用它并创建 PreviewTextInput,这将在您正在扩展的文本框内的文本框之前运行。我相信预览键在 PreviewTextInput 之前运行,你可以在那里处理它。

标签: .net wpf events event-handling


【解决方案1】:

这确实是一个很好的问题。正如您所指出的,它是按您注册事件处理程序的方式排序的。如果您在运行时通过反射进行操作并更改处理程序顺序,它可以按预期工作。我准备了一个场景你上面说了。

这里我定义了一个属性

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomAttribute : Attribute
{
    private int _value;

    public int Value
    {
        get { return _value; }
        set { _value = value; }
    }

    private string _eventName;

    public string EventName
    {
        get { return _eventName; }
        set { _eventName = value; }
    }


    public CustomAttribute()
    {

    }
}

我创建了一个从 TextBox 扩展的自定义框

public class CustomBox : TextBox
{
    public CustomBox()
    {
        this.PreviewTextInput += CustomBox_TextChanged;
        this.PreviewTextInput += CustomBox_PreviewTextInput;
    }

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);

        foreach (var item in typeof(CustomBox).GetRuntimeMethods().ToList())
        {
            var a = item.GetCustomAttributes();

            // unsubscribe 
            foreach (var i in a)
            {
                if (i.GetType() == typeof(CustomAttribute))
                {
                    if (((CustomAttribute)i).Value > 0)
                    {
                        RemoveEvent(((CustomAttribute)i).EventName, item.Name);
                    }
                }
            }
        }
        // subscribe according to your order 
        var methods = typeof(CustomBox).GetRuntimeMethods()
                  .Where(m => m.GetCustomAttributes(typeof(CustomAttribute), false).Length > 0)
                  .ToList();

        foreach (var item in methods.OrderBy(m => ((CustomAttribute)m.GetCustomAttribute(typeof(CustomAttribute))).Value))
        {
            AddEvent(((CustomAttribute)item.GetCustomAttribute(typeof(CustomAttribute))).EventName, item.Name);
        }

    }
    private void RemoveEvent(string eventName, string methodName)
    {
        EventInfo ev = this.GetType().GetEvent(eventName);
        Type tDelegate = ev.EventHandlerType;
        MethodInfo miHandler = typeof(CustomBox).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
        Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
        ev.RemoveEventHandler(this, d);
    }

    private void AddEvent(string eventName,string methodName)
    {
        EventInfo ev = this.GetType().GetEvent(eventName);
        Type tDelegate = ev.EventHandlerType;
        MethodInfo miHandler = typeof(CustomBox).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
        Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
        ev.AddEventHandler(this,d);
    }

    [CustomAttribute(EventName = "PreviewTextInput",Value = 2)]
    private void CustomBox_TextChanged(object sender, TextCompositionEventArgs e)
    {
        this.Text = e.Text;
        e.Handled = true;
    }

    [CustomAttribute(EventName = "PreviewTextInput", Value = 1)]
    private void CustomBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        if (e.Text.Contains("e"))
        {
            e.Handled = true;
        }
        else e.Handled = false;
    }
}

以上,即使有人创造了

this.PreviewTextInput += CustomBox_TextChanged; 操纵的处理程序 文本框文本并将其更改为非自愿文本并通过 e.handle = true 阻止另一个事件;

就在此之前。PreviewTextInput += CustomBox_PreviewTextInput;被建造, 反射会根据你的定义改变它们的顺序。

【讨论】:

    猜你喜欢
    • 2015-02-02
    • 2012-06-27
    • 2012-05-02
    • 2010-12-26
    • 2011-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-15
    相关资源
    最近更新 更多