Перехват и отмена события Click кнопки WinForms - PullRequest
0 голосов
/ 25 марта 2012

Есть ли способ захватить событие Click для кнопки из родительского элемента управления и предотвратить его возникновение, если переменная внутри родительского элемента управления имеет значение true?

Например:

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 дочернего элемента Control и устанавливаю переменную _isScrolling в true. В событии MouseUp я установил _isScrolling на false.

Поскольку Click происходит до MouseUp, я могу проверить в событии Click, чтобы убедиться, что при нажатии кнопки прокрутка не произошла. Таким образом, в основном, если прокручивается моя панель, мне нужно предотвратить запуск события Click (подписанного во время разработки).

Ответы [ 4 ]

2 голосов
/ 25 марта 2012

Это может дать полезную информацию

Как удалить все обработчики событий из элемента управления

В частности, посмотрите на

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 голос
/ 25 марта 2012

Как альтернативный способ взглянуть на проблему, почему бы просто не создать подкласс хостинговой формы и искать клики по вашим целям.Если ваш preventClick равен true, пометьте сообщение как обработанное, чтобы предотвратить его передачу ребенку.

0 голосов
/ 25 марта 2012

Благодаря Hehewaffles ответ , вот решение, которое я придумал:

/// <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);
        }
    }
}
0 голосов
/ 25 марта 2012

Как насчет отключения кнопок вместо установки переменной в родительском элементе управления в значение 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);
    }
}

Просто замените кнопки по умолчанию на прокручиваемые кнопки.

...