頁面與ViewModel(上)

来源:http://www.cnblogs.com/ms-uap/archive/2016/06/16/5589644.html
-Advertisement-
Play Games

在UWP淘寶與旺信中,筆者主要負責頁面與控制項的製作,這些工作看似簡單,但要想做的全面細緻仍然需要深入的思考。本文想分享一些在UWP旺信的製作過程中,筆者在UI頁面與控制項製作上體會到的一些心得。可能筆者的有些方法並不見得高明,或者仍需要時間的檢驗,所以也歡迎大家拍磚,共同進步。 UWP旺信是一個非常依 ...


在UWP淘寶與旺信中,筆者主要負責頁面與控制項的製作,這些工作看似簡單,但要想做的全面細緻仍然需要深入的思考。本文想分享一些在UWP旺信的製作過程中,筆者在UI頁面與控制項製作上體會到的一些心得。可能筆者的有些方法並不見得高明,或者仍需要時間的檢驗,所以也歡迎大家拍磚,共同進步。

UWP旺信是一個非常依賴網路的應用,在應用頁面中的很多數據都需要訪問網路才能取到最新的結果,這樣一來網路狀況就會影響到用戶體驗。為了把網路對用戶體驗的影響降低,在UWP旺信中採用了比較通用的做法:數據緩存。在進入頁面以後先把緩存中的數據呈現給用戶,然後在後臺進行聯網拉取最新的數據並更新頁面顯示。以UWP旺信中的群信息頁面為例:在Loaded方法中,頁面會先從緩存中獲得群的數據並更新頁面顯示。接著頁面繼續調用網路介面,並通過介面返回數據對頁面UI進行更新。

看起來是非常簡單的過程,但是其中存在幾個需要註意的問題:

首先,我們在UI頁面上顯示內容時,一般會採取綁定到後臺數據的方法。而UI頁面實際上還有很多狀態,如各個元素的顯示隱藏,Progress控制項的激活與停止等等。這些狀態往往也需要綁定到後臺數據。如果我們把這些內容和狀態的數據都放在頁面的code Behind中,則會大大增加code Behind的複雜度,因此我們可以將這些內容和狀態數據集中放在一個View Model類中,讓UI頁面的元素來綁定。

其次,一般數據綁定的方法有Binding 或 x:Bind。使用Binding方法會比較簡單,只要在code Behind中設置頁面的DataContext為View Model就可以綁定了。而由於微軟官方已經明確了x:Bind方法在運行效率上是優於Binding方法的,那麼我們應當優先使用x:Bind方法。但是x:Bind方法有個缺點就是它綁定的屬性是頁面或控制項自身code Behind的屬性,而不能靈活的選擇不同類型的DataContext來進行綁定。因此我們可以將View Model作為Code behind的一個成員變數,這樣一來也能實現頁面對View Model中數據的綁定。

第三,在UWP應用中,當導航到一個頁面時可以用OnNavigatedTo方法的參數來傳遞數據到該頁面。但當從一個頁面goback到之前的頁面時,卻沒有方法來返回一個數據到之前的頁面。對於這種情況有很多解決辦法:可以使用全局變數,可以讓前一個頁面的緩存模式設為enabled或required併在導航到下一個頁面時傳遞引用型變數參數等等。筆者在旺信中則嘗試了把ViewModel做成單例,讓相關頁面使用同一個ViewModel的方法。例如旺信中群成員,群成員管理,添加群成員,群設置管理等頁面是一系列相關的頁面,需要統一從群成員頁面進入,在應用中不存在同類型頁面有多個實例同時存在的情況,並且有很多數據是共用的。於是筆者為它們創建了一個共同的ViewModel,在這些頁面之間導航時,都使用一個ViewModel實例。這樣就解決了數據回傳的問題。並且用戶操作後,各個頁面都能同步更新。

第四,在更新View Model中與頁面UI綁定的數據時,如果是從頁面的code Behind中採用await的非同步方法來更新的話,會非常順利。UWP旺信獲取群信息的介面是通過回調來返回數據的,當回調方法試圖更新View Model中綁定到UI界面的數據時,會觸發異常,提示"The application called an interface that was marshalled for a different thread."。這是由於回調方法一般來說並不是由UI線程發起的,而頁面綁定的數據只能通過UI線程來修改。如果一定要通過其他線程來修改,需要使用頁面的Dispatcher的RunAsync方法來進行。因此在View Model中我們需要增加一個CoreDispatcher成員變數,在頁面初始化View Model時,將頁面的Dispatcher賦值給該變數。當回調方法更新View Model時,如果CoreDispatcher成員變數不為空,就調用CoreDispatcher來更新綁定數據。

根據這些註意點,以旺信的群成員頁面為例:

在xaml頁面中群成員列表數據源綁定了View Model中的mainList列表變數:

    <Page.Resources>
        <CollectionViewSource x:Name="ContactsCVS" Source="{x:Bind thisData.MainList,Mode=OneWay}"  IsSourceGrouped="True" />
    </Page.Resources>

 而在code Behind中,聲明瞭thisData變數作為ViewModel:

