[UWP]不那麼好用的ContentDialog

ContentDialog是UWP開發中最經常使用的組件之一,一個體驗良好的UWP應用很難避免不去使用它。博客園裏也有許多的文章介紹如何來利用ContentDialog實現各類自定義樣式的彈窗界面。不過實際上ContentDialog是一個使人又愛又恨的組件,今天咱們就來講一下ContentDialog的缺點。git

ContentDialog適合實現輕量級的UI需求,但在處理複雜UI需求時很是難用,例如說:程序員

  • 多層級彈窗狀況下的UI實現;
  • MVVM框架下的UI與業務邏輯的分離;
  • 須要彈窗關閉時返回用戶操做結果的狀況。

上述狀況下,若是仍舊使用ContentDialog實現功能需求,會須要不少的代碼來完成界面UI交互,這是多餘且沒有必要的。github

多層級彈窗狀況下的UI實現

先說第一種狀況,多層級彈窗狀況下的UI實現。假設咱們有一個這樣的需求:咱們須要彈出一個窗口讓用戶修改應用設置,同時在用戶修改後點擊「保存設置」按鈕時,彈出一個自定義UI的確認對話框詢問用戶是否肯定保存。c#

怎麼實現呢?很天然的想到,咱們能夠寫兩個ContentDialog,一個是設置界面的彈窗,另一個是自定義UI的確認對話框。先彈出設置彈窗,點擊「保存設置」時彈出確認對話框。聽起來很完美,邏輯上也沒有問題,編碼運行一下呢,應用崩潰了...windows

這是個悲劇,看下VS的崩潰信息:api

Only a single ContentDialog can be open at any time.架構

WTF!!! UWP應用同時只支持喚出一個ContentDialog 麼?這也太坑了吧!框架

不要驚訝,事實上確實如此,關於這點,微軟官方給出的解決方案是這樣的:async

Only one ContentDialog can be shown at a time. To chain together more than one ContentDialog, handle the Closing event of the first ContentDialog. In the Closing event handler, call ShowAsync on the second dialog to show it.mvvm

也就是說想要同時顯示兩個彈窗是不可能的,只能在第一個彈窗關閉後再來打開第二個。

那咱們怎麼讓第二個彈窗出現時仍能保持第一個彈窗的工做狀態呢?在這種狀況下,我能想到兩種解決方法,一是使用MessageDialog代替確認對話框(拋棄掉自定義UI),或者ContentDialog 內使用Frame作Page間導航,須要用戶確認時,導航到確認頁面。可是毫無疑問,這兩種方法都極爲影響用戶體驗。

MVVM框架下的UI與業務邏輯的分離

上面已經說到了ContentDialog 自己的限制使其很難實現複雜UI需求,而這種困難涉及到MVVM框架時狀況會更爲複雜一些。

咱們知道一個好的基於MVVM框架構建的項目必定是結構清晰,UI交互與後臺業務邏輯分離的完美狀態。ContentDialog自己是一個UI組件,若是隻是輕量級的UI需求,好比說只是自定義一個確認對話框,在MVVM項目中使用倒還行。可是若是是一個較爲複雜的多(層級)彈窗交互需求,或者彈窗內涉及到導航服務,這種狀況下,將View層與ViewModel層間的代碼整理清楚就有些困難了。

在以前的一個項目中,我有遇到這樣的狀況,當時的選擇是使用中間人模式,搭建了一箇中介類。這個中介類對ViewModel層提供打開或跳轉到指定彈窗頁面的接口,對View層則實現調度ContentDialog,控制ContentDialog中Frame的頁導航。

這樣看起來好像也還不錯,功能都實現了。可是缺點是仍舊是沒法實現多層彈窗,同時要考慮ViewModel調用彈窗的多種狀況,實現過程比較複雜,並不能算是一個優雅的解決方式。

須要彈窗關閉時返回用戶操做結果的狀況

在不少狀況下,咱們使用彈窗的交互方式並不只僅是交互需求,而是業務邏輯上的須要,咱們想要用戶作出交互,而且返回交互結果給後臺代碼作進一步的處理。

舉個例子說,咱們作一個繪畫應用,咱們提供給用戶一個調色板來選取畫筆顏色,可是這個調色板常駐在畫布有些過於侵佔用戶繪畫空間,咱們的理想狀態是把它作成一個顏色選取彈窗。這個彈窗須要在用戶點擊更換顏色時彈出來讓用戶選擇顏色,若是用戶取消選取顏色則關閉不作任何操做,若是肯定選取某一顏色則關閉並返回選取的顏色。若是用ContentDialog來作會怎麼樣呢?ContentDialog關閉時會返回一個類型爲ContentDialogResult的對象來標識用戶操做,其定義以下:

//
// 摘要:
//     指定用於指示 ContentDialog 的返回值的標識符。
public enum ContentDialogResult
{
    //
    // 摘要:
    //     未點擊按鈕。
    None = 0,
    //
    // 摘要:
    //     主按鈕由用戶點擊。
    Primary = 1,
    //
    // 摘要:
    //     輔助按鈕由用戶點擊。
    Secondary = 2
}

那麼要實現上面的需求咱們須要在ContentDialog中先暫存用戶選取的顏色,在拿到返回結果後,若是值爲ContentDialogResult.Primary則去取出暫存的顏色,不然不作任何處理。

聽起來這已是個完美的方案了,可是仍是有個大問題:咱們選取顏色是在一個顏色盤上點擊想要的顏色的位置取色,而ContentDialog的返回結果是依賴於點擊預約義的幾個按鈕(PrimaryButton/SecondaryButton/CloseButton),這種狀況下,對於UI交互的限制很是大,咱們沒法實如今顏色盤上取色後當即關閉彈窗,而且返回結果。

結尾

說了這麼多,那麼有沒有一個完美的解決方案呢?你問我有沒有,確定是有的啊!請看下圖!

ContentDialog

ContentDialog的內部實現實際上是依賴Popup,這就讓我有了一個大膽的想法,咱們程序員最愛乾的事情是什麼?造輪子呀!ContentDialog很差用,造個好用的新輪子呀!

接下來幾篇博文來教你們如何造一個好用的,適用於MVVM框架的彈窗層組件。有興趣的能夠先看一下個人開源項目HHChaosToolkit中的Picker部分(GitHub連接點這裏)。

好的,本篇博文到此結束,不知道你們有沒有收穫,謝謝你們!

相關文章
相關標籤/搜索