必须在与 DependencyObject 相同的线程上创建 DependencySource
我有一个用 wpf 编写的应用程序,它下载一些网页,解析 html 代码并保存一些值。
class ListOfItems
{
public List<SomeObject> ListToBind;
public void DownloadItems()
{
Task.Factory.StartNew(() =>
{
...
...
if (OnDownloadCompleted != null)
OnDownloadCompleted(this, EventArgs.Empty);
}
}
}
class SomeObject
{
public string NameOfItem;
public MyClass Properties;
}
class MyClass
{
public int Percentage;
public SolidColorBrush Color;
}
这是我正在使用的对象模型。这是简化版本,我不希望你重新组织它,我这样写是有原因的。在 ListOfItems
类中是完成所有工作的方法(内部还使用了一些其他方法来使代码可读) - 下载源代码,解析并用数据填充 ListToBind
,fe
[0] => NameOfItem = "FirstOne", Properties = {99, #FF00FF00}
[1] => NameOfItem = "SecondOne", Properties = {50, #FFFF0000}
etc.
As您可以看到,当此方法 DownloadItems
完成其工作时,会引发 OnDownloadCompleted
事件。在主线程中,
void listOfItems_OnDownloadCompleted(object sender, EventArgs args)
{
dataGrid.Dispatcher.Invoke(new Action(() => {
dataGrid.ItemsSource = ListOfItemsInstance.ListToBind;
}));
}
MainWindow.xaml
上的以下代码 DataGrid 填充了值,因为以下 xaml 代码片段。
<DataGrid Name="dataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Tag" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Color" Binding="{Binding MyClass.Percentage}">
<!--<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding MyClass.Color}" />
</Style>
</DataGridTextColumn.CellStyle>-->
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
它工作得很好。但有这个问题。尝试取消注释已注释的 xaml 代码段,您将收到 Must create DependencySource on same Thread as the DependencyObject.
错误。
最后,我的问题是,如何避免这个错误?
编辑:
最终应该看起来像这样。这张图片取自 MS Excel,并在 Adobe Photoshop 中着色。
I have an application written in wpf, which downloads some webpages, parses html code and saves some values.
class ListOfItems
{
public List<SomeObject> ListToBind;
public void DownloadItems()
{
Task.Factory.StartNew(() =>
{
...
...
if (OnDownloadCompleted != null)
OnDownloadCompleted(this, EventArgs.Empty);
}
}
}
class SomeObject
{
public string NameOfItem;
public MyClass Properties;
}
class MyClass
{
public int Percentage;
public SolidColorBrush Color;
}
This is the object model I'm using. It's simplified version and I don't want you to reorganize it, there is a reason I wrote it this way. In ListOfItems
class is method which does all the job (there are some other methods used inside to make code readable) - downloads source, parses and fills ListToBind
with data, f.e.
[0] => NameOfItem = "FirstOne", Properties = {99, #FF00FF00}
[1] => NameOfItem = "SecondOne", Properties = {50, #FFFF0000}
etc.
As you can see, when this method DownloadItems
completes its job, OnDownloadCompleted
event is raised. In the main thread is following code
void listOfItems_OnDownloadCompleted(object sender, EventArgs args)
{
dataGrid.Dispatcher.Invoke(new Action(() => {
dataGrid.ItemsSource = ListOfItemsInstance.ListToBind;
}));
}
DataGrid on the MainWindow.xaml
is filled with values, because of following xaml code snippet.
<DataGrid Name="dataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Tag" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Color" Binding="{Binding MyClass.Percentage}">
<!--<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding MyClass.Color}" />
</Style>
</DataGridTextColumn.CellStyle>-->
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
It works just fine. But there is this problem. Try to uncomment commented xaml snippet and you will get Must create DependencySource on same Thread as the DependencyObject.
error.
Finally, my question is, how to avoid this error?
EDIT:
It should look like this in the end. This picture is taken from MS Excel and coloured in Adobe Photoshop.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
SolidColorBrush 是一个 Freezable,它是派生的 DispatcherObject。 DispatcherObjects 具有线程关联性 - 即它只能在创建它的线程上使用/交互。然而 Freezables 确实提供了冻结实例的能力。这将防止对对象进行任何进一步的更改,但也会释放线程关联性。因此,您可以更改它,以便您的属性不存储像 SolidColorBrush 这样的 DependencyObject,而只存储 Color。或者,您可以使用冻结方法冻结正在创建的SolidColorBrush。
The SolidColorBrush is a Freezable which is a derived DispatcherObject. DispatcherObjects have thread affinity - i.e it can only be used/interacted with on the thread on which it was created. Freezables however do offer the ability to freeze an instance. This will prevent any further changes to the object but it will also release the thread affinity. So you can either change it so that your property is not storing a DependencyObject like SolidColorBrush and instead just store the Color. Or you can freeze the SolidColorBrush that you are creating using the Freeze method.
我认为标准方法是从 Freezable 派生数据对象,并在将其传递给另一个线程之前将其冻结。一旦对象被冻结,您就无法再更改它,因此不存在线程错误的危险。
另一种选择可能是使数据对象成为普通 C# 对象(不是从
DispatcherObject
派生)并自行实现INotifyPropertyChanged
。I think the standard way is to derive the data object from
Freezable
andFreeze
it before passing it to another thread. Once the object is frozen, you can't change it any more, so there's no danger of threading bugs.Another option might be to make the data object a plain C# object (not derived from
DispatcherObject
) and implementINotifyPropertyChanged
yourself.在主线程上设置
dataGrid.ItemsSource
还不够。您必须在主线程上创建每个项目。像这样的东西:
Its not enough to set your
dataGrid.ItemsSource
on the main thread. You must create each item on the main-thread.Something like: