在 winforms 中丢弃 ui 事件的简单解决方案?
Simple solution to discard ui events in winforms?
上下文
在深入探讨这个问题之前,我首先需要指出,我正在尝试在 winforms 应用程序中单击某个按钮时显示 matlab 模态图形。
用于显示图形的 matlab 代码很简单,并且在 matlab 环境中工作正常(即图形是模态的,我无法按预期点击其他任何地方):
function [] = ShowModalDlg()
%[
% Create a modal figure
fig = figure('WindowStyle', 'modal');
% Add some drawings in it
membrane();
% Block execution until figure gets closed.
uiwait(fig);
%]
将 matlab 代码编译为 .NET 并将其集成到我的 winforms 应用程序中也很容易(只需要使用 Matlab Compiler SDK):
private void onBtnClick(object sender, EventArgs e)
{
matlabComponent.ShowModalDlg();
}
问题
在我的 winforms 应用程序中单击按钮时,似乎一切正常:
- MATLAB 图按预期弹出。
- 图形停留在顶部,我无法点击其他任何地方(即模态行为)。
- winforms 线程真正阻塞在
matlabComponent.ShowModalDlg()
直到关闭图形以继续下一行代码。
But,因为有一个but,就好像winform应用还在继续入队UI事件当我关闭 matlab 图时,它们都是连续处理的。例如,当显示 matlab 图形时,如果我尝试移动按住按钮的 winform 没有任何反应,但是一旦我关闭图形,下面的 winform 就会移动!!
我怀疑 matlab 端使用不同的调度模型或其他任何东西(这是 java 在后台)......无论如何我想阻止 winforms 来解决这个问题,例如:
private void onBtnClick(object sender, EventArgs e)
{
using(var oo = new DiscardUIEvents(this))
{
matlabComponent.ShowModalDlg();
}
}
这可能吗?
您可能可以通过使用 Windows API 拦截事件来实现您的需要。
查看 this,它使用 windows API 并防止用户手动移动表单。所以这将是一个为了你自己的目的而调整它的情况。
编辑
尝试将这段代码添加到您的表单中,并在您的点击事件处理程序中设置 _controlsEnabled before/after 然后启动您的对话框。这应该可以防止表格被移动。 (您显然必须对此进行改进以适应您想要忽略的所有事件)。
private void onBtnClick(object sender, EventArgs e)
{
_controlsEnabled = false;
matlabComponent.ShowModalDlg();
_controlsEnabled = true;
}
private bool _controlsEnabled = true;
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch (m.Msg)
{
case WM_SYSCOMMAND:
if (!_controlsEnabled)
{
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
}
break;
}
base.WndProc(ref m);
}
谷歌搜索@DotNetHitMan 的回答,我终于找到了 PeekMessage
让我在离开 matlab 代码时从 windows 队列中删除不需要的消息,所以很容易:
private void onBtnClick(object sender, EventArgs e)
{
// Call matlab
matlabComponent.ShowModalDlg();
// Remove unwanted messages from the queue
NativeMessage msg;
while (PeekMessage(out msg, new HandleRef(this, this.Handle), WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)); // Remove mouse messages
while (PeekMessage(out msg, new HandleRef(this, this.Handle), WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); // Remove keyboard messages
}
注意:PeekMessage
和 NativeMessage
互操作的语法可以从 here.
中读取
编辑
这是将事物重构为 using
语句的代码:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
/// <summary>Allows discarding keyboard and mouse events while thread is blocked in executing some code while its windows message queue is continuing to accumulate events.</summary>
///<example>If you don't disable events while showing matlab modal dialog, events will continue to accumulate in message queue and will be processed after modal dialog is closed (i.e. not the behaviour one expects for a modal dialog).</example>
public class DiscardUiEvents : IDisposable
{
/// <summary>Create new discard object.</summary>
/// <param name="hr">Reference to the control that need to block its events.</param>
/// <remarks>
/// Must be used like this:
///
/// using(new DiscardUiEvent(new HandleRef(myControl, myControl.Handle)))
/// {
/// matlabCompiledCode.ShowSomeModalFigure();
/// }
/// </remarks>
public DiscardUiEvents(HandleRef hr)
{
this.hr = hr;
}
/// <summary>Dispose discard object.</summary>
public void Dispose()
{
if (disposed) { return; }
NativeMessage msg;
while (PeekMessage(out msg, hr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) { /* Remove all mouse messages */ }
while (PeekMessage(out msg, hr, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) { /* Remove all keyboard messages */ }
}
private readonly HandleRef hr;
private bool disposed = false;
private const UInt32 PM_REMOVE = 0x0001;
private const UInt32 WM_MOUSEFIRST = 0x0200;
private const UInt32 WM_MOUSELAST = 0x020D;
private const UInt32 WM_KEYFIRST = 0x0100;
private const UInt32 WM_KEYLAST = 0x0108;
// ReSharper disable MemberCanBePrivate.Local
[StructLayout(LayoutKind.Sequential)]
private struct NativeMessage
{
public IntPtr handle;
public UInt32 msg;
public IntPtr wParam;
public IntPtr lParam;
public UInt32 time;
public Point p;
}
// ReSharper restore MemberCanBePrivate.Local
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PeekMessage(out NativeMessage lpMsg, HandleRef hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
}
可以这样使用:
using(new DiscardUiEvent(new HandleRef(myControl, myControl.Handle)))
{
matlabCompiledCode.ShowSomeModalFigure();
}
上下文
在深入探讨这个问题之前,我首先需要指出,我正在尝试在 winforms 应用程序中单击某个按钮时显示 matlab 模态图形。
用于显示图形的 matlab 代码很简单,并且在 matlab 环境中工作正常(即图形是模态的,我无法按预期点击其他任何地方):
function [] = ShowModalDlg()
%[
% Create a modal figure
fig = figure('WindowStyle', 'modal');
% Add some drawings in it
membrane();
% Block execution until figure gets closed.
uiwait(fig);
%]
将 matlab 代码编译为 .NET 并将其集成到我的 winforms 应用程序中也很容易(只需要使用 Matlab Compiler SDK):
private void onBtnClick(object sender, EventArgs e)
{
matlabComponent.ShowModalDlg();
}
问题
在我的 winforms 应用程序中单击按钮时,似乎一切正常:
- MATLAB 图按预期弹出。
- 图形停留在顶部,我无法点击其他任何地方(即模态行为)。
- winforms 线程真正阻塞在
matlabComponent.ShowModalDlg()
直到关闭图形以继续下一行代码。
But,因为有一个but,就好像winform应用还在继续入队UI事件当我关闭 matlab 图时,它们都是连续处理的。例如,当显示 matlab 图形时,如果我尝试移动按住按钮的 winform 没有任何反应,但是一旦我关闭图形,下面的 winform 就会移动!!
我怀疑 matlab 端使用不同的调度模型或其他任何东西(这是 java 在后台)......无论如何我想阻止 winforms 来解决这个问题,例如:
private void onBtnClick(object sender, EventArgs e)
{
using(var oo = new DiscardUIEvents(this))
{
matlabComponent.ShowModalDlg();
}
}
这可能吗?
您可能可以通过使用 Windows API 拦截事件来实现您的需要。 查看 this,它使用 windows API 并防止用户手动移动表单。所以这将是一个为了你自己的目的而调整它的情况。
编辑
尝试将这段代码添加到您的表单中,并在您的点击事件处理程序中设置 _controlsEnabled before/after 然后启动您的对话框。这应该可以防止表格被移动。 (您显然必须对此进行改进以适应您想要忽略的所有事件)。
private void onBtnClick(object sender, EventArgs e)
{
_controlsEnabled = false;
matlabComponent.ShowModalDlg();
_controlsEnabled = true;
}
private bool _controlsEnabled = true;
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch (m.Msg)
{
case WM_SYSCOMMAND:
if (!_controlsEnabled)
{
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
}
break;
}
base.WndProc(ref m);
}
谷歌搜索@DotNetHitMan 的回答,我终于找到了 PeekMessage
让我在离开 matlab 代码时从 windows 队列中删除不需要的消息,所以很容易:
private void onBtnClick(object sender, EventArgs e)
{
// Call matlab
matlabComponent.ShowModalDlg();
// Remove unwanted messages from the queue
NativeMessage msg;
while (PeekMessage(out msg, new HandleRef(this, this.Handle), WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)); // Remove mouse messages
while (PeekMessage(out msg, new HandleRef(this, this.Handle), WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); // Remove keyboard messages
}
注意:PeekMessage
和 NativeMessage
互操作的语法可以从 here.
编辑
这是将事物重构为 using
语句的代码:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
/// <summary>Allows discarding keyboard and mouse events while thread is blocked in executing some code while its windows message queue is continuing to accumulate events.</summary>
///<example>If you don't disable events while showing matlab modal dialog, events will continue to accumulate in message queue and will be processed after modal dialog is closed (i.e. not the behaviour one expects for a modal dialog).</example>
public class DiscardUiEvents : IDisposable
{
/// <summary>Create new discard object.</summary>
/// <param name="hr">Reference to the control that need to block its events.</param>
/// <remarks>
/// Must be used like this:
///
/// using(new DiscardUiEvent(new HandleRef(myControl, myControl.Handle)))
/// {
/// matlabCompiledCode.ShowSomeModalFigure();
/// }
/// </remarks>
public DiscardUiEvents(HandleRef hr)
{
this.hr = hr;
}
/// <summary>Dispose discard object.</summary>
public void Dispose()
{
if (disposed) { return; }
NativeMessage msg;
while (PeekMessage(out msg, hr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) { /* Remove all mouse messages */ }
while (PeekMessage(out msg, hr, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) { /* Remove all keyboard messages */ }
}
private readonly HandleRef hr;
private bool disposed = false;
private const UInt32 PM_REMOVE = 0x0001;
private const UInt32 WM_MOUSEFIRST = 0x0200;
private const UInt32 WM_MOUSELAST = 0x020D;
private const UInt32 WM_KEYFIRST = 0x0100;
private const UInt32 WM_KEYLAST = 0x0108;
// ReSharper disable MemberCanBePrivate.Local
[StructLayout(LayoutKind.Sequential)]
private struct NativeMessage
{
public IntPtr handle;
public UInt32 msg;
public IntPtr wParam;
public IntPtr lParam;
public UInt32 time;
public Point p;
}
// ReSharper restore MemberCanBePrivate.Local
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PeekMessage(out NativeMessage lpMsg, HandleRef hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
}
可以这样使用:
using(new DiscardUiEvent(new HandleRef(myControl, myControl.Handle)))
{
matlabCompiledCode.ShowSomeModalFigure();
}