Home > database >  How create a Thread for update UI in Mvvm?
How create a Thread for update UI in Mvvm?

Time:02-18

My code must display the time in the TextBlock while a method is running.

Unfortunately the TextBlock never updates and I think the problem is that the Display Thread is not on the same Thread that executes the method.

How can I manage the updating of the TextBlock at the same time as the execution of the method?

I create a Thread ?

Bye

<Window x:Class="WpfApp6.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp6"
    mc:Ignorable="d"
    Title="A320216" Height="450" Width="800">

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

<StackPanel>
    <Button Width="100" Height="100" Command="{Binding MyCommand}" Content="Start"/>
    <TextBlock Text="{Binding Alfa}" />
</StackPanel>    
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace WpfApp98
{
    public class ViewModel : ViewModelBase
    {
        public ViewModel() => Start();

        private string _alfa;
        public string Alfa
        {
            get { return _alfa; }
            set
            {
                _alfa= value;
                base.OnPropertyChanged("Alfa");
            }
        }

        private ICommand _cmd;
        public ICommand MyCommand
        {
            get
            {
                if (_cmd == null)
                    _cmd = new RelayCommand(Command_Execute, Command_CanExecute);

                return _cmd;
            }
        }
        private bool Command_CanExecute(object param)
        {
            return true;
        }
        private void Command_Execute(object param)
        {
            Go();
        }

        void Start()  
        {
            Alfa= DateTime.Now.ToString("HH:mm:ss");  //Show in TextBlock current Time
        }

        void Go() //After click on button .....
        {
            Timer t = new Timer(CB, null, 0, 500);   

            bool is_ok = false;

            is_ok = Calc(); //  I would like the TextBlock to be updated with the current time ("void CB") while this ImportData is running

            t.Dispose();

            if (is_ok == true)
            { MessageBox.Show(" Ok !!"); }
            else
            { MessageBox.Show("Failure"); }
        }

        private void CB(object state)
        {
            Alfa= DateTime.Now.ToString("HH:mm:ss");
        }

        public bool Calc()
        {
            // Calc updated 8 tables on MsSql Server (in this case I use a text file for example) 

            using (StreamWriter sw = File.CreateText(@"C:\temp\test.txt"))
            {
                for (int i = 0; i < 10000; i  ) 
                {
                    sw.WriteLine("Values {0} Time {1} ", i.ToString(), DateTime.Now.ToString("HH:mm:ss"));
                }
            }
            return true;
        }
    }

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        internal void OnPropertyChanged([CallerMemberName] string nameProp = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameProp));
    }

    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }

            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            return _canExecute == null || _canExecute((object)parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested  = value;
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }
        public void Execute(object parameter)
        {
            _execute((object)parameter);
        }
    }
}

CodePudding user response:

You do this by running the computation on a background thread. Often done by calling Task.Run with the Calc method as the parameter. Make the Go method async, await the resulting task, and do any cleanup after.

This lets the UI thread do things like updating the UI. You should also check what flavor of timer you are using, a winforms timer always raises its event on the UI thread, other timers uses a background thread by default. You do not want to have multiple UI threads, since that usually only leads to more complication.

It might also be helpful to show a modal dialog while the computation is running, to prevent the user from doing anything else in the UI. Ideally with a progress bar and a cancel button.

CodePudding user response:

In this case a good solution is replace this code

private void Command_Execute(object param) 
{
    Go;
}

with this

private void Command_Execute(object param) 
{
    Task.Factory.StartNew(Go);
}
  • Related