MrFission Ответов: 1

WPF с использованием MVVM raiseexecutechanged предотвращает индикатор функционирования


Мне действительно нужна панель прогресса в моем приложении WPF MVVM
Использование его обычным способом и простое обновление связанного значения на любом шаге приводит к тому, что панель прогресса не обновляется.
Я нашел эту статью и естественно попытался применить ее к своему решению
Демонстрация BackgroundWorker и ProgressBar[^]

Но Backgroundworker предоставляет свою собственную проблему:
Я получаю исключение, указывающее, что мои переменные (а не Progressbarvalue, который, кажется, обновляется должным образом) используются другим потоком при вызове RaiseExecuteChanged.

Ваша помощь будет очень признательна! Заранее спасибо.

Что я уже пробовал:

//The Command I use
public class BaseCommand : ICommand
    {
        /// <summary>
        /// Specifys the conditions to enable the command
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public virtual bool CanExecute(object parameter)
        {
            return false;
        }

        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Specifys what should happen if the command is being called
        /// </summary>
        /// <param name="parameter"></param>
        public virtual void Execute(object parameter)
        {
        }

        public void RaiseExecuteChanged(object sender, EventArgs e)
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(sender, e);
        }
    }

private CommandErrorAnalysis commandErrorAnalysis;
        public CommandErrorAnalysis CommandErrorAnalysis
        {
            get
            {
                return commandErrorAnalysis;
            }
        }

//How I initialise my Command in the ViewModel

            commandErrorAnalysis = new CommandErrorAnalysis(this);
            addToPropertyChanged(CommandErrorAnalysis);

//My Commands Execute (It's called CommandErrorAnalysis and only gets it's calling ViewModel as parameter in its Constructor)
public override void Execute(object parameter)
        {
            bw = new BackgroundWorker();
            bw.WorkerReportsProgress = true;
            bw.DoWork += new DoWorkEventHandler(doProgress);
            bw.RunWorkerAsync();
        }

//And finally the actual work being done
private void doProgress(object sender, DoWorkEventArgs e)
        {
            bw.ReportProgress(10); //Can be stepped over
            vma.CommandSecurity.Execute(null); //Calls a RaiseExecuteChanged and throws the Exception
        }

//This is the line throwing the exception, with vm being the ViewModel I use the Command from
vm.ColorSecurity = Brushes.Green;

//The Property itself
private SolidColorBrush colorSecurity;
        public SolidColorBrush ColorSecurity
        {
            get
            {
                return colorSecurity;
            }
            set
            {
                colorSecurity = value;
                this.onPropertyChanged("ColorSecurity");
            }
        }

//And finally the actual point of the Exception, which is inside BaseCommand
public void RaiseExecuteChanged(object sender, EventArgs e)
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(sender, e); //<- This is where Visual Studio stops and shows the Exception. 
//I do have the english Version of VS, yet the Exception is german. I'll translate
//"Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet."
//The calling thread cannot use this object, as the object belongs to a different thread.
        }

1 Ответов

Рейтинг:
10

johannesnestler

Всегда одна и та же проблема с такого рода функциональностью... Вы устанавливаете свойство пользовательского интерфейса из другого потока - так что просто маршалируйте вызов к правильному (потоку пользовательского интерфейса). Я использую

Dispatcher dispatcherUIThread
Переменная в моих ViewModels, которую я заполняю (при инициализации приложения) текущим диспетчером приложения (
App.Current.Dispatcher

Таким образом, ваша проблемная линия должна быть изменена на что-то вроде этого
dispatcherUIThread.Invoke(() => vm.ColorSecurity = Brushes.Green);


Я стараюсь проектировать свою фоновую работу как можно более независимо от пользовательского интерфейса, поэтому я не использую, например,
sometextbox.Text 
внутри асинхронных методов, но используйте переменные для ввода и только маршалируйте конечный вывод, информацию об ошибках или прогресс обратно в поток пользовательского интерфейса. (Имейте в виду, что BackgroundWoker выполняет некоторую автоматическую сортировку, вызывая Completed в стартовом потоке - вы можете сделать то же самое с вашим RaiseExecuteChanged...