Do you know whether the binding is working (including displaying a value in the TextBlock)[?][2]
MVVM binding is not updating UI element
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
4 answers
Sort by: Most helpful
-
-
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)); } }
-
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); } } }
-
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; } }