是否可以非模态地显示WinForms“字体选择器”对话框?或者是否有另一种字体选择器可以非模态使用?非模态WinForms FontDialog?
我们的应用程序有很多窗口,用户经常需要中断他们正在做的事情,并切换到另一个窗口来查看某些内容。如果他们使用任务栏按钮切换窗口,则这往往会导致出现模式对话框的“隐藏对话框”场景,其中用户界面无响应,但它并不是立即显而易见的原因,因为捕获焦点的模式对话框另一个窗口。
是否可以非模态地显示WinForms“字体选择器”对话框?或者是否有另一种字体选择器可以非模态使用?非模态WinForms FontDialog?
我们的应用程序有很多窗口,用户经常需要中断他们正在做的事情,并切换到另一个窗口来查看某些内容。如果他们使用任务栏按钮切换窗口,则这往往会导致出现模式对话框的“隐藏对话框”场景,其中用户界面无响应,但它并不是立即显而易见的原因,因为捕获焦点的模式对话框另一个窗口。
的FontDialog
此行为的主要特征是不是父(所有者)的关系,但实际上您只能通过调用ShowDialog
来使用它,并且没有明显的方法来阻止GUI线程。
幸运的是,是解决该问题的一种方法。我使用BackgroundWorker
来将ShowDialog
调用分流到工作线程上,从而允许GUI线程继续。异步FontDialog
包装类看起来是这样的:
public class FontDialogAsync
{
public event EventHandler<NewFontChosenEventArgs> NewFontChosen;
private readonly IWin32Window parentHandle;
private readonly BackgroundWorker fontDialogWorker = new BackgroundWorker();
private class WindowWrapper : IWin32Window
{
public WindowWrapper(IntPtr hWnd)
{
Handle = hWnd;
}
public IntPtr Handle { get; private set; }
}
public FontDialogAsync(IWin32Window parent)
{
parentHandle = new WindowWrapper(parent.Handle);
fontDialogWorker.DoWork += FontDialogWorkerDoWork;
fontDialogWorker.RunWorkerCompleted += FontDialogWorkerRunWorkerCompleted;
}
private class FontDialogAsyncArgs
{
public readonly IWin32Window ParentForm;
public readonly Font InitialFont;
public FontDialogAsyncArgs(IWin32Window parent, Font initFont)
{
ParentForm = parent;
InitialFont = initFont;
}
}
public void Show(Font font)
{
if (!fontDialogWorker.IsBusy) fontDialogWorker.RunWorkerAsync(new FontDialogAsyncArgs(parentHandle, font));
}
private static void FontDialogWorkerDoWork(object sender, DoWorkEventArgs e)
{
try
{
var args = (FontDialogAsyncArgs)e.Argument;
var fontDialog = new FontDialog { Font = args.InitialFont };
var result = fontDialog.ShowDialog(args.ParentForm);
e.Result = (result == DialogResult.Cancel) ? null : fontDialog.Font;
}
catch (Exception ex)
{
UtilitiesAndConstants.ReportExceptionToCommonLog(ex);
}
}
private void FontDialogWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e1)
{
if (e1.Result == null) return;
if (NewFontChosen != null) NewFontChosen(this, new NewFontChosenEventArgs((Font)e1.Result));
}
}
[请注意,您需要隐藏WindowWrapper
实例内部父窗口的句柄,以保持运行时从提高跨线程异常。]
的EventArgs
类看起来是这样的:
public class NewFontChosenEventArgs : EventArgs
{
public readonly Font FontChosen;
public NewFontChosenEventArgs(Font newFont)
{
FontChosen = newFont;
}
}
...你使用这样的:
private FontDialogAsync nonBlockingFontDialog;
public void SetFont()
{
if (nonBlockingFontDialog == null)
{
nonBlockingFontDialog = new FontDialogAsync(ParentForm);
nonBlockingFontDialog.NewFontChosen += NonBlockingFontDialogNewFontChosen;
}
nonBlockingFontDialog.Show(Font);
}
其中ParentForm
是您要绑定对话框的Windows.Form
实例。由此产生的对话框将相对于父对象变为模态(即,如果不首先解除对话框,您将无法对父对象执行任何操作),但其他应用程序的UI将正常工作。
我不知道确切的代码,但您需要用桌面替换Owner
。您可以使用GetDesktopWindow
API方法获取句柄到桌面如下所述:
http://www.pinvoke.net/default.aspx/user32.getdesktopwindow
一种方法来设置Owner
将创建一个从FontDialog
继承自己的自定义类,然后设置所有者通过protected
CommonDialog.RunDialog
方法,但也可能是其他方式。
编辑:其实,可能会工作到刚刚在桌面手柄发送作为参数传递给ShowDialog
...
OP的描述对我来说是错误的。如果字体对话框是模态的,并且其他窗口被禁用,其他窗口如何在字体对话框前出现? – 2010-05-28 00:00:22
他们不是“禁用”的 - 他们只是不会响应点击。您可以使用它的任务栏图标(这是我们的用户在丢失'模式对话框时通常会这样做的)图标。 – McKenzieG1 2010-05-28 13:45:19
@Rusty - 很好的建议,但对我的团队来说可能不是一个好的投资。我们不使用可定制的字体,字体选择器是一个有点复杂的对话框。 – McKenzieG1 2010-05-28 13:52:46