1. 程式人生 > >[UWP]不那麽好用的ContentDialog

[UWP]不那麽好用的ContentDialog

返回 業務 清晰 分享 time 利用 com aos 顏色

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

ContentDialog適合實現輕量級的UI需求,但在處理復雜UI需求時非常難用,例如說:

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

上訴情況下,如果仍舊使用ContentDialog實現功能需求,會需要很多的代碼來完成界面UI交互,這是多余且沒有必要的。

多層級彈窗情況下的UI實現;

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

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

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

Only a single ContentDialog can be open at any time.

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

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

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.

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

那我們怎麽讓第二個彈窗出現時仍能保持第一個彈窗的工作狀態呢?在這種情況下,我能想到兩種解決方法,一是使用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的內部實現其實是依賴Popup,這就讓我有了一個大膽的想法,我們程序員最愛幹的事情是什麽?造輪子呀!ContentDialog不好用,造個好用的新輪子呀!

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

好的,本篇博文到此結束,不知道大家有沒有收獲,謝謝大家!

[UWP]不那麽好用的ContentDialog