Проблема 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