【Win 10 应用开发】通过数据绑定更新进度条

Cle****-he UID.1073626
2016-05-24 发表

实现 INotifyPropertyChanged 接口可以在属性更改后通知数据的使用者,这个相信大伙儿都知道。于是,有朋友会问:对于要实时显示进度的情况,比如更新进度条,能用这个实现吗?

当然是可以的,也很简单,定义一个类,实现 INotifyPropertyChanged 接口,然后公开表示处理进度的属性,并且在属性更改后引发通知事件。

然后把该类的实例与进度条进行绑定即可,和一般的绑定差不多。不过,有一点需要强调:通常是把属性更改通知发送给UI对象的,多数情况下,我们在处理一些耗时操作都会在另一个线程上执行,这就使得在实现这个接口时,引发PropertyChanged 事件的时候容易发生错误。为了避免错误发生,在实现接口时,应当用Dispatcher来引发事件,确保能在UI线程上执行。

一个比较不错的方法,是先弄个抽象类,让它实现 INotifyPropertyChanged 接口,后面你所定义的各种 Model 类就可以从这个抽象类派生,这样会相当省事。

比如这样:

[mw_shl_code=csharp,false]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));
});
}
}[/mw_shl_code]

因为在引发属性更改通知时需要指定属性名称,咱们使用一个技巧,在 OnPropertyChanged 方法的参数上加一个 CallerMemberNameAttribute,这么一来,只要在被更改的属性的set方法中调用这个方法,就会自动把属性的名字传给参数了,也不用我们手动输,既免去繁杂工作,也不容易弄错。注意参数在附加CallerMemberNameAttribute后一定要给它一个默认值,说白了就是让这个参数变成可选参数,以方便运行时库在运行阶段修改它。

调用 Window.Current.Dispatcher.RunAsync 方法确保事件引发的代码是在UI线程上执行的,就不会出现因交叉线程更改用户界面而导致的异常。



好,上面说了一车的废话,下面我们来定义一个表示进度数据的类,主要包括进度的最大值、最小值,以及当前进度,这个类从刚才定义的 NotifyPropertyChangedBase 类派生。

[mw_shl_code=csharp,false]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();
}
}
}
}[/mw_shl_code]


在用户界面上声明 ProgressBar 控件,然后绑定到上面类型的属性。

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


如何让 ProgressData 实例与 ProgressBar 控件关联呢,这好办,因为有一个 DataContext 属性,它可以赋任何类型的值,然后控件中的绑定会从该属性的值中寻找数据。

下面我们来关联一下。

[mw_shl_code=csharp,false] m_progressdata = new ProgressData();
m_progressdata.Max = 100;
m_progressdata.Min = 0;
m_progressdata.CurrentValue = 0;
this.pb.DataContext = m_progressdata;[/mw_shl_code]


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



然后定义一个基于 Task 的异步方法,来模拟在新线程上处理数据。

[mw_shl_code=csharp,false]async Task TestSomethingAsync()
{
while (m_progressdata.CurrentValue < m_progressdata.Max)
{
m_progressdata.CurrentValue++;
await Task.Delay(20);
}
}[/mw_shl_code]

随后可以进行测试了。

[mw_shl_code=csharp,false]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;
}[/mw_shl_code]

至此,就完成进度的绑定了,当你在线程中处理进度时,你可以不用管UI上用什么来显示进度了,哪怕是用文本显示,或用进度条来显示,都无所谓,数据与界面分离。



看看效果,还是挺不错的。

***附件停止解析***

这种方法除了在UWP中可用,同样适用于WPF项目,因为都是一脉相承的。



源码下载:***链接停止解析***


来自:老周博客 开发者交流群:53078485,期待你的加入!


敬告:
为防止不可控的内容风险,本站已关闭新用户注册,新贴的发表及评论;
你现在看到的内容只是互联网用户曾经发表的言论快照,仅用于老用户留存纪念,且仅与科技行业相关,全部内容不代表本站观点及立场;
本站重新开放前已针对包括用户隐私、版权保护、信息安全、国家政策在内的各种互联网法律法规要求,执行了隐患内容的自查、屏蔽和删除;
本站目前所属个人主体,未有任何盈利安排与计划,且与原WFUN.COM所属公司不存在任何关联关系;
如果本帖内容或者相关资源侵犯到您的合法权益,或者您认为存在问题,那么请您务必点此举报或投诉!
全部回复:
怂JR UID.1099841
2016-05-24 回复

好好好 可惜看不懂

Fantasy肖 UID.590405
2016-05-24 使用 Lumia 1520 回复

嗯嗯额,,

本站使用Golang构建,点击此处申请开源鄂ICP备18029942号-4联系站长投诉/举报