将进度日志消息从Model更新到MVVM中的ViewModel

时间:2015-08-07 21:36:08

标签: wpf mvvm model backgroundworker viewmodel

我在Model中定义了一个方法,该方法将执行一个长时间运行的脚本,我希望在脚本正在进行时捕获输出消息并通过ViewModel输出到View。我理解为了获得输出消息的实时更新,我应该在后台工作程序中运行Model方法,并在有输出消息报告时引发它的ReportProgress事件,以便在两个单独的线程上运行UI更新和脚本。我遇到的问题是backgroundworker对象是在ViewModel中定义的,因此使用它来调用Model方法是直截了当的,但是如何从Model方法中引发ReportProgress事件呢?我能想到的唯一方法是将backgroundworker作为输入参数传入方法,但我对此感到不安。谁能告诉我这是否是实现MVVM框架的正确方法?

这是我的代码被剥离到最裸露的骨头。在我的View xaml中,我在ViewModel中有一个TextBox绑定到Logger属性和DeployCommand命令:

            <TextBox Grid.Row="1 " Name="txtOutput"  MinHeight="40"
                 Text="{Binding Logger}"
                 IsReadOnly="True" Margin="10,10" VerticalScrollBarVisibility="Auto"
                 IsEnabled="True" MaxLines="2000" TextWrapping="WrapWithOverflow"/>

            <Button x:Name="BtnDeploy"
                    Command="{Binding DeployCommand}"
                    Content="Deploy"
                    Height="23"
                    Margin="5,2"
                    HorizontalAlignment="Right"
                    Width="125"
                    FontFamily="Kalinga"
                    AutomationProperties.AutomationId="DeployButton"/>

在我的ViewModel中,DeployCommand命令将触发方法OnDeploy,而OnDeploy又将使用backgroundworker对象调用Model中的Deploy方法:

    private string logger = string.Empty;
    public string Logger
    {
        get { return logger; }
        set
        {
            logger = value;
            RaisePropertyChanged("Logger");
        }
    }

    public ICommand DeployCommand { get; private set; }

    public MainWindowViewModel()
    {
        _worker = new BackgroundWorker()
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
        _worker.DoWork += worker_DoWork;
        // _worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        _worker.ProgressChanged += worker_ProgressChanged;

        DeployController = new DeploymentModel();

        this.DeployCommand = new DelegateCommand<object>(this.OnDeploy);
    }

    private void OnDeploy(object obj)
    {
        Logger += @"Offline Deployment Started" + System.Environment.NewLine;
        if (!_worker.IsBusy)
        {
            _worker.RunWorkerAsync(DeployController);
        }
    }

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = (BackgroundWorker)sender;
        var deployModel = (DeploymentModel)e.Argument;

        deployModel.Deploy(script);

    }

    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Logger += e.UserState.ToString();   
    }

最后在模型中:

        public bool Deploy(string ScriptFile)
    {
        bool Success = true;
        string strCmdText = string.Format(@"/c ""{0}""", ScriptFile);

        try
        {
            var startInfo = new ProcessStartInfo
                {
                    WindowStyle = ProcessWindowStyle.Hidden,
                    WorkingDirectory = kitFolder,
                    UseShellExecute = false,
                    RedirectStandardError = true,
                    RedirectStandardOutput = true,
                    FileName = "cmd.exe",
                    CreateNoWindow = true,
                    Arguments = strCmdText,
                };

            // Launch shell command to run powersheel script
            using (Process myProcess = Process.Start(startInfo))
            {
                // capturing script output message
                myProcess.OutputDataReceived += (s, e) =>
                    {
                        LogMessage("ExecuteDeploymentKit: " + e.Data);
                    };

                myProcess.ErrorDataReceived += (s, e) =>
                    {
                        Success = false;
                        LogMessage("ExecuteDeploymentKit: ! > " + e.Data);
                    };

                myProcess.BeginErrorReadLine();
                myProcess.BeginOutputReadLine();

                System.Threading.Thread.Sleep(5000);

                myProcess.WaitForExit();
            }

        }
        catch (Exception ex)
        {
            LogMessage("ExecuteDeploymentKit: " + ex.Message);
            return false;
        }

        if (Success)
        {
            LogMessage("ExecuteDeploymentKit: Offline Deployment Kit executed successfully");
        }
        else
        {
            LogMessage("ExecuteDeploymentKit: Offline Deployment Kit failed");
        }

        return Success;
    }

我已经添加了workder_ProgressChanged来处理backgroundworker的ProgressChanged事件,以便更新UI线程中的View但是在我的模型中没有backgroundworker对象,我不能从方法Deploy()中引发ProgressChanged事件

由于

2 个答案:

答案 0 :(得分:0)

如果我理解你的问题,你可能会通过让Model驱动你的viewmodel和view来破坏MVVM的核心原则。如果没有太大的帮助,我会怀疑最好的方法是实际创建一个&#34;服务&#34;。

让你的模特愚蠢,让它只包含数据。想想POCO。然后,使用实现后台工作程序的服务。让View Model运行该服务。 View模型可以调用服务并为该服务提供对实例化模型的引用。这样,您就不会将模型与视图模型紧密耦合。

答案 1 :(得分:0)

标准方法是让您的VM实现IProgress接口,并将您的M转换为IP转发对象。你不应该把它传给VM,因为这可能是一个参考噩梦。

但实际上,后台工作者应该在VM中实现,而不是M.并且您不应该再使用BackgroundWorker并转向新的异步方法。