2010-05-14 38 views
6

我想創建一個WPF窗口,其行爲如同一個模式對話框,同時促進在同一應用程序的某些其他窗口上的選定操作。這種行爲的例子可以在Adobe Photoshop中看到,它提供了幾個對話框,允許用戶使用吸管工具從圖像中進行選擇,同時禁用所有其他應用程序功能。一個窗口,表現爲模態和非模態

我在猜測前進的方向是創建一個非模態的,始終在頂部的對話並以編程方式禁用那些不適用於對話的應用程序功能。有沒有一種簡單的方法在WPF中實現這一點?或者可能有我可以採用的設計模式。

+0

我想你是在正確的道路上。您可以在窗口打開/關閉時禁用/啓用代碼隱藏控件,也可以在XAML中使用DataTriggers,根據新窗口是否打開設置各種控件的「IsEnabled」屬性。 – 2010-05-14 14:45:33

回答

0

你在找什麼類似於Multiple Document Interface。這在WPF默認情況下不可用,但有一些努力可以支持這一點,包括freecommercial

這將由您來確定應用程序的當前狀態並啓用/禁用UI元素以響應此操作。

+0

碰巧我正在構建MDI,但MDI的內在本質不支持我描述的行爲。我同意你的第二段,但是有一個潛在的令人頭疼的問題:如果我想禁用一些ICommands,那麼我需要一種以編程的方式來做這件事,並且給了這個想法,這似乎很多工作。 – Chris 2010-05-14 15:05:22

0

我認爲在編程上禁用某些應用程序功能的永遠在頂部的窗口是這樣做的方法。在打開這個表單時,保留一個可以啓用的功能的「白名單」可能會更容易,然後禁用不在列表中的所有內容(而不是試圖維護一個所有內容的「黑名單」不能被啓用)。

2

是的,有一種傳統的方法可以描述程序啓用/禁用功能的位置,但是WPF也提供了幾種WinForms和舊技術中無法實現的新功能。

我將解釋4 WPF特有的方式來做到這一點:

  • 您可以祕密,並自動與圖片的使用矩形與VisualBrush,其內容從而有效地禁止替換窗口的內容它。對於用戶來說,它看起來好像窗口沒有變化,但實際的內容將會出現在圖片的下面,所以您可以將它用於命中測試,甚至將選定的事件轉發給它。

  • 您可以將MergedDictionary添加到窗口的ResourceDictionary中,使得所有TextBoxes成爲TextBlocks,所有按鈕被禁用等等,除非使用自定義附加屬性明確覆蓋。因此,您只需在MergedDictionaries集合中添加或刪除一個對象,而不是循環遍歷所有的UI來選擇啓用/禁用。

  • 您可以使用InputManager以編程方式在禁用窗口的特定部分生成並處理真實的鼠標事件,不允許任何未命中的鼠標事件 - 測試「已批准的內容」。「

  • 使用數據綁定和樣式啓用/禁用單獨控制,而不是通過他們

詳細迭代與窗口

的圖片替換窗口對於這個解決方案,重複你的應用程序並使用包含原始內容和矩形的網格替換每個內容,如下所示:

<Window ...> 
    <Grid> 
    <ContentPresenter x:Name="OriginalContent" /> 
    <Rectangle> 
     <Rectangle.Fill> 
     <VisualBrush Visual="{Binding ElementName=OriginalContent}" /> 
     </Rectangle.Fill> 
    </Rectangle> 
    </Grid> 
</Window> 

這可以通過編程或在窗口上使用模板來完成,但我的首選是創建一個自定義控件並使用其模板創建上述結構。如果做到這一點,你可以簡單地這樣編寫你的窗戶:

<Window ...> 
    <my:SelectiveDisabler> 
    <Grid x:Name="LayoutRoot"> ... </Grid> <!-- Original content --> 
    </my:SelectiveDisabler> 
</Window> 

通過添加鼠標事件處理的矩形,並呼籲ContentPresenter​​確定單擊在原來的內容是什麼對象。從這一點開始,您可以選擇忽略鼠標事件,將其轉發到原始內容進行處理,或者在吸管控制或對象選擇功能的情況下,只需提取所需的對象/信息即可。在MergedDictionary方法

細節很明顯,你可以使用restyle一個資源字典你的整個UI合併到窗口的資源。

一個令人反感的方式是在合併的ResourceDictionary中簡單地創建隱式樣式,以使所有文本框顯示爲TextBlocks,所有Button顯示爲邊框等。這種方式不太好,因爲任何具有自己樣式的TextBox或顯式設置的ControlTemplate可能會錯過更新。此外,您可能無法獲得所有對象,並且無法輕鬆地從按鈕中刪除命令或單擊事件,因爲它們是明確指定的,並且樣式不會覆蓋該對象。

更好的工作方式是讓合併後的ResourceDictionary中的樣式設置一個附加屬性,然後在PropertyChangedCallback中使用代碼隱藏來更新您真正想要更改的屬性。您附加的「ModalMode」屬性(如果設置爲true)會將對象的私有DependencyProperty中的許多屬性(Template,Command,Click,IsEnabled等)的所有本地值和綁定保存,然後用標準值。例如,一個按鈕的Command屬性將被臨時設置爲null。當附加的「ModalMode」屬性爲false時,將從臨時存儲中複製所有原始本地值和綁定,並清除臨時存儲。

此方法通過簡單地添加另一個附加屬性「IgnoreModalMode」,提供了一種方便的方法來選擇性地啓用/禁用部分UI。您可以在任何您不希望應用模態模式更改的UIElement上手動將其設置爲True。你的ModalMode PropertyChangedCallback然後檢查這個,如果是真的,它什麼也不做。在輸入管理辦法

詳細

如果捕獲鼠標,即可獲得鼠標座標無論身在何處很是感動。使用CompositionTarget.TransformToDevice()將這些轉換爲屏幕座標,然後在每個候選窗口上使用CompositionTarget.TransformFromDevice()。如果鼠標座標處於邊界內,則對被禁用的窗口進行命中測試(即使在禁用窗口時仍然可以完成此操作),並且如果您喜歡用戶單擊的對象,請使用InputManager.ProcesInput使鼠標事件成爲在另一個窗口中處理,就好像它沒有被禁用一樣。默認情況下,所有項目

<Setter Property="IsEnabled" Value="{Binding NonModal, Source={x:Static local:ModalModeTracker.Instance}}" /> 

現在:

使用數據詳細綁定

您可以使用樣式按鈕,的MenuItems等的IsEnabled屬性綁定到一個靜態值,這樣當您的NonModal屬性變爲false時,這些樣式將自動禁用。但是,即使在模態模式下,任何單獨的控制都可以用IsEnabled="true"覆蓋以保持啓用狀態。使用MultiBinding和EDF ExpressionBinding可以完成更復雜的綁定,以設置您想要的任何規則。


這些方法都不需要迭代您的可視界面,啓用和禁用功能。您實際選擇了哪些內容取決於您在模式模式下實際想要提供哪些功能以及如何設計其餘的UI。

在任何情況下,WPF都比WinForms日子要容易得多。你不喜歡WPF的力量嗎?

0

我相信解決這個問題的最好方法是使用前面提到的InputManager方法。這種設計模式允許您將命令連接到工具欄按鈕/菜單項等,每個命令都會調用您爲命令指定的CanExecute處理程序。在此處理程序中,如果始終處於最前面的非模態窗口已打開,您可以將該命令設置爲不啓用。

http://msdn.microsoft.com/en-us/library/ms752308.aspx