【问题标题】:Create objects in worker thread and bind to them在工作线程中创建对象并绑定到它们
【发布时间】:2011-08-04 16:25:54
【问题描述】:

我在 C#/WPF/.NET 4.0 中遇到了跨线程操作的问题。

情况:

当用户单击按钮然后绑定到树时,我必须创建一个对象树。因为创建需要很长时间(子对象递归实例化),所以我使用了 Thread/BackgroundWorker/Task 来防止 UI 冻结。

问题:

绑定到对象树时,我得到一个 XamlParserException(必须在与 DependencyObject 相同的线程上创建 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>

您好, 多米尼克

【问题讨论】:

  • 如果你能展示你的一些代码会有所帮助。
  • 我通过编辑操作在问题中添加了一些代码 sn-ps。
  • 您不应将[Solved] 添加到您的标题或在您的问题中发布解决方案。您将不得不等待 24 小时,但您应该在自己的答案中发布解决方案,然后您可以接受(延迟后再次)。然后这会向系统和其他用户表明问题已解决。
  • @ChrisF - 对不起 [已解决] 标签。我只是想阻止其他人为已经解决的问题付出努力。我尝试解决了大约两天,并在我发布问题后找到了解决方案。
  • 别太担心 - 关于在您发布后立即解决,这似乎经常发生!

标签: c# wpf exception binding multithreading


【解决方案1】:

您可以在工作线程上创建图标,但在 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));

【讨论】:

  • 谢谢!这是完美的解决方案!
【解决方案2】:

您以前使用过 Dispatcher.Invoke 方法吗?我的理解是,您可以在单独的线程(例如任务)上执行长时间运行的进程,并使用 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)));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 2015-06-06
    • 2013-08-09
    • 1970-01-01
    • 2012-09-26
    相关资源
    最近更新 更多