2016-01-05 2 views
0

Мне нужна была небольшая функция, которая будет ждать выхода левой кнопки мыши и не будет основана на MouseUp event.Лучший способ реализовать функцию WaitForMouseUp()?

Во многих случаях, когда нам это нужно, мы просто пишем обработчик событий для MouseUp event.
Это просто, и он работает.

Есть, однако случаи, когда с помощью MouseUp event не будет полезным,
, например, когда мы уже в другой (разные) обработчик события
и левая кнопка мыши может быть нажата, когда этот обработчик события вызывается, и нам нужно дождаться его выхода. (цель должна иметь единый поток кода, а не разделить его между несколькими местами, которые могли бы быть уже занятым другим кодом)

Я реализовал это следующим образом:

public void WaitForMouseUp() 
{ 
    while((Control.MouseButtons&MouseButtons.Left)!=0) 
     Application.DoEvents(); 
} 

Он работает ,
вы можете использовать его, например, когда вы находитесь в обработчике событий для события Control.Enter,
, и если элемент управления был введен с помощью мыши, эта функция будет блокироваться до тех пор, пока не будет отпущена кнопка мыши.

Я только беспокоиться о одно:
Я использую Application.DoEvents() там, и мне интересно, если есть другой способ вместо использования Application.DoEvents(). (Application.DoEvents(); имеет недостатки возможного реентерабельности, и так, поэтому по этой причине я стараюсь свести к минимуму его использования, когда это возможно)

Каждый имеет представление о том, с чем я могу заменить Application.DoEvents() часть?

+0

Вы не можете использовать [событие mouseup] (https://msdn.microsoft.com/en-us/library/system.windows.forms.control.mouseup (v = vs.110) .aspx)? – bansi

+0

Часто да, но есть моменты, когда вам нужно иметь блок кода, который делает что-то, и не иметь его на нескольких разных обработчиках событий (которые некоторые могут уже выполнять другую работу) – spaceman

+3

* Мне нужна была небольшая функция, которая будет ждать левую кнопку мыши, чтобы выпустить. * Нет. Вы действительно этого не делаете. Вам нужно понимать события. –

ответ

1

Если вы не хотите писать поток в рамках одного метода, вы можете сделать это с помощью объекта TaskCompletionSource.

Ваш поток:

await MouseUp(); 

... 

private Task MouseUp() { 
    _tcs = new TaskCompletionSource(); 
    return _tcs.Task; 
} 

public ... OnMouseUpEvent() { 
    _tcs?.SetResult(true); 
} 

Извините за псевдокод, обновит этот раз я получаю что-то иное, чем мобильный телефон.

OT: комментаторы: подумайте за пределами коробки!

+0

Думайте за пределами коробки? Что вы имеете в виду? –

+0

TaskCompletionSource был создан для включения сценариев, в которых вам нужно (или просто хотите) привести потоки выполнения в поток async/wait, вместо того, чтобы разбивать меня на нагрузки обработчиков событий. Это значительно улучшает читаемость потока метода.Хотя это, вероятно, не предназначалось для событий пользовательского интерфейса, это по-прежнему действительный метод. Просто потому, что что-то было событием, основанным на его создании, не означает, что мы должны сохранять его таким образом, когда есть способы, которые лучше подходят нашему текущему делу. –

+0

Спасибо, Кай. Мне нравится эта идея и будет ее использовать. Также приятно, что кто-то понимает, что вам нужно, в отличие от другого человека здесь ... :) – spaceman

0

Мне нужна была небольшая функция, которая будет ждать выхода левой кнопки мыши.

Нет, вы этого не сделаете. WinForms GUI-программирование управляется событиями, асинхронно. Вы должны использовать событие MouseUp для обнаружения отпускания кнопки мыши. Это означает, что вам нужно реализовать свою логику, используя асинхронные методы, основанные на состоянии, а не синхронную модель, которую вы жаждете.

+0

Дэвид: Вы слишком упрямы и слишком превентивны, пытаясь понять, что делает другой человек, и что ему нужно. Поверьте мне, я использовал событие MouseUp в .NET тысячи раз, начиная с 2001 года. В этой программе я хочу что-то по-другому. Попытайтесь понять, почему, прежде чем сказать людям «нет, вы не понимаете событий». – spaceman

+0

Вы не понимаете модель, управляемую событиями. Именно поэтому вы называете DoEvents, хотя вы знаете, что это неправильно. –

+0

Вызов DoEvents является временным. Вот почему я создал этот вопрос, чтобы узнать другие способы его достижения. Кай ответил. Вы, наоборот, любите спорить. – spaceman

2

Вот отличный способ сделать то, что вы просите. Используйте реактивные расширения Microsoft для создания одной строки кода, чтобы сделать все, что вы хотите.

Реактивные расширения предоставляют множество операторов, которые могут применяться к событиям.

Итак, сначала некоторые основные наблюдаемые, которые непосредственно связаны с нормальными событиями управления:

 var mouseEnters = 
      Observable 
       .FromEventPattern(
        h => button1.MouseEnter += h, 
        h => button1.MouseEnter -= h); 

     var mouseLeaves = 
      Observable 
       .FromEventPattern(
        h => button1.MouseLeave += h, 
        h => button1.MouseLeave -= h); 

     var mouseUps = 
      Observable 
       .FromEventPattern<MouseEventHandler, MouseEventArgs>(
        h => button1.MouseUp += h, 
        h => button1.MouseUp -= h); 

Теперь нам нужен запрос, который будет срабатывать только один раз, когда происходит мышь, но только если мышь вошла в button1, но только до того, как он уйдет.

 var query = 
      mouseEnters 
       .Select(me => mouseUps.Take(1).TakeUntil(mouseLeaves)) 
       .Switch(); 

Теперь подписаться на событие, чтобы быть в состоянии справиться с этим:

 var subscription = 
      query 
       .Subscribe(ep => 
       { 
        /* 
         this code runs for the first mouse up only 
         after each mouse enter on `button1` 
         unless the mouse leaves `button1` 
        */ 
       }); 

Это сейчас, потому что очень просто отписать как тип subscription является IDisposable. Поэтому вы просто вызываете subscription.Dispose();.

Просто NuGet «Rx-WinForms», чтобы получить бит для вашего проекта.

+0

Привет Enigmativity. Большое спасибо за подробный ответ. Это выглядит очень красиво, несмотря на много сложностей кода относительно того, что мне нужно ... (в таком случае я бы предпочел разделить мой код между двумя обработчиками событий: 'Control.Enter' и' Control.MouseUp') – spaceman

+2

@ spaceman - Я ценю ваш комментарий, но если вы говорите, что это большая сложность, то я несколько склонен согласиться с Дэвидом Хеффернаном, что вам, вероятно, нужно немного глубже понять события, чтобы получить то, что вам нужно. Я не пытаюсь быть грубым, немного откровенным. Надеюсь, вы не ошибетесь. – Enigmativity

2

На самом деле то, что предлагает @Kai Brummund, является вариантом моего answer - Force loop to wait for an event. Настройка кода из там MouseUp просто как

public static class Utils 
{ 
    public static Task WhenMouseUp(this Control control) 
    { 
     var tcs = new TaskCompletionSource<object>(); 
     MouseEventHandler onMouseUp = null; 
     onMouseUp = (sender, e) => 
     { 
      control.MouseUp -= onMouseUp; 
      tcs.TrySetResult(null); 
     }; 
     control.MouseUp += onMouseUp; 
     return tcs.Task; 
    } 
} 

и использование является

Control c = ...; 
await c.WhenMouseUp(); 

Тот же метод может быть использован для любого события.

Смежные вопросы