非模态WinForms FontDialog?

时间:2010-05-27 18:18:12

标签: .net winforms fonts

是否可以非模态地显示WinForms“字体选择器”对话框?或者除了可以非模态使用的标准字体之外还有另一种字体选择器吗?

我们的应用程序有许多窗口,用户经常需要中断他们正在做的事情并切换到另一个窗口来查看某些内容。如果他们使用任务栏按钮来切换窗口,这往往会导致“隐藏对话”场景与模态对话框,其中UI没有响应,但是没有立即明白为什么,因为已经捕获焦点的模态对话框落后另一个窗口。

2 个答案:

答案 0 :(得分:1)

我不知道确切的代码,但您需要将Owner替换为桌面。您可以使用此处所述的GetDesktopWindow API方法获取桌面的句柄:

http://www.pinvoke.net/default.aspx/user32.getdesktopwindow

设置Owner的一种方法是创建自己的自定义类,该类继承自FontDialog,然后通过protected CommonDialog.RunDialog方法设置所有者,但可能也是其他方式。

编辑:实际上,可能只需将桌面句柄作为参数发送到ShowDialog ...

答案 1 :(得分:0)

这里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的其余部分将正常工作。