拦截和取消WinForms按钮的Click事件

时间:2012-03-25 13:01:47

标签: c# winforms events compact-framework

有没有办法从父控件捕获Button的Click事件,并防止在父Control中的变量为真时发生?

例如:

private void AssignEventOverrides()
{
    foreach (Button button in Buttons) 
    {
        // Get the event assigned at design time
        var someUnknownEventHandler = GetTheClickHandlerSomehow(button);

        // Unsubscribe the unknown event
        button.Click -= SomeUnknownEventHandler;

        // Assign the intercepting event
        button.Click += button_Click;
    }
}

private void button_Click(object sender, EventArgs e)
{
    if (!preventClick)
    {
        // Fire the intercepted event that was previously unsubscribed
    }
}

希望有一个比上面的例子更好的方法。需要注意的是,我对分配给上述按钮的事件没有任何控制权。他们在其他地方订阅了,如果消费父控件上的变量为true,我只需要防止它们发生。

我的真实场景:

我正在使用Windows Mobile 6.5(遗留应用程序重写)我创建了一个可以包含N个子控件的面板控件。面板控件允许iPhone样式垂直滚动。我订阅了子MouseMove的{​​{1}}事件,并将变量Control设置为_isScrolling。在true活动中,我将MouseUp设置为_isScrolling

false发生在Click之前我可以在MouseUp事件中查看,以确保按下按钮时没有发生滚动。所以基本上,如果我的面板滚动发生,我需要阻止点击事件(在设计时订阅)发射。

4 个答案:

答案 0 :(得分:2)

这可能会提供一些有用的见解

How to remove all event handlers from a control

具体来说,看看

private void RemoveClickEvent(Button b)
{
    FieldInfo f1 = typeof(Control).GetField("EventClick", 
        BindingFlags.Static | BindingFlags.NonPublic);
    object obj = f1.GetValue(b);
    PropertyInfo pi = b.GetType().GetProperty("Events",  
        BindingFlags.NonPublic | BindingFlags.Instance);
    EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
    list.RemoveHandler(obj, list[obj]);
}

答案 1 :(得分:1)

作为查看问题的另一种方法,为什么不只是对托管表单进行子类化并查找目标点击次数。如果您的preventClick为真,则将邮件标记为已处理,以防止邮件传递给孩子。

答案 2 :(得分:0)

如何禁用按钮而不是将父控件中的变量设置为true?

更新: 设置button.Enabled = false肯定会阻止Click事件提升:)

更新2(针对真实场景):考虑创建自定义按钮

public class ScrollableButton : Button
{
    private bool _isScrolling;

    protected override void OnMouseMove(MouseEventArgs mevent)
    {
        _isScrolling = true;
        base.OnMouseMove(mevent);
    }

    protected override void OnMouseUp(MouseEventArgs mevent)
    {
        base.OnMouseUp(mevent);
        _isScrolling = false;
    }

    public bool IsScrolling { get; set; }

    protected override void OnClick(EventArgs e)
    {
        if (!_isScrolling)
            base.OnClick(e);
    }
}

只需用可滚动按钮替换默认按钮。

答案 3 :(得分:0)

感谢Hehewaffles answer,这是我提出的解决方案:

/// <summary>
/// Dictionary for holding the controls design time Click EventHandlers.
/// </summary>
private Dictionary<Control, EventHandler> _interceptedClickEventHandlers;

/// <summary>
/// Recursively moves through the child controls assigning events for scrolling and replacing Click events 
/// with an interception EventHandler that prevents the Click event occuring if the Panel was scrolled.
/// </summary>
/// <param name="control">The control to handle the events for and whose children to recurse through.</param>
private void RecursivelySubscribeChildEvents(Control control)
{
    if (!(control is IPanelItem))
    {
        return;
    }

    // Subscribe the events for the scroll handling
    control.MouseDown += new MouseEventHandler(UIPanel_MouseDown);
    control.MouseMove += new MouseEventHandler(UIPanel_MouseMove);
    control.MouseUp += new MouseEventHandler(UIPanel_MouseUp);

    // Intercept the click event
    FieldInfo eventClickField = typeof(Control).GetField("Click", BindingFlags.NonPublic | BindingFlags.Instance);
    if (eventClickField != null)
    {
        if (_interceptedClickEventHandlers == null)
        {
            _interceptedClickEventHandlers = new Dictionary<Control, EventHandler>();
        }

        // Get the original event and store it against the control for actioning later
        EventHandler eventClick = (EventHandler)eventClickField.GetValue(control);
        _interceptedClickEventHandlers.Add(control, eventClick);

        // Unsubscribe the old event and assign the interception event
        control.Click -= (EventHandler)eventClick;
        control.Click += new EventHandler(UIPanel_ChildClick);
    }

    // Recurse the event subscription operation
    foreach (Control child in control.Controls)
    {
        RecursivelySubscribeChildEvents(child);
    }
}

private void UIPanel_ChildClick(object sender, EventArgs e)
{
    EventHandler interceptedHandler = _interceptedClickEventHandlers[(Control)sender];
    if (interceptedHandler != null)
    {
        // Fire the originally subscribed event if the panel is not scrolling
        if (!_isScrolling)
        {
            interceptedHandler(sender, e);
        }
    }
}