MVVM binding is not updating UI element

Anto BAro 86 Reputation points
2022-02-16T08:48:22.167+00:00

Hi everyone,
my issue is I don't update the user interface (TextBlock).

In summary:
by executing a method under translation (ImportData, which performs a series of Inserts in an MS SQL DB) the textblock should contain the time as the Insert proceeds in the ImportData method.

For this purpose I have created a Timer that is started before the transaction

This is the code involved.

<TextBlock Text="{Binding NumRig}" />

I have also used this statement but without success

<TextBlock Text="{Binding NumRig, UpdateSourceTrigger=PropertyChanged}" />

The property

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

The declaration of the Timer and the transaction that calls ImportData

{
Timer t = new Timer(TimerCallback, null, 0, 500);
using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, new System.TimeSpan(0, 10, 0)))
{
importResult = ImportData();
if (importResult)
ts.Complete();
}
t.Dispose();
}

and now the TimerCallback procedure

1st version of the procedure performed by the Timer (and which does not work)

void TimerCallback(object sender, EventArgs e)
{
NumRig= DateTime.Now.ToString("HH:mm:ss");
}

2nd version of the procedure performed by the Timer (and which does not work)

void TimerCallback(object sender, EventArgs e)
{
Application.Current.Dispatcher.Invoke(() =>
{
NumRig= DateTime.Now.ToString("HH:mm:ss");
});
}

3rd version of the procedure performed by the Timer (and which does not work)

void TimerCallback(object sender, EventArgs e)
{
Application.Current.Dispatcher.Invoke(() =>
{
NumRig= DateTime.Now.ToString("HH:mm:ss");
OnPropertyChanged("NumRig");
});
}

I'm wondering, why does the TextBlock never update?
What am I doing wrong ??

Thanks to who will answer me
Bye

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,755 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,819 questions
{count} votes

4 answers

Sort by: Most helpful
  1. gaerui Jogi 1 Reputation point
    2022-02-16T10:30:56.797+00:00

    Do you know whether the binding is working (including displaying a value in the TextBlock)[?][2]


  2. Peter Fleischer (former MVP) 19,321 Reputation points
    2022-02-16T19:28:14.24+00:00

    Hi,
    I cannot reproduce your problem with following code:

    XAML:

    <Window x:Class="WpfApp1.Window108"
            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:WpfApp108"
            mc:Ignorable="d"
            Title="AntoBAro-5632_220216" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <StackPanel>
        <TextBlock Text="{Binding NumRig}" />
      </StackPanel>
    </Window>
    

    and classes:

    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using System.Windows;
    
    namespace WpfApp108
    {
      public class ViewModel : ViewModelBase
      {
        public ViewModel() => Start();
        private string _NumRig;
        public string NumRig
        {
          get { return _NumRig; }
          set
          {
            _NumRig = value;
            base.OnPropertyChanged("NumRig");
          }
        }
    
         void Start()
        {
          Timer t = new Timer(CB, null, 0, 500);
        }
    
        private void CB(object state)
        {
          NumRig = DateTime.Now.ToString("HH:mm:ss");
        }
    
      }
    
      public class ViewModelBase : INotifyPropertyChanged
      {
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string nameProp = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameProp));
      }
    }
    

  3. Anto BAro 86 Reputation points
    2022-02-17T14:14:08.387+00:00

    This is the code to better explain the situation

    <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="AntoBAro-5632_220216" Height="450" Width="800">
    
            <Window.DataContext>
                <local:ViewModel/>
            </Window.DataContext>
    
            <StackPanel>
                <Button Width="100" Height="100" Command="{Binding MyCommand}" Content="Start"/>
                <TextBlock Text="{Binding NumRig}" />
            </StackPanel>    
        </Window>
    
    
    
    namespace WpfApp6
    {
        public class ViewModel : ViewModelBase
        {
            public ViewModel() => Start();
    
            private string _NumRig;
            public string NumRig
            {
                get { return _NumRig; }
                set
                {
                    _NumRig = value;
                    base.OnPropertyChanged("NumRig");
                }
            }
    
            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()  
            {
                NumRig = DateTime.Now.ToString("HH:mm:ss");  //Show in TextBlock current Time (the binding is ok)
            }
    
            void Go() //After click on button .....
            {
                Timer t = new Timer(CB, null, 0, 500);   
    
                bool is_ok = false;
    
                is_ok = ImportData(); //  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)
            {
                NumRig = DateTime.Now.ToString("HH:mm:ss");
            }
    
            public bool ImportData()
            {
                // ImportData populates 8 tables on MsSql Server, but in this case (for example) I create a text file 
    
                using (StreamWriter sw = File.CreateText(@"C:\temp\0000.txt"))
                {
                    for (long i = 0; i < 6000000; 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);
            }
        }
    }
    
    0 comments No comments

  4. Peter Fleischer (former MVP) 19,321 Reputation points
    2022-02-17T15:14:19.347+00:00

    Hi,
    if your method ImprtData executes in UI thread without waiting (async/await, 100% CPU) there's no time to refresh UI. The best way is to execute ImportData in seperate thread (Task) like in following demo. Replace your ViewModel with following ViewModel.

    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;
    

    ....

      public class ViewModel : ViewModelBase
      {
        public ViewModel() => Start();
    
        private string _NumRig;
        public string NumRig
        {
          get { return _NumRig; }
          set
          {
            _NumRig = value;
            base.OnPropertyChanged("NumRig");
          }
        }
    
        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) => true;
    
        private void Command_Execute(object param) => Task.Factory.StartNew(Go);
    
        void Start()
        {
          NumRig = DateTime.Now.ToString("HH:mm:ss");  //Show in TextBlock current Time (the binding is ok)
        }
    
        void Go() //After click on button .....
        {
          Timer t = new Timer(CB, null, 0, 500);
    
          bool is_ok = false;
    
          is_ok = ImportData(); //  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)
        {
          NumRig = DateTime.Now.ToString("HH:mm:ss");
        }
    
        public bool ImportData()
        {
          // ImportData populates 8 tables on MsSql Server, but in this case (for example) I create a text file 
    
          using (StreamWriter sw = File.CreateText(@"C:\temp\0000.txt"))
          {
            for (long i = 0; i < 6000000; i++)
            {
              sw.WriteLine("Values {0} Time {1} ", i.ToString(), DateTime.Now.ToString("HH:mm:ss"));
            }
          }
          return true;
        }
    
      }
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.