【Win 10 應用開發】通過數據綁定更新進度條


實現 INotifyPropertyChanged 接口可以在屬性更改后通知數據的使用者,這個相信大伙兒都知道。於是,有朋友會問:對於要實時顯示進度的情況,比如更新進度條,能用這個實現嗎?

當然是可以的,也很簡單,定義一個類,實現 INotifyPropertyChanged 接口,然后公開表示處理進度的屬性,並且在屬性更改后引發通知事件。

然后把該類的實例與進度條進行綁定即可,和一般的綁定差不多。不過,有一點需要強調:通常是把屬性更改通知發送給UI對象的,多數情況下,我們在處理一些耗時操作都會在另一個線程上執行,這就使得在實現這個接口時,引發PropertyChanged 事件的時候容易發生錯誤。為了避免錯誤發生,在實現接口時,應當用Dispatcher來引發事件,確保能在UI線程上執行。

一個比較不錯的方法,是先弄個抽象類,讓它實現 INotifyPropertyChanged 接口,后面你所定義的各種 Model 類就可以從這個抽象類派生,這樣會相當省事。

比如這樣:

    public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected async void OnPropertyChanged([CallerMemberName] string propName = "")
        {
            await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.High,
                () =>
                {
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
                });
        }
    }

因為在引發屬性更改通知時需要指定屬性名稱,咱們使用一個技巧,在 OnPropertyChanged 方法的參數上加一個 CallerMemberNameAttribute,這么一來,只要在被更改的屬性的set方法中調用這個方法,就會自動把屬性的名字傳給參數了,也不用我們手動輸,既免去繁雜工作,也不容易弄錯。注意參數在附加CallerMemberNameAttribute后一定要給它一個默認值,說白了就是讓這個參數變成可選參數,以方便運行時庫在運行階段修改它。

調用 Window.Current.Dispatcher.RunAsync 方法確保事件引發的代碼是在UI線程上執行的,就不會出現因交叉線程更改用戶界面而導致的異常。

 

好,上面說了一車的廢話,下面我們來定義一個表示進度數據的類,主要包括進度的最大值、最小值,以及當前進度,這個類從剛才定義的 NotifyPropertyChangedBase 類派生。

    public sealed class ProgressData : NotifyPropertyChangedBase
    {
        private int _max, _min, _currvalue;

        public int Max
        {
            get { return _max; }
            set
            {
                if (value != _max)
                {
                    _max = value;
                    OnPropertyChanged();
                }
            }
        }

        public int Min
        {
            get { return _min; }
            set
            {
                if (value != _min)
                {
                    _min = value;
                    OnPropertyChanged();
                }
            }
        }

        public int CurrentValue
        {
            get { return _currvalue; }
            set
            {
                if (value != _currvalue)
                {
                    _currvalue = value;
                    OnPropertyChanged();
                }
            }
        }
    }

 

在用戶界面上聲明 ProgressBar 控件,然后綁定到上面類型的屬性。

        <ProgressBar Name="pb" Height="25" Margin="3,30,5,2"
                     Maximum="{Binding Max}"
                     Minimum="{Binding Min}"
                     Value="{Binding CurrentValue}"
                     SmallChange="1.0"/>

如何讓 ProgressData 實例與 ProgressBar 控件關聯呢,這好辦,因為有一個 DataContext 屬性,它可以賦任何類型的值,然后控件中的綁定會從該屬性的值中尋找數據。

下面我們來關聯一下。

            m_progressdata = new ProgressData();
            m_progressdata.Max = 100;
            m_progressdata.Min = 0;
            m_progressdata.CurrentValue = 0;
            this.pb.DataContext = m_progressdata;

注意看最后一行,不解釋。

 

然后定義一個基於 Task 的異步方法,來模擬在新線程上處理數據。

        async Task TestSomethingAsync()
        {
            while (m_progressdata.CurrentValue < m_progressdata.Max)
            {
                m_progressdata.CurrentValue++;
                await Task.Delay(20);
            }
        }

 

隨后可以進行測試了。

        private async void OnClick(object sender, RoutedEventArgs e)
        {
            m_progressdata.CurrentValue = m_progressdata.Min;
            Button btn = sender as Button;
            btn.IsEnabled = false;
            await TestSomethingAsync();
            btn.IsEnabled = true;
        }

 

至此,就完成進度的綁定了,當你在線程中處理進度時,你可以不用管UI上用什么來顯示進度了,哪怕是用文本顯示,或用進度條來顯示,都無所謂,數據與界面分離。

 

看看效果,還是挺不錯的。

 

這種方法除了在UWP中可用,同樣適用於WPF項目,因為都是一脈相承的,所以老周一直堅持:只要WPF學好了,別的都好辦

 

示例下載地址

 


免責聲明!

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



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