public sealed partial class TribeCardMorePage : BasePage
    {
        //
        private TribeCardMoreVM thisData;
        //
     }

 並且在頁面初始化時把ViewModel類型的單例賦值給它,並將頁面的Dispatcher傳遞給ViewModel:

        public TribeCardMorePage()
        {
            this.InitializeComponent();
            thisData = TribeCardMoreVM.Instance;
            thisData.dispatcher = Dispatcher;
        }

 在頁面的OnNavigatedTo方法中設置ViewModel的一些參數,並嘗試從緩存中取出緩存的數據:

        public override async void OnNavigatedTo(NavigationEventArgs args)
        {
            base.OnNavigatedTo(args);

            if (args != null && args.Parameter != null && args.Parameter is Tribe)
            {
                thisData.para = args.Parameter as Tribe;
            }
            thisData.LoadDataFromCache();
            //

        }

在頁面的Loaded事件中再通過網路更新數據:

        protected async override void OnLoaded(RoutedEventArgs e)
        {
            base.OnLoaded(e);
            await thisData.LoadData();
        }

而ViewModel則是一個繼承了INotifyPropertyChanged介面的數據類型,這樣ViewModel中數據的變化才能通知到頁面,數據綁定才有效。一般我們會寫一個類來繼承INotifyPropertyChanged介面,而ViewModel則繼承這個類就可以了。

    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged([CallerMemberName]string propertyName = null)
        {
            var handler = PropertyChanged;

            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

如上所述,ViewModel需要繼承ObservableObject類,要有供UI綁定的數據,要有更新數據用的Dispatcher,數據取回之後要用Dispatcher來更新:

    public class TribeCardMoreVM : ObservableObject
    {
        //
        private ContactMgr _cmgr = new ContactMgr();
        private volatile static TribeCardMoreVM _instance = null;
        public static TribeCardMoreVM Instance //ViewModel的單例
        {
            get
            {
                if (_instance == null)
                {
                     _instance = new TribeCardMoreVM();
                }
                return _instance;
            }
        }

        public CoreDispatcher dispatcher { get; set; }//頁面的Dispatcher

        private Tribe _para;

        public Tribe para //獲取數據的參數
        {
            get { return _para; }
            set
            {
                _para = value;
                RaisePropertyChanged();
            }
        }

        private ObservableCollection<TribeMemberUIGroup> _MainList = new ObservableCollection<TribeMemberUIGroup>();

        public ObservableCollection<TribeMemberUIGroup> MainList//頁面綁定的數據
        {
            get { return _MainList; }
            set { _MainList = value; RaisePropertyChanged(); }
        }

        public async Task LoadData()
        {
            //
            _cmgr.OnOnlineContactComplete += (ex, tx) =>
            {
                dispatcher?.RunAsync(CoreDispatcherPriority.Normal, () =>  //在回調方法中用dispatcher來更新頁面UI
                {
                    updateTribeMemberUIGroup();
                });
            };
            await _cmgr.OnEventOnlineContactList(); //從網路獲取數據
            //
        }
        //

    }

以上這些就是筆者所體會到的在類似於UWP旺信這種依賴於網路的應用的頁面的實現中需要註意的一些地方。

在下一篇博客中,筆者將進一步分享對於頁面細節實現的體會。歡迎大家關註,拍磚,共同進步。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在Linux中要修改一個文件夾或文件的許可權我們需要用到linux chmod命令來做. 語法如下: 命令中各選項的含義為 操作符號可以是: 設置mode所表示的許可權可用下述字母的任意組合: X 只有目標文件對某些用戶是可執行的或該目標文件是目錄時才追加x 屬性。 實例 修改文件可讀寫屬性的方法 例如 ...
  • 一. 準備工作 下載nsis相關工具包,點擊此下載 1. 安裝程式:nsis-2.46-setup.exe 2. 編輯程式:cnisedit203.exe 3. 幫助文檔:NSIS205幫助文檔.rar 4. 第三方庫:ExecCmd.dll、AddPath.nsh、WriteEnvStr.nsh ...
  • 我們知道,現在能調試.net程式通常有兩個,第一個是ILSpy,還是一個是Reflector,這兩個小反編譯軟體算是我們研究底層代碼中所擁有的一把 鋒利小尖刀~~~,比如你看到的ILSpy這樣的界面圖: 但是呢!!! 用過ILSpy的同學大概都知道,這個毛軟體是調試不了web代碼的。。。也只能調試調 ...
  • 一直以來苦苦尋求適合自己的ORM,之前也用過Entity Framework、ormlite、nhibernate、dapper,都感覺準備步驟繁瑣,除非公司提前已經搭建起來一套成熟的框架,那樣只能做下添肉的工作,短時間內不能獨立搭建。--廢話終止 直接上圖: 就這麼簡單。 ...
  • 方法、函數、過程、靜態變數、類、結構體、構造函數、析構函數、運算符重載、索引器、類型轉換重載、嵌套類型 ...
  • 類 定義新的數據類型以及這些新的數據類型進行相互操作的方法 定義方式: C#中所有的類都是預設由object類派生來的,顯示指定或者省略效果是一樣的,所以上面的兩個例子是完全相同的。 C#中類包括:抽象類、密封類、非抽象類 abstract:表示修飾的類不完整,也就是抽象類,只能用做基類。 在使用是 ...
  • 本文目錄 Asp.net Core 對於授權的改動很友好,非常的靈活,本文以MVC為主,當然如果說webapi或者其他的分散式解決方案授權,也容易就可以實現單點登錄都非常的簡單,可以使用現成的IdentityServer框架或者自定義實現動非常方便和乾凈,如果你在運行示例代碼的時候未達到預期效果,請 ...
  • if語句、switch語句、Loops語句、Jump語句、格式輸出語句 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...