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

實現 INotifyPropertyChanged 接口能夠在屬性更改後通知數據的使用者,這個相信大夥兒都知道。因而,有朋友會問:對於要實時顯示進度的狀況,好比更新進度條,能用這個實現嗎?異步

固然是能夠的,也很簡單,定義一個類,實現 INotifyPropertyChanged 接口,而後公開表示處理進度的屬性,而且在屬性更改後引起通知事件。async

而後把該類的實例與進度條進行綁定便可,和通常的綁定差很少。不過,有一點須要強調:一般是把屬性更改通知發送給UI對象的,多數狀況下,咱們在處理一些耗時操做都會在另外一個線程上執行,這就使得在實現這個接口時,引起PropertyChanged 事件的時候容易發生錯誤。爲了不錯誤發生,在實現接口時,應當用Dispatcher來引起事件,確保能在UI線程上執行。測試

一個比較不錯的方法,是先弄個抽象類,讓它實現 INotifyPropertyChanged 接口,後面你所定義的各類 Model 類就能夠從這個抽象類派生,這樣會至關省事。this

好比這樣:spa

    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線程上執行的,就不會出現因交叉線程更改用戶界面而致使的異常。code

 

好,上面說了一車的廢話,下面咱們來定義一個表示進度數據的類,主要包括進度的最大值、最小值,以及當前進度,這個類從剛纔定義的 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 控件,而後綁定到上面類型的屬性。blog

        <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學好了,別的都好辦

 

示例下載地址

相關文章
相關標籤/搜索