0

I'm writing a program that reads cmd script output and shows the results in a log (View Model). Since the script running for a long time, I want to be able to see the output after each line is written. Now I can see the output only when the process ends. I have no idea how to do it în Wpf-MVVM.

StepViewModel


        public StepsViewModel()
        {
            RunSteps = new RelayCommand(OnRunSteps);
            Steps = new ObservableCollection<Step>();
            AddSteps();
        }

        private void OnRunSteps(object obj)
        {
            IsEnabled = false;
            foreach (var step in Steps)
            {
                if (step.IsChecked)
                    StepsManager.Instance.RunStep(step);
            }
            IsEnabled = true;
        }  

StepsManager

        public void RunStep(Step step)
        {
            switch (step.Name)
            {
                case "MyStep":
                    MyStep(0); // 0 is the index of the step
                    break;
            }
        } 

        private void MyStep(int stepIndex)
        {
            VMLocator.LogsViewModel.Logs.Add(new Log("MyStep", false)); // fale -> green colore, red -> red color
;
            string command = @"mycmd";

            ExecuteCmd(command);
        }

        private void ExecuteCmd(string command)
        {
            System.Diagnostics.ProcessStartInfo procStartInfo =
                new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command)
                {
                    RedirectStandardError = true,
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };

            // The following commands are needed to redirect the standard output.
            // This means that it will be redirected to the Process.StandardOutput StreamReader.
            // Do not create the black window.
            System.Diagnostics.Process
                proc = new System.Diagnostics.Process
                {
                    StartInfo = procStartInfo
                }; // Now we create a process, assign its ProcessStartInfo and start it
            proc.Start();

            List<string> outputData = new List<string>();
            List<string> errorData = new List<string>();

            proc.OutputDataReceived += (s, e) =>
            {
                lock (outputData)
                {
                    if (!string.IsNullOrEmpty(e.Data))
                     {
                       outputData.Add(e.Data);
                       UpdateLog(e.Data, stepIndex);
                     }                      
                }
            };

            proc.ErrorDataReceived += (s, e) =>
            {
                lock (errorData)
                {
                    if (!string.IsNullOrEmpty(e.Data))
                    {
                        errorData.Add(e.Data);
                        UpdateLog(e.Data, stepIndex);
                    }
                }
            };

            proc.BeginOutputReadLine();
            proc.BeginErrorReadLine();

            proc.WaitForExit();

        }

        private void UpdateLog(string log)
        {
            if(few checks for log)
                 VMLocator.LogsViewModel.Logs.Add(new Log(log, false)); // fale -> green colore, red -> red color
        }

LogViewModel

        public ObservableCollection<Log> Logs { get; set; }
        #endregion

        public LogsViewModel()
        {
            Logs = new RelayCommand.AsyncObservableCollection<Log>();
        }

Log

        public string Message { get; set; }
        public string Color { get; set; }

        public Log(string message, bool error)
        {
            Message = message;
            Color = error ? "Red" : "Green";
        }
        public Log(string message, string color)
        {
            Message = message;
            Color = color;
        }

1 Answers1

0

The trick with this sort of task is to ensure that WPF gets a change notification event when the data changes.

If you make outputData and errorData observable collections, then they'll notify WPF when they change.

Robin Bennett
  • 3,192
  • 1
  • 8
  • 18
  • Can you give an example? I date them ObservableCollection,but how they can help when I use UpdateLog? – Răzvan Zavalichi Nov 11 '19 at 16:07
  • Oh, sorry, I didn't realise you were binding to `Logs`. In that case, we need to work out whether the problem is `Logs` not being updated, or WPF not noticing. Can you confirm that `UpdateLogs` is running at the right time, and show use the binding, and check for binding errors in the output? – Robin Bennett Nov 11 '19 at 16:08
  • Robin, now UpdateLog doesn't work because it is inside of another thread than main thread. But if I put it after ExecuteCmd function, then It will get call at final of threads, after all steps are done, but I want in real time, because the cmd is pretty long. – Răzvan Zavalichi Nov 11 '19 at 16:15
  • Ah, I was about to suggest a threading issue. There are ways to update the UI thread from a background thread. https://stackoverflow.com/questions/4253088/updating-gui-wpf-using-a-different-thread and https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the?rq=1 – Robin Bennett Nov 11 '19 at 16:17
  • My problem is with MVVM – Răzvan Zavalichi Nov 11 '19 at 17:32