窗口句柄单击事件

4

我现在在处理WPF窗口上的SizeChanged事件时需要跨越一些障碍。我有一些自定义代码需要在用户完成调整窗口大小之后执行,不幸的是我还没有发现任何与此相关的事件,因此我使用了响应式扩展来限制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);
    });

基本上,它确保在没有 SizeChanged 事件发生200毫秒之后才调用我的自定义代码。这个功能很好,但是如果用户将窗口手柄拖出并继续按住鼠标按钮,则代码仍将被执行。我想确保在鼠标按钮按下时不能执行自定义代码。我尝试连接到PreviewMouseLeftButtonDown,但它仅在单击窗口框架内的鼠标时触发,而不是在单击窗口手柄时触发。有没有类似的事件可以插入到适用于窗口句柄的鼠标按下?或者有人能想到一个适合我问题的解决方法吗?
2个回答

2

Windows会发送专用消息来通知窗口模态大小/移动循环已退出,这个消息叫做WM_EXITSIZEMOVE,当用户松开鼠标按钮或按下Esc键时触发。但是,是的,WPF没有公开它。可以搜索“wpf wm_exitsizemove”找到想要的Interop代码。一个好的搜索结果是这篇博客文章


啊,忘了这个……这可能比我之前的尝试更清晰。 :) - JerKimball
我应该提到一下,实际上我已经有一些使用互操作服务的工作代码了,但希望能找到一个更加简洁的解决方案...很难在代码审查中解释为什么我需要引入低级的Windows相关内容来处理看起来应该如此微不足道的事情。谢谢你的建议,这篇博文很有价值。 - Jesse Carter

1

这可能有点过度,但是为了特别回答您的“如何确定鼠标按钮是否按下?”问题,请看一下这个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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接