#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.aspx2. @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);
}));