Silverlight数据绑定跨线程问题
我有一个图像控件,它的源绑定到对象的属性(图像的字符串url)。在进行服务调用之后,我用一个新的URL更新数据对象。在调用PropertyChanged事件后,异常在离开我的代码后抛出 数据结构和服务逻辑都在一个不了解UI的核心dll中完成。当我无法访问调度程序时,如何与UI线程同步 PS:访问Application.Current.RootVisual以获取调度程序不是一个解决方案,因为root visual位于不同的线程上(导致我需要防止的确切异常)Silverlight数据绑定跨线程问题,silverlight,multithreading,data-binding,Silverlight,Multithreading,Data Binding,我有一个图像控件,它的源绑定到对象的属性(图像的字符串url)。在进行服务调用之后,我用一个新的URL更新数据对象。在调用PropertyChanged事件后,异常在离开我的代码后抛出 数据结构和服务逻辑都在一个不了解UI的核心dll中完成。当我无法访问调度程序时,如何与UI线程同步 PS:访问Application.Current.RootVisual以获取调度程序不是一个解决方案,因为root visual位于不同的线程上(导致我需要防止的确切异常) PPS:这只是图像控件的问题,绑定到任何
PPS:这只是图像控件的问题,绑定到任何其他ui元素,跨线程问题会为您处理。您是否尝试过实现?应用程序类上的getter for RootVisual属性具有导致该异常的线程检查。我通过在我的App.xaml.cs中的我自己的属性中存储根visual的dispatcher来解决这个问题:
public static Dispatcher RootVisualDispatcher { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Page();
RootVisualDispatcher = RootVisual.Dispatcher;
}
如果随后在App.RootVisualDispatcher而不是Application.Current.RootVisual.Dispatcher上调用BeginInvoke,则不应出现此异常。我遇到了与此类似的问题,但这是在windows窗体中: 我有一个类,它有自己的线程,更新关于另一个进程的统计信息,在我的UI中有一个控件,它被数据绑定到这个对象。我遇到了跨线程调用问题,下面是我如何解决的:
Form m_MainWindow; //Reference to the main window of my application
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
if(m_MainWindow.InvokeRequired)
m_MainWindow.Invoke(
PropertyChanged, this, new PropertyChangedEventArgs(propertyName);
else
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
这似乎是伟大的工作,如果有人有建议,请让我知道
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});
另外,当我们想要更新UI相关的项目时,也要查看该操作应该发生在UI线程中,否则您将获得无效的跨线程访问异常
Deployment.Current.Dispatcher.BeginInvoke( () =>
{
UpdateUI(); // DO the actions in the function Update UI
});
public void UpdateUI()
{
//to do :Update UI elements here
}
INotifyPropertyChanged
接口用于通知客户端(通常是绑定客户端)属性值已更改
例如,考虑一个具有FieldNew属性的对象对象。为了提供通用属性更改通知,Person类型实现INotifyPropertyChanged接口,并在更改FirstName时引发PropertyChanged事件
要在绑定客户端和数据源之间的绑定中发生更改通知,绑定类型应: 实现INotifyPropertyChanged
接口(首选)
为绑定类型的每个属性提供更改事件
不要两者都做
示例:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
// Change the namespace to the project name.
namespace TestNotifyPropertyChangedCS
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer type
// in the list does.
public partial class Form1 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();
// This DataGridView control displays the contents of the list.
private DataGridView customersDataGridView = new DataGridView();
// This BindingSource binds the list to the DataGridView control.
private BindingSource customersBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
// Set up the "Change Item" button.
this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Click +=
new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);
// Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);
this.Size = new Size(400, 200);
}
private void Form1_Load(object sender, EventArgs e)
{
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
// Attach the BindingSource to the DataGridView.
this.customersDataGridView.DataSource =
this.customersBindingSource;
}
// Change the value of the CompanyName property for the first
// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer> customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer>;
// Change the value of the CompanyName property for the
// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
}
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统图;
使用System.Runtime.CompilerServices;
使用System.Windows.Forms;
//将名称空间更改为项目名称。
命名空间TestNotifyPropertyChangedCS
{
//此表单演示如何使用BindingSource进行绑定
//DataGridView控件的列表。该列表不包含
//引发更改通知。但是DemoCustomer类型
//在列表中没有。
公共部分类Form1:Form
{
//此按钮导致更改列表元素的值。
私有按钮changeItemBtn=新按钮();
//此DataGridView控件显示列表的内容。
私有DataGridView CustomerDataGridView=新DataGridView();
//此BindingSource将列表绑定到DataGridView控件。
private BindingSource CustomerBindingSource=新BindingSource();
公共表格1()
{
初始化组件();
//设置“更改项目”按钮。
this.changeItemBtn.Text=“变更项目”;
this.changeItemBtn.Dock=DockStyle.Bottom;
this.changeItemBtn.Click+=
新建事件处理程序(changeItemBtn_单击);
this.Controls.Add(this.changeItemBtn);
//设置DataGridView。
CustomerDataGridView.Dock=DockStyle.Top;
this.Controls.Add(CustomerDataGridView);
该尺寸=新尺寸(400200);
}
私有void Form1\u加载(对象发送方、事件参数e)
{
//创建并填充DemoCustomer对象列表
//它将向DataGridView提供数据。
BindingList customerList=新建BindingList();
添加(DemoCustomer.CreateNewCustomer());
添加(DemoCustomer.CreateNewCustomer());
添加(DemoCustomer.CreateNewCustomer());
//将列表绑定到BindingSource。
this.CustomerBindingSource.DataSource=customerList;
//将BindingSource附加到DataGridView。
this.customersDataGridView.DataSource=
此文件为.customersBindingSource;
}
//更改第一个项目的CompanyName属性的值
//单击“更改项目”按钮时列表中的项目。
void changeItemBtn\u单击(对象发送方,事件参数e)
{
//从BindingSource获取对列表的引用。
BindingList客户列表=
将this.CustomerBindingSource.DataSource作为BindingList;
//更改的CompanyName属性的值
//列表中的第一项。
customerList[0]。CustomerName=“Tailspin玩具”;
customerList[0]。电话号码=“(708)555-0150”;
}
}
//这是一个简单的客户类
//实现IPropertyChange接口。
公共类DemoCustomer:INotifyPropertyChanged
{
//这些字段包含公共属性的值。
私有Guid idValue=Guid.NewGuid();
私有字符串customerNameValue=string.Empty;
私有字符串phoneNumberValue=string.Empty;
公共事件属性更改事件处理程序属性更改;
//此方法由每个属性的集合访问器调用。
//应用于可选propertyName的CallerMemberName属性