I've got a WPF application with a status bar.
<StatusBar Grid.Row="1"
Height="23"
Name="StatusBar1"
VerticalAlignment="Bottom">
<TextBlock Name="TextBlockStatus" />
</StatusBar>
I'd like to display text there and switch to the hourglass Wait cursor when I do a small amount of work.
This code will update the cursor, but the StatusBar text does not update...
Cursor = Cursors.Wait
TextBlockStatus.Text = "Loading..."
System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3))
TextBlockStatus.Text = String.Empty
Cursor = Cursors.Arrow
Inspired by Alexandra's answer...
It works if I do it this way, but I'm not at all happy with this solution. Is there a simpler 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'd rather it instead looked 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
I've also tried to bind the TextBlock to a public property and implement INotifyPropertyChanged as IanGilham suggested. This 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
...
You should use BackgroundWorker. The work will take place an a separate thread, meaning your UI thread will be free and your application will still be responsive.
It's not going to be an incredibly compact solution code-wise, but it's the most robust and friendly to your users.
You can easily call
TextBlockStatus.UpdateLayout();
right after you change the Text property which should refresh the control and change the text on the screen.
I also use
this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate {
/* your code here */
}));
to (try to) make sure that my task runs after the refreshing is done.
I have to admit, it works ~90% - 95% of the time (there are times when text changes only after the task has finished or it changes with a slight delay) but I couldn't find anything better.
EDIT for the question's edit:
I'm not an expert in VB but if it doesn't support anonymous inline 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With