线程中的绑定?
在一个文件下载程序中(可参见著名的SLMultiFileUploader)
DataGrid绑定了List<File>
其中File类继承了INotifyPropertyChanged接口,并且至少有FileName(文件名),SIZE(大小),Percentage(已下载的百分
比),BytesDownloaded(已下载的字节数)这几个属性。
现在我使用多线程,将文件分为若干块,为每一块创建一个线程并分配一个FileDownLoader类来进行下载
问题来了,怎样在子线程中修改文件的Percentage、BytesDownloaded等属性,使得下载情况能反映在界面上?
我试过在子线程中直接修改,即类似file.Percentage = 100;结果程序开始下载后,界面直接白了。。。
不知道问题描述得是否清楚,希望大家指点。。。如果分不够,我再加,本人可用分还是挺多的。。。
[解决办法]
引用官方帮助文档:
System.Windows.Threading.Dispatcher 类当前只提供从非用户界面 (UI) 线程在 UI 上运行代码的支持。
您可以通过 DependencyObject.Dispatcher 和 ScriptObject.Dispatcher 属性访问 UI 线程的 Dispatcher 对象。这些方法是实例方法,但这些类型的实例无法频繁从非 UI 线程访问。但是,该应用程序的 Deployment 对象是 DependencyObject,并且它可通过其 Current 属性用于任何线程。
您可以调用 CheckAccess 方法以确定调用方是否处于 UI 线程上。如果调用方不处于 UI 线程上,则可以调用 BeginInvoke 以便对 UI 线程运行指定的委托。
如果是要显示“已用时间”、“估计剩余时间”,则要使用 System.Windows.Threading.DispatcherTimer 。如果使用 System.Threading.Timer,则值得注意的是 Timer 运行于与用户界面线程 (UI) 不同的线程上。为了访问 UI 线程上的对象,需要使用 Dispatcher.BeginInvoke 将操作发布到 UI 线程的 DispatcherTimer 上,而当使用 DispatcherTimer 时,这不是必需的。
[解决办法]
给一段代码
using System;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Ink;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;using System.Threading;namespace SilverlightApplication11{ public partial class MainPage : UserControl { private static PersonList Persons = new PersonList(); private static SynchronizationContext SynchronizationContext_UI = SynchronizationContext.Current; public MainPage() { // 为初始化变量所必需 InitializeComponent(); Persons.Add(new Person { Name = "测试1" }); Persons.Add(new Person { Name = "测试2" }); listbox.ItemsSource = Persons; Thread thread = new Thread(run); thread.Start(); } public static void run() { Thread.Sleep(new TimeSpan(0, 0, 5)); SynchronizationContext_UI.Post(delegate { Persons.Add(new Person { Name = "测试3" }); }, null); } } public class Person : System.ComponentModel.INotifyPropertyChanged { public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } private string _Name = string.Empty; public string Name { get { return this._Name; } set { if (this._Name != value) { this._Name = value; this.OnPropertyChanged("Name"); } } } } public class PersonList : System.Collections.ObjectModel.ObservableCollection<Person> { }}