Проблема Threading "Вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток". Любые решения?
У меня есть список, содержащий имена файлов. У меня есть другой список, содержащий возможные действия для переименования этих файлов. Наконец, у меня есть метка, которая отображает предварительный результат. Когда объект выбран в каждом из списков, я хочу отобразить предварительный просмотр. Вы можете выбрать только один файл, но одно или несколько действий. Я использую WPF/Xaml для своего интерфейса. Я решил выполнить мой предварительный просмотр с помощью потока.
Вот часть моего кода:
private Thread _thread;
public MainWindow()
{
InitializeComponent();
_thread = new Thread(DoWork);
}
public void DoWork()
{
while (true)
{
FileData fileData = listViewFiles.SelectedItem as FileData; // ERROR HERE
if (fileData != null)
{
string name = fileData.FileName;
foreach (var action in _actionCollection)
{
name = action.Rename(name);
}
previewLabel.Content = name;
}
Thread.Sleep(1000);
}
}
private void listViewFiles_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_thread.Start();
}
Во время выполнения я получаю сообщение об ошибке "Вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток". в файле FileData fileData = listViewFiles.SelectedItem как FileData; линия. Знаете ли вы, что мне делать?
Ответы
Ответ 1
Вы не можете изменять или получать доступ к пользовательскому интерфейсу из потока nonUI. Поэтому, если вы все еще хотите использовать другой поток, вам нужно добавить некоторую модель (для получения дополнительной информации о привязке и модели попробуйте найти "wpf mvvm" ), а затем привяжите вас к listViewFiles.SelectedItem
к некоторому свойству этого модель позволит вам получить доступ к выделенным видам через потоки. Во-вторых, вам нужно отделить всю логику, которая изменяет интерфейс пользователя или метод lambda, поэтому в конце он может выглядеть так:
public void DoWork()
{
while (true)
{
FileData fileData = Model.SelectedValue;
if (fileData != null)
{
string name = fileData.FileName;
foreach (var action in _actionCollection)
{
name = action.Rename(name);
}
this.Dispatcher.Invoke((Action)()=> //use Window.Dispatcher
{
label3.Content = fileData.FileName;
label4.Content = name;
});
}
Thread.Sleep(1000);
}
}
UPD. Некоторые дополнительные слова о синхронизации с UI: в WPF каждый объект пользовательского интерфейса наследуется от класса DispatcherObject. Таким образом, весь доступ к объекту этого типа может быть сделан только из потока, в котором был создан этот объект, если вы хотите получить доступ к DispatcherObject (DO) из другого потока, вам нужно использовать метод DO.Dispatcher.Invoke(Delegate)
, это заставит ваш код работать с потоком DO, Поэтому в заключение, чтобы запустить код в потоке пользовательского интерфейса, вам нужно использовать Dipatcher любого элемента пользовательского интерфейса, в этом случае мы используем диспетчер окна (предположим, что код в коде окна позади).
Ответ 2
Простым ответом является то, что вы не можете этого сделать: thread A не может напрямую обращаться к объектам winforms (элементам управления), созданным потоком B.
На практике вы можете использовать делегат, чтобы безопасно его выполнять на другом потоке ala:
form.Invoke(new MethodInvoker(() => {
FileData fileData = listViewFiles.SelectedItem as FileData; // ERROR HERE
if (fileData != null)
{
string name = fileData.FileName;
foreach (var action in _actionCollection)
{
name = action.Rename(name);
}
previewLabel.Content = name;
}
}));
Однако вы можете просто
используйте фоновый рабочий: http://msdn.microsoft.com/en-us/library/8xs8549b.aspx
Подробнее: http://weblogs.asp.net/justin_rogers/pages/126345.aspx