Это меня немного озадачило.
Я написал несколько методов расширения для UIElement, чтобы обеспечить Observables для некоторых событий мыши. Вот соответствующие:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseLeftButtonDown(this UIElement element)
{
return Observable.FromEventPattern<MouseEventArgs>(element, "MouseLeftButtonDown");
}
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseMove(this UIElement element)
{
return Observable.FromEventPattern<MouseEventArgs>(element, "MouseMove");
}
Пока что все так просто. Затем я создаю составное событие, чтобы определить, когда пользователь начинает перетаскивать элемент UIElement (все это используется моим пользовательским элементом управления, точный характер которого не особенно важен). Сначала приведем небольшую вспомогательную функцию, чтобы увидеть, перетащил ли пользователь минимальное расстояние перетаскивания:
private static bool MinimumDragSeen(Point start, Point end)
{
return Math.Abs(end.X - start.X) >= SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(end.Y - start.Y) >= SystemParameters.MinimumVerticalDragDistance;
}
А потом сам составной наблюдаемый:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseDrag(this UIElement element)
{
return Observable.CombineLatest(
element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element)),
element.ObserveMouseMove().Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed),
(md, mm) => new { Down = md, Move = mm })
.Where(i => MinimumDragSeen(i.Down, i.Move.EventArgs.GetPosition(element)))
.Select(i => i.Move);
}
Это все работает хорошо, после большого скрежета зубов и раздражения, что нет никаких достойных примеров перетаскивания на основе Rx с элементами управления WPF (большинство из них - наивно упрощенные дубликаты перетаскивания изображения на холсте) .
Проблема возникает, когда окно развернуто, особенно если дважды щелкнуть строку заголовка. Если в максимизированном макете один из моих элементов управления, который подписан на свой собственный ObserveMouseDrag (), оказывается под курсором мыши, он получает MouseLeftButtonDown и MouseMove, один из которых, очевидно, до максимизации, а другой - после. Конечным результатом является то, что он запускает событие перетаскивания, которое затем немедленно останавливается, когда кнопка мыши отпущена, а затем имеет тенденцию вызывать падение элемента управления на себя, что в некоторых ситуациях в этом приложении действительно что-то делает.
Это очень странно, потому что я не понимаю, почему я должен получать MouseDown и MouseMove, вызванные двойным щелчком по максимизации. Конечно, все связанные с этим события мыши должны обрабатываться Windows, потому что я не использую настраиваемые границы окна или что-то в этом роде.
Итак, у кого-нибудь есть идеи?
На следующий день ...
Исправлено! (С некоторой помощью из ответа Ли ниже и этого вопроса: Как правильно определить конец перетаскивания мышью с помощью Rx? )
Код теперь выглядит так:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseDrag(this UIElement element)
{
var mouseDown = element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element));
var mouseMove = element.ObserveMouseMove();
var stop = Observable.Merge(
element.ObserveMouseUp(),
element.ObserveMouseLeave().Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed)
);
return mouseDown.SelectMany(
md => mouseMove
.Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed)
.Where(ep => MinimumDragSeen(md, ep.EventArgs.GetPosition(element)))
.TakeUntil(stop)
);
}
И обрабатывает минимальное расстояние перетаскивания, события мыши и все виды вещей, необходимые для перетаскивания, чтобы работать должным образом. Ошибка максимизации также исчезла (хотя я до сих пор не совсем понимаю ее, я подозреваю, что обработка мыши может иметь к этому какое-то отношение).
Ключевым моментом здесь является использование SelectMany для обработки нескольких потоков событий от mouseMove до либо mouseUp в элементе управления, либо указатель мыши, оставляющий его при нажатой кнопке мыши.