我有一个Image控件,其源绑定到对象的属性(图像的字符串url)。 进行服务呼叫后,我使用新的URL更新数据对象。 调用PropertyChanged事件后,该异常在离开我的代码后引发。
数据结构和服务逻辑都是在不了解UI的核心dll中完成的。 无法访问分派器时如何与UI线程同步?
PS:为了获得Dispatcher而访问Application.Current.RootVisual并不是一个解决方案,因为root视觉位于另一个线程上(这是我需要避免的确切异常)。
PPS:这仅是图像控件的问题,绑定到任何其他ui元素,交叉线程问题已为您处理。
1
| System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...}); |
也看这里。
您是否尝试实现INotifyPropertyChanged?
INotifyPropertyChanged接口用于通知客户端(通常是绑定客户端)属性值已更改。
例如,考虑具有名为FirstName的属性的Person对象。为了提供通用的属性更改通知,Person类型实现INotifyPropertyChanged接口,并在更改FirstName时引发PropertyChanged事件。
为了使更改通知在绑定的客户端和数据源之间的绑定中发生,您的绑定类型应为:
实现INotifyPropertyChanged接口(首选)。
为绑定类型的每个属性提供一个更改事件。
两者都不做。
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
| 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();
}
}
}
}
} |
每当我们要更新与UI相关的项目时,该操作应在UI线程中发生,否则您将收到无效的跨线程访问异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Deployment.Current.Dispatcher.BeginInvoke( () =>
{
UpdateUI(); // DO the actions in the function Update UI
});
public void UpdateUI()
{
//to do :Update UI elements here
} |
我遇到了与此类似的问题,但这是在Windows窗体中:
我有一个具有自己的线程的类,用于更新有关另一个进程的统计信息,我的UI中有一个控件绑定到该对象。我遇到了跨线程调用问题,这是我如何解决的问题:
1 2 3 4 5 6 7 8 9 10
| 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);
} |
这似乎很好用,如果有人提出建议,请告诉我。
Application类上RootVisual的属性getter具有导致该异常的线程检查。通过在我的App.xaml.cs中的我自己的属性中存储根可视对象的调度程序来解决此问题:
1 2 3 4 5 6 7
| 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,则不应获取此异常。