2010-05-27 34 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 ...