Intercepting and cancelling the Click event of a WinForms Button

柔情痞子 提交于 2019-12-12 04:18:40

问题


Is there a way to capture the Click event of a Button from the parent Control and prevent it occuring if a variable within the parent Control is true?

For example:

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

Hopefully there's a nicer way to do this than the above example. Just to note, I don't have any control over the events assigned to the afforementioned buttons. They're subscribed elsewhere and I just need to prevent them happening if a variable is true on the consuming parent Control.

My real scenario:

I'm using Windows Mobile 6.5 (legacy application rewrite) I've created a panel control that can contain N number of child controls. The panel control allows for iPhone style vertical scrolling. I subscribe to the MouseMove event of the child Control and set a variable _isScrolling to true. In the MouseUp event I set _isScrolling to false.

As Click occurs before MouseUp I can check within the Click event to ensure a scroll hadn't occured when the button was pressed. So basically, if a scroll of my panel occurs, I need to prevent the Click event (subscribed at design time) from firing.


回答1:


This might provide some useful insight

How to remove all event handlers from a control

Specifically, look at

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



回答2:


As an alternate way to look at the problem, why not just Subclass the hosting form and look for clicks on your targets. If your preventClick is true, then mark the message as handled to prevent it from getting passed to the child.




回答3:


What about disabling buttons instead of setting variable within the parent Control to true?

UPDATE: Setting button.Enabled = false definitely will prevent from Click event raising :)

UPDATE 2 (for real scenario): consider creating custom button

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

Just replace default buttons with scrollable buttons.




回答4:


Thanks to Hehewaffles answer, here's the solution I came up with:

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


来源:https://stackoverflow.com/questions/9860306/intercepting-and-cancelling-the-click-event-of-a-winforms-button

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!