2010-05-27 30 views
1

是否可以非模态地显示WinForms“字体选择器”对话框?或者是否有另一种字体选择器可以非模态使用?非模态WinForms FontDialog?

我们的应用程序有很多窗口,用户经常需要中断他们正在做的事情,并切换到另一个窗口来查看某些内容。如果他们使用任务栏按钮切换窗口,则这往往会导致出现模式对话框的“隐藏对话框”场景,其中用户界面无响应,但它并不是立即显而易见的原因,因为捕获焦点的模式对话框另一个窗口。

+0

OP的描述对我来说是错误的。如果字体对话框是模态的,并且其他窗口被禁用,其他窗口如何在字体对话框前出现? – 2010-05-28 00:00:22

+0

他们不是“禁用”的 - 他们只是不会响应点击。您可以使用它的任务栏图标(这是我们的用户在丢失'模式对话框时通常会这样做的)图标。 – McKenzieG1 2010-05-28 13:45:19

+0

@Rusty - 很好的建议,但对我的团队来说可能不是一个好的投资。我们不使用可定制的字体,字体选择器是一个有点复杂的对话框。 – McKenzieG1 2010-05-28 13:52:46

回答

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将正常工作。

1

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

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

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

编辑:其实,可能会工作到刚刚在桌面手柄发送作为参数传递给ShowDialog ...