2013-05-07 81 views
4

我正试图在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,但只有在窗口框架内单击鼠标时才会触发窗口句柄。有没有类似的事件可以插入适用于窗口句柄的鼠标?或者任何人都可以为我遇到的问题想出一个合适的解决方法?

回答

2

Windows发送一条专用消息来通知窗口模态大小/移动循环已退出。 WM_EXITSIZEMOVE,当用户放开鼠标按钮或按下Escape时触发。但是,是的,WPF不公开它。谷歌“wpf wm_exitsizemove”找到你想要的互操作代码。一个好看的打击是this blog post

+0

啊,忘了这一点......这可能是比上面我尝试一个更简洁的方法。 :) – JerKimball 2013-05-07 16:36:35

+0

我可能应该提到我实际上有一些工作代码使用互操作服务,但希望找到一些不太沉重的东西......它很难在代码审查中解释为什么我需要拉低级别的窗口东西对于看起来应该如此微不足道的东西。不过谢谢,博客文章是一个很好的阅读。 – 2013-05-07 17:03:46

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(); 
}