Событие для щелчка ручки окна

В данный момент я пытаюсь пройти через некоторые обручи, имея дело с событием WPF SizeChanged в окне. У меня есть некоторый пользовательский код, который мне нужно выполнить после, когда пользователь завершит изменение размера окна, к сожалению, нет события, с которым я смог столкнуться для этого, поэтому я создал решение, использующее Reactive Extensions для регулирования события SizeChange:

IObservable<SizeChangedEventArgs> ObservableSizeChanges = Observable
    .FromEventPattern<SizeChangedEventArgs>(this, "SizeChanged")
    .Select(x => x.EventArgs)
    .Throttle(TimeSpan.FromMilliseconds(200));

IDisposable SizeChangedSubscription = ObservableSizeChanges
    .ObserveOn(SynchronizationContext.Current)
    .Subscribe(x => {
        Size_Changed(x);
    });

По сути, это гарантирует, что 200 миллисекунд отсутствия событий SizeChanged должны пройти, прежде чем он вызовет мой пользовательский код. Это отлично работает, однако я столкнулся с проблемой, что если пользователь перетаскивает дескриптор окна и продолжает удерживать кнопку мыши, код все равно будет выполняться. Я хочу убедиться, что пользовательский код не может выполняться, пока кнопка мыши нажата. Я попытался подключиться к PreviewMouseLeftButtonDown, но он не срабатывает при щелчке дескриптора окна, а только при щелчке мышью внутри рамки окна. Есть ли какое-либо подобное событие, которое я могу подключить для нажатия мыши, которое применяется к дескриптору окна? Или кто-нибудь может придумать подходящее решение проблемы, с которой я столкнулся?


person Jesse Carter    schedule 07.05.2013    source источник


Ответы (2)


Windows отправляет специальное сообщение, чтобы уведомить окно о выходе из цикла модального размера/перемещения. WM_EXITSIZEMOVE, срабатывает, когда пользователь отпускает кнопку мыши или нажимает Escape. Но да, WPF не раскрывает это. Погуглите «wpf wm_exitsizemove», чтобы найти нужный код взаимодействия. Хорошо выглядящий хит — эта запись в блоге

person Hans Passant    schedule 07.05.2013
comment
Ах, забыл об этом ... это, вероятно, более чистый подход, чем моя попытка выше. :) - person JerKimball; 07.05.2013
comment
Я, вероятно, должен был упомянуть, что у меня действительно был некоторый рабочий код, использующий службы взаимодействия, но я надеялся найти что-то, что было бы немного менее тяжелым... Трудно объяснить в обзоре кода, почему мне нужно использовать низкоуровневый материал Windows для чего-то, что кажется, что это ДОЛЖНО быть настолько тривиальным. Тем не менее, спасибо, сообщение в блоге хорошо читается. - person Jesse Carter; 07.05.2013

Это, вероятно, излишне, но конкретно для вашего вопроса «Как я могу понять, нажата ли кнопка мыши?» вопрос, взгляните на эту оболочку P/Invoke:

public class ButtonObserver : IDisposable
{
    public struct MouseButtons
    {
        public bool LeftButton;
        public bool RightButton;
    }

    [DllImport("user32.dll")]
    static extern short GetAsyncKeyState(int vKey);
    private const int VK_LBUTTON = 0x01;
    private const int VK_RBUTTON = 0x02;

    private Task _pollTask = null;
    private Subject<MouseButtons> _pollBuffer = new Subject<MouseButtons>();
    private CancellationTokenSource _canceller;

    public IObservable<MouseButtons> PollMouse(int pollDelayMs)
    {
        if(_pollTask == null)
        {
            _canceller = new CancellationTokenSource();
            _pollTask = Task.Factory.StartNew(() =>
            {
                while(!_canceller.IsCancellationRequested)
                {
                    var mbLeft = GetAsyncKeyState(VK_LBUTTON) != 0;
                    var mbRight = GetAsyncKeyState(VK_RBUTTON) != 0;
                    _pollBuffer.OnNext(new MouseButtons{ LeftButton = mbLeft, RightButton = mbRight});
                    Thread.Sleep(pollDelayMs);
                }
            });            
        }
        return _pollBuffer;
    }

    public void Dispose()
    {
        _canceller.Cancel();
        _pollTask.Wait();
        _pollTask = null;
    }
}

Вы можете использовать его как:

void Main()
{
    var buttonObs = new ButtonObserver();
    var buttons = buttonObs.PollMouse(100).Where(mb => mb.LeftButton);
    using(buttons.Subscribe(mb => Console.WriteLine("Left button down")))
    {
        Console.ReadLine();
    }
    buttonObs.Dispose();
}
person JerKimball    schedule 07.05.2013