我在C#/ WPF / .NET 4.0中遇到跨线程操作问题。
情况:
当用户单击按钮然后绑定到树时,我必须创建一个对象树。因为创建需要很长时间(子对象以递归方式实例化),所以我使用Thread / BackgroundWorker / Task来防止UI冻结。
问题:
绑定到对象树时,我得到一个XamlParserException(必须在与DependencyObject相同的Thread上创建DependencySource)。
我理解这个问题,但如何解决?我无法在UI线程上创建对象树,因为这会冻结UI。但我也无法在另一个线程上创建对象树,因为那时我无法绑定它。
有没有办法将对象'封送'到UI线程?
事件处理程序代码(在UI线程上执行)
private void OnDiff(object sender, RoutedEventArgs e)
{
string path1 = this.Path1.Text;
string path2 = this.Path2.Text;
// Some simple UI updates.
this.ProgressWindow.SetText(string.Format(
"Comparing {0} with {1}...",
path1, path2));
this.IsEnabled = false;
this.ProgressWindow.Show();
this.ProgressWindow.Focus();
// The object tree to be created.
Comparison comparison = null;
Task.Factory.StartNew(() =>
{
// May take a few seconds...
comparison = new Comparison(path1, path2);
}).ContinueWith(x =>
{
// Again some simple UI updates.
this.ProgressWindow.SetText("Updating user interface...");
this.DiffView.Items.Clear();
this.Output.Items.Clear();
foreach (Comparison diffItem in comparison.Items)
{
this.DiffView.Items.Add(diffItem);
this.AddOutput(diffItem);
}
this.Output.Visibility = Visibility.Visible;
this.IsEnabled = true;
this.ProgressWindow.Hide();
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
示例绑定
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplate="{StaticResource DataGridIconCellTemplate}"/>
<DataGridTextColumn Header="Status" Binding="{Binding Path=ItemStatus}"/>
<DataGridTextColumn Header="Type" Binding="{Binding Path=ItemType}"/>
<DataGridTextColumn Header="Path" Binding="{Binding Path=RelativePath}"
Width="*"/>
</DataGrid.Columns>
问候, 多米尼克
答案 0 :(得分:3)
您可以在工作线程上创建图标,但在UI线程上使用它之前需要将其冻结:
var icon = Imaging.CreateBitmapSourceFromHIcon(
sysicon.Handle,
System.Windows.Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
icon.Freeze();
Dispatcher.Invoke(new Action(() => this.Icon = icon));
答案 1 :(得分:0)
您以前使用过Dispatcher.Invoke方法吗?我的理解是你可以在一个单独的线程(例如Task)上执行一个长时间运行的进程,并使用Dispatcher使用委托来更新UI线程上的控件。
private void DoWork()
{
// Executed on a separate thread via Thread/BackgroundWorker/Task
// Dispatcher.Invoke executes the delegate on the UI thread
Dispatcher.Invoke(new System.Action(() => SetDatesource(myDatasource)));
}