В данный момент я пытаюсь пройти через некоторые обручи, имея дело с событием 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, но он не срабатывает при щелчке дескриптора окна, а только при щелчке мышью внутри рамки окна. Есть ли какое-либо подобное событие, которое я могу подключить для нажатия мыши, которое применяется к дескриптору окна? Или кто-нибудь может придумать подходящее решение проблемы, с которой я столкнулся?
Ответ 1 · рейтинг 2 · Hans Passant
Windows отправляет специальное сообщение, чтобы уведомить окно о выходе из цикла модального размера/перемещения. WM_EXITSIZEMOVE, срабатывает, когда пользователь отпускает кнопку мыши или нажимает Escape. Но да, WPF не раскрывает это. Погуглите "wpf wm_exitsizemove", чтобы найти нужный код взаимодействия. Хорошо выглядящий хит - эта запись в блоге
Ответ 2 · рейтинг 1 · JerKimball
Это, вероятно, излишне, но конкретно для вашего вопроса "Как я могу понять, нажата ли кнопка мыши?" вопрос, взгляните на эту оболочку 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();
}