Создавать объекты в рабочем потоке и привязывать к ним - PullRequest
4 голосов
/ 13 апреля 2011

У меня проблема с межпоточными операциями в C # / WPF / .NET 4.0.

Ситуация:

Мне нужно создать дерево объектовкогда пользователь нажимает кнопку, а затем привязывается к дереву.Поскольку создание занимает много времени (дочерние объекты создаются рекурсивно), я использовал Thread / BackgroundWorker / Task для предотвращения зависания пользовательского интерфейса.

Проблема:

Я получаю исключение XamlParserException (необходимо создать DependencySource в том же потоке, что и объект DependencyObject) при привязке к дереву объектов.

Я понимаю проблему, но как ее можно исправить?Я не могу создать дерево объектов в потоке пользовательского интерфейса, потому что это замораживает пользовательский интерфейс.Но я также не могу создать дерево объектов в другом потоке, потому что тогда я не могу привязаться к нему.

Есть ли способ «маршалировать» объекты в потоке пользовательского интерфейса?

Код обработчика событий (выполняется в потоке пользовательского интерфейса)

    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>

Привет, Доминик

Ответы [ 2 ]

3 голосов
/ 13 апреля 2011

Вы можете создать значок в рабочем потоке, но вам нужно заморозить его перед использованием в потоке пользовательского интерфейса:

            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));
0 голосов
/ 13 апреля 2011

Использовали ли вы метод Dispatcher.Invoke раньше? Насколько я понимаю, вы можете иметь длительный процесс, выполняющийся в отдельном потоке (например, Task), и использовать Dispatcher для обновления элементов управления в потоке пользовательского интерфейса с помощью делегата.

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)));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...