頁面與ViewModel(下)


在上一篇博客中,筆者分享了一些從頁面整體的角度對頁面與ViewModel的思考。在本文中筆者希望從相對細節的角度分享一些對頁面與ViewModel的思考。

比如,當我們在更新View Model中的綁定數據時,應該怎樣更新呢?簡單的自然可以用新的數據實例直接替代舊的,但是這樣容易造成UI界面閃爍。尤其是綁定數據是一個列表的情況下,如果整個列表被替換,可以非常明顯的看到列表"一閃"。這樣的用戶體驗無疑是不理想的。那么我們在更新View Model中綁定的數據實例時,可以采用差異更新的方法。以一個數據列表為例,在更新時對比新舊列表,先遍歷新表,對每一個元素查看在舊表中有無對應元素。如果沒有,說明是新增的數據,可以將該新表中的元素同時加入到一個臨時表和舊表中,如果舊表有排序則還需要注意插入的位置。如果有,說明是舊元素更新,則用新元素的值更新舊元素后,將舊元素加入到臨時表中。然后遍歷舊表,對舊表中每一個元素在臨時表中查看有無對應元素。如果有,則不用做任何處理。如果沒有,則說明該元素已經被刪除,應該在舊表中將這個元素移除。這樣對UI界面的更新看起來會比較平滑。

這里寫一下筆者在旺信UWP中所寫的差異化更新算法,權當拋磚引玉。

            var bList = new List<bool>();//輔助列表
            for (int j = 0; j < MainList.Count; j++)//輔助列表初始與舊表同長
            {
                bList.Add(false);
            }
            for (int i = 0; i < groups.Count; i++)//遍歷新表
            {
                bool inserted = false;
                bool contains = false;
                for (int j = 0; j < MainList.Count; j++)//新表中的元素與舊表對比
                {
                    if (groups[i].key != MainList[j].key)//如果不是同一元素
                    {
                        if ((groups[i].key == "群主")//嘗試插入
                            || (
                                MainList[j].key != "群主" && MainList[j].key != "管理員"
                                && (groups[i].key == "管理員" || groups[i].key.CompareTo(MainList[j].key) < 0)
                                )
                            )
                        {
                            MainList.Insert(j, groups[i]);
                            bList.Insert(j, true);
                            inserted = true;
                            Debug.WriteLine("inserted:" + j + "," + groups[i].key);
                            break;
                        }
                    }
                    else//如果是同一元素,用新表元素內容更新舊表
                    {
                        contains = true;
                        MainList[j].update(groups[i]);
                        bList[j] = true;
                        break;
                    }
                }
                if ((!contains) && (!inserted))//不包括在舊表內,也沒有插入,則追加在舊表尾部
                {
                    MainList.Add(groups[i]);
                    bList.Add(true);
                }
            }
            for (int i = bList.Count; i > 0; i--)//對比輔助列表,移除舊表中不應再存在的元素
            {
                if (!bList[i-1])
                {
                    try
                    {
                        MainList.RemoveAt(i - 1);
                    }
                    catch (Exception)
                    {
                        Debug.WriteLine("RemoveAt error:" + i);
                    }
                }
            }

在這段代碼中,用新的數據groups更新舊的數據MainList

再比如,在我們的頁面上,我們一般都會放置一個表示正在加載數據的控件。這個加載中控件的狀態一般也是綁定一個后台數據的。對於一般的頁面,我們可以采取在加載數據前后設置該綁定值的方法來修改頁面所顯示的加載狀態。而對於UWP旺信這種依賴網絡,一個頁面可能同時調用多個網絡接口更新數據的情況,就不是非常合適了。比如a,b兩個接口同時請求數據,將加載狀態置為加載中。如果a接口先返回,則會將加載狀態置為完成。而實際上b接口仍然在請求數據,正確的加載狀態應該還是加載中,直到b接口也返回。為此筆者想到了可以增加一個初始值為0的計數變量,當有加載請求時就自增1,當請求異步結束或回調返回時就自減1,綁定的加載狀態的get方法根據當計數是否為0返回是否在加載狀態。這樣一來就可以使多個加載請求都能正確的改變加載狀態。

在旺信UWP中,筆者就為ViewModel添加了這樣的變量:

 

        public bool isLoading
        {
            get { return loadingCount > 0; }
        }

        private int _loadingCount = 0;

        public int loadingCount
        {
            get { return _loadingCount; }
            set { _loadingCount = (value < 0 ? 0 : value); RaisePropertyChanged("isLoading"); }
        }

在xaml頁面上則將ProgressRing控件的IsActive屬性綁定到isLoading變量上:

<ProgressRing Grid.Row="2" Grid.RowSpan="2" IsActive="{x:Bind thisData.isLoading,Mode=OneWay}" Width="60" Height="60" Foreground="{ThemeResource WXThemeColorBrush}"></ProgressRing>

在調用異步方法前后,並不直接設置isLoading變量,而是采取上面提到的,調用前loadingCount變量自增1,完成后loadingCount變量自減1的方法來影響ProgressRing控件所顯示的加載狀態IsActive。

另外,在使用x:Bind方法時,筆者發現如果把綁定image圖片控件的source綁定到一個string,當綁定的string值為空時會在log中出現exception。即使在string值為空時把image控件隱藏也仍然會出現。然而旺信中數據的屬性值基本都是從服務器傳輸到客戶端的,有時確實會有一些圖片的url為空。這樣一來最好給圖片屬性值都給一個默認值。那么默認值該如何確定呢?如果是普通的占位圖片,那么可能在不該出現圖片的地方顯示。經過實踐,筆者選擇了在應用中加入一個長度為0的圖片,把該圖片的uri作為圖片屬性的默認值。當然這個方法只是消除了log中的exception,具體是否提升了應用的效率,還有待驗證。

以上就是筆者對對頁面與ViewModel的細節的思考,希望能對小伙伴們開發UWP應用有所幫助。當然也歡迎大家拍磚,提出更多更好的經驗,讓我們共同進步。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM