Can I update the text of the WPF StatusBar before the user waits?

I have a WPF application with a status bar.

<StatusBar Grid.Row="1" Height="23" Name="StatusBar1" VerticalAlignment="Bottom"> <TextBlock Name="TextBlockStatus" /> </StatusBar> 

I want to display the text there and switch to the β€œWait” clockwise when I do a little work.

This code will update the cursor, but the StatusBar text is not updated ...

 Cursor = Cursors.Wait TextBlockStatus.Text = "Loading..." System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3)) TextBlockStatus.Text = String.Empty Cursor = Cursors.Arrow 

Update

Inspired by Alexandra answer ...

It works if I do this, but I'm not completely happy with this solution. Is there an easier way?

 Delegate Sub Load1() Sub Load2() System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3)) End Sub Dim Load3 As Load1 = AddressOf Load2 Sub Load() Cursor = Cursors.Wait TextBlockStatus.Text = "Loading..." Dispatcher.Invoke(DispatcherPriority.Background, Load3) TextBlockStatus.Text = String.Empty Cursor = Cursors.Arrow End Sub 

I would prefer it to look something like this ...

 Sub Load() Cursor = Cursors.Wait TextBlockStatus.Text = "Loading..." 'somehow put all the Dispatcher, Invoke, Delegate, AddressOf, and method definition stuff here' TextBlockStatus.Text = String.Empty Cursor = Cursors.Arrow End Sub 

Or even better ...

 Sub Load() Cursor = Cursors.Wait ForceStatus("Loading...") System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3)) ForceStatus(String.Empty) Cursor = Cursors.Arrow End Sub Sub ForceStatus(ByVal Text As String) TextBlockStatus.Text = Text 'perform magic' End Sub 

Update

I also tried associating a TextBlock with a public property and implementing INotifyPropertyChanged as IanGilham . This one does not work .

XAML:

 <TextBlock Text="{Binding Path=StatusText}"/> 

Visual Basic:

 Imports System.ComponentModel Partial Public Class Window1 Implements INotifyPropertyChanged Private _StatusText As String = String.Empty Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Property StatusText() As String Get Return _StatusText End Get Set(ByVal value As String) _StatusText = value OnPropertyChanged("StatusText") End Set End Property Shadows Sub OnPropertyChanged(ByVal name As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name)) End Sub ... Sub Load() ... Cursor = Cursors.Wait StatusText = "Loading..." System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3)) StatusText = String.Empty Cursor = Cursors.Arrow ... End Sub ... 
+4
source share
7 answers

You must use BackgroundWorker . The work will have a separate thread, that is, your user interface thread will be free, and your application will still respond.

It will not be an incredibly compact code solution, but it is the most reliable and convenient for your users.

+5
source

You can call easily

 TextBlockStatus.UpdateLayout(); 

immediately after changing the Text property, which should update the control and change the text on the screen.

I also use

 this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate { /* your code here */ })); 

(try) make sure my task is running after the upgrade.

I have to admit that it works ~ 90% - 95% of the time (there are times when the text changes only after the task is completed or it changes with a slight delay), but I could not find anything better.

EDIT to edit the question:

I am not an expert in VB, but if it does not support anonymous built-in methods, then your second way is the one that should work. Try calling UpdateLayout() before calling the dispatcher

 Cursor = Cursors.Wait TextBlockStatus.Text = "Loading..." TextBlockStatus.UpdateLayout(); //include the update before calling dispatcher Dispatcher.Invoke(DispatcherPriority.Background, Load3) TextBlockStatus.Text = String.Empty Cursor = Cursors.Arrow 
+1
source

User Ray provided an answer that solves this problem in another question. His answer is based on Shaun Bowe's answer in the third question.

This is my implementation ...

 Sub UpdateStatus(ByVal Message As String) If Message = String.Empty Then Cursor = Cursors.Arrow Else Cursor = Cursors.Wait End If TextBlockStatus.Text = Message AllowUIToUpdate() End Sub Public Sub AllowUIToUpdate() Dim frame As New DispatcherFrame() Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, New DispatcherOperationCallback(AddressOf JunkMethod), frame) Dispatcher.PushFrame(frame) End Sub Private Function JunkMethod(ByVal arg As Object) As Object DirectCast(arg, DispatcherFrame).Continue = False Return Nothing End Function 

It might be nice to combine this with the XAML binding and INotifyPropertyChanged for IanGilham .

+1
source

here is my code (Status - WPF text block with x: Name = "Status")

(This is C #, but I think it should be portable to VB ...)

  private void Button_Click(object sender, RoutedEventArgs e) { var bw = new BackgroundWorker(); bw.DoWork += DoSomething; bw.RunWorkerAsync(); } private void DoSomething(object sender, DoWorkEventArgs args) { UpdateStatus("Doing Something..."); ... UpdateStatus("Done..."); } private void UpdateStatus(string text) { Status.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => SetStatus(text))); } private void SetStatus(string text) { Status.Text = text; } 
+1
source

Have you tried like this?

 TextBlockStatus.Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => { TextBlockStatus.Text = message; })); 

I had the same problem and for some reason using DispatcherPriority.Loaded worked for me. In addition, there is no need for UpdateLayout.

I hope this works for you, and welcomes

Andre

+1
source

It works well. but pay attention to: DispatcherPriority.Render "

 private static Action EmptyDelegate = delegate() { }; public void RefreshUI(UIElement uiElement) { uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); } public string StatusBarString { set { statusBar.Content = _satusBarString; RefreshUI(statusBar); } } 
+1
source

I would create a public property to save the text box and implement INotifyPropertyChanged . You can easily bind> TextBlock to a property in the xaml file.

 <TextBlock Text="{Binding Path=StatusText}"/> 

Then you just change the property and the user interface will always be updated.

In my work projects at the moment, the class representing the window contains only method handlers that invoke the public interface of the ViewModel / Presenter class. ViewModel contains everything that the user interface must bind and run any and all functions, adding tasks to ThreadPool.

Of course, all of my projects at the moment are related to batch processing, so ThreadPool resolution with concurrency problems makes sense in my case.

Update: Can you try the property approach again, but make sure the property is publicly available. According to this page , you can only bind to public properties.

 Imports System.ComponentModel Partial Public Class Window1 Implements INotifyPropertyChanged Private _StatusText As String = String.Empty Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged // I think the property has to be public for binding to work Public Property StatusText() As String Get Return _StatusText End Get Set(ByVal value As String) _StatusText = value OnPropertyChanged("StatusText") End Set End Property Shadows Sub OnPropertyChanged(ByVal name As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name)) End Sub ... Sub Load() ... Cursor = Cursors.Wait StatusText = "Loading..." System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3)) StatusText = String.Empty Cursor = Cursors.Arrow ... End Sub 

...

0
source

All Articles