Проблема с диспетчером WPF при добавлении элементов tab в элемент управления tab

#c# #wpf #dispatcher

#c# #wpf #диспетчер

Вопрос:

Я получаю сообщение об ошибке при запуске этого кода:

 tabControl1.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => { tabControl1.Items.Add(tbItem); }));   
  

tabcontrol1 жестко запрограммирован в xaml, а элементы tab создаются во время выполнения.
Я получаю сообщение об ошибке:

Исключение TargetInvocationException было необработанным, исключение было вызвано целью вызова.

Я хотел бы услышать любые мысли по этому поводу. Спасибо

Обновить

внутреннее исключение:

{«Вызывающий поток не может получить доступ к этому объекту, поскольку он принадлежит другому потоку».}

полный код метода:

 TabItem tbItem = new TabItem();
                tbItem.Header = worker;       

                Grid grid = new Grid();

                ListBox listBox = new ListBox();
                listBox.HorizontalAlignment = HorizontalAlignment.Stretch;
                listBox.VerticalAlignment = VerticalAlignment.Stretch;
                listBox.ItemsSource = datasource.Where(i => i.Category == worker);

                grid.Children.Add(listBox);
                tbItem.Content = grid;

                tabControl1.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => { tabControl1.Items.Add(tbItem); })); 
  

Метод вызывается с помощью этого:

 Thread newThread = new Thread(myMethod);
            newThread.SetApartmentState(ApartmentState.STA);
            newThread.Start(); 
  

ДРУГОЕ ОБНОВЛЕНИЕ

Это работает:

 tabControl1.Dispatcher.Invoke(DispatcherPriority.Normal, 
                    (Action)(() => 
                    {
                        TabItem tbItem = new TabItem();
                        tbItem.Header = worker;


                        //Grid amp; ListBox(within tab item)
                        Grid grid = new Grid();

                        ListBox listBox = new ListBox();
                        listBox.HorizontalAlignment = HorizontalAlignment.Stretch;
                        listBox.VerticalAlignment = VerticalAlignment.Stretch;
                        listBox.ItemsSource = datasource.Where(i => i.Category == worker);

                        grid.Children.Add(listBox);
                        tbItem.Content = grid;

                        tabControl1.Items.Add(tbItem); 
                    }));                            
  

Комментарии:

1. откуда вы это запускаете? можете ли вы предоставить контекст. а также как вы создали tbItem?

2. Я добавил еще несколько деталей. Что вы думаете?

Ответ №1:

как вы можете видеть, ваш tbItem создается в другом потоке, даже если он пытается отправить его обратно в основной поток gui TabControl.

почему бы не извлечь часть, которая занимает больше времени (для которой вы являетесь потоком usign), и как только вы получите результат обратно, продолжите создание tbItem и добавление его в TabControl в потоке GUI

Пример:

 tabControl.Dispatcher.Invoke calls below function with dataSoruce result it gets 
List<string> result = null;
tabControl.Dispatcher.Invoke((Action<IEnumerable<string>>)ProcessResult, result);

public  void ProcessResult(IEnumerable<string> datasource)
{
   //this is where you do creating TabItem and assigning data source to it and adding to TabControl. 

}
  

Не скомпилирован, пожалуйста, проверьте синтаксис

Комментарии:

1. Я не уверен, что вы имеете в виду. Не могли бы вы опубликовать пример?

Ответ №2:

Проверьте InnerException свойство, чтобы выяснить причину. TargetInvocationException это просто оболочка среды выполнения wpf. Вероятно, ваш лямбда-код выдает ошибку, но без фактического исключения вы не можете сказать.

Редактировать

Вы создаете свои элементы TabItem в другом потоке, поэтому поток GUI не может получить к ним доступ, даже если вы используете диспетчер для фактического добавления. Как вы уже опубликовали в своем последнем фрагменте, вы должны создать TabItem в потоке GUI. Выполняйте вычисления только в другом потоке, и как только результат вернется, выполните фактическое создание TabItem в потоке GUI (при необходимости через Dispatcher ).

Комментарии:

1. Однако вам необходимо привести или создать делегат определенного типа, начиная с Dispatcher. Для вызова требуется Delegate , а не действие. Смотрите: msdn.microsoft.com/en-us/library/ms591596.aspx

2. @Reed: вы правы. Метод, который я имел в виду, на самом деле был методом расширения, чтобы упростить вызов Invoke методов.

Ответ №3:

Проблема в том, что вы создаете свои UIElements в отдельном потоке. Это недопустимо.

Вы можете выполнить свою обработку в фоновом потоке (вызов datasource.Where(i => i.Category == worker); ), но, к сожалению, каждый элемент пользовательского интерфейса должен создаваться и использоваться полностью в основном потоке пользовательского интерфейса.

В вашем случае это означает создание вашего списка и сетки в потоке пользовательского интерфейса внутри вызова диспетчера.

Я бы предложил переписать это как:

 // Load the data on the background...
var data = datasource.Where(i => i.Category == worker);

// Do all work on the UI thread
tabControl1.Dispatcher.Invoke(DispatcherPriority.Normal, 
                (Action)(() => 
                {
                    TabItem tbItem = new TabItem();
                    tbItem.Header = worker;

                    //Grid amp; ListBox(within tab item)
                    Grid grid = new Grid();

                    ListBox listBox = new ListBox();
                    listBox.HorizontalAlignment = HorizontalAlignment.Stretch;
                    listBox.VerticalAlignment = VerticalAlignment.Stretch;
                    listBox.ItemsSource = data;

                    grid.Children.Add(listBox);
                    tbItem.Content = grid;

                    tabControl1.Items.Add(tbItem); 
                }));