Необходимо создать DependencySource в том же потоке, что и DependencyObject
у меня есть приложение, написанное на 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
С данными, f.e.
[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;
}));
}
DataGrid в MainWindow.xaml
заполняется значениями из-за следующего фрагмента кода 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.
ошибка.
наконец, мой вопрос в том, как избежать этого ошибка?
EDIT:
это должно выглядеть так в конце концов. Эта фотография взята из MS Excel и окрашена в Adobe Photoshop.
3 ответов
на SolidColorBrush это Freezable который является производным DispatcherObject. DispatcherObjects имеют сродство потоков-i.e его можно использовать/взаимодействовать только с потоком, на котором он был создан. Однако Freezables предлагаем возможность заморозить экземпляр. Это предотвратит любые дальнейшие изменения объекта, но также освободит сродство потоков. Таким образом, вы можете либо изменить его, чтобы ваше свойство не хранило DependencyObject как SolidColorBrush и вместо этого просто сохранить цвет. Или вы можете заморозить SolidColorBrush, который вы создаете с помощью заморозить метод.
Я думаю, что стандартный способ-вывести объект данных из Freezable
и Freeze
это, прежде чем передать его в другой поток. После того, как объект заморожен, вы не можете изменить его больше, так что нет никакой опасности резьбы ошибок.
другой вариант может заключаться в том, чтобы сделать объект данных простым объектом C# (не производным от DispatcherObject
) и реализовать INotifyPropertyChanged
себя.
его недостаточно, чтобы установить ваш dataGrid.ItemsSource
в основном потоке. Вы должны создать каждый элемент в основном потоке.
Что-то вроде:
List<SomeObject> l = new List<SomeObject>();
foreach(var item in ListOfItemsInstance.ListToBind)
{
l.Add(new SomeObject(){NameOfItem = item.NameOfItem, Properties = item.Properties });
}
dataGrid.ItemsSource = l;