在派生WPF控件时,是否可以保证控件的事件处理程序首先处理事件?

时间:2016-11-10 13:35:57

标签: .net wpf events event-handling

我正在派生一个WPF TextBox控件来创建一个只接受美元货币值作为输入的控件。我知道之前已经完成了这项工作,并且我可以使用现有的库,但这更像是一种学习练习,因为尝试使用其中一种现有的库控件失败 - 它不符合我的要求

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

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

1 个答案:

答案 0 :(得分:0)

这确实是一个非常好的问题。正如你所指出的,它是按照你如何注册eventhandlers的顺序排序的。如果你在运行时通过反射操作并改变处理程序顺序,它可以按预期工作。我准备了一个scenerio就像你上面说过。

这里我定义了一个属性

[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;被建造, 反射根据您的定义更改订单。