是否可以非模態地顯示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