我有一个带状态栏的WPF应用程序。
<StatusBar Grid.Row="1"
Height="23"
Name="StatusBar1"
VerticalAlignment="Bottom">
<TextBlock Name="TextBlockStatus" />
</StatusBar>
我想在那里显示文字,当我做少量工作时切换到沙漏等待光标。
此代码将更新光标,但StatusBar 文本不会更新 ...
Cursor = Cursors.Wait
TextBlockStatus.Text = "Loading..."
System.Threading.Thread.Sleep(New TimeSpan(0, 0, 3))
TextBlockStatus.Text = String.Empty
Cursor = Cursors.Arrow
它的工作,但我对这个解决方案并不满意。有更简单的方法吗?
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
我宁愿它看起来像这样......
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
甚至更好......
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
我还尝试将TextBlock绑定到公共属性,并将INotifyPropertyChanged实现为IanGilham suggested。 不起作用。
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
...
答案 0 :(得分:5)
您应该使用BackgroundWorker。该工作将发生一个单独的线程,这意味着您的UI线程将是免费的,您的应用程序仍将是响应。
它不会是一个代码非常紧凑的解决方案,但它对用户来说是最强大和最友好的。
答案 1 :(得分:1)
您可以轻松致电
TextBlockStatus.UpdateLayout();
在您更改Text属性后立即刷新控件并更改屏幕上的文本。
我也用
this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate {
/* your code here */
}));
到(尝试)确保我的任务在刷新完成后运行。
我不得不承认,它的工作时间约为90% - 95%(有时候文本会在任务完成后发生变化,或者稍有变化时会发生变化)但我找不到更好的东西。< / p>
编辑问题的编辑:
我不是VB的专家,但如果它不支持匿名内联方法,那么你的第二种方式就是应该工作的方法。尝试在调用调度程序之前调用UpdateLayout()
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
答案 2 :(得分:1)
用户Ray在另一个问题中提交了answer that solves this problem。在第三个问题中,他的答案基于Shaun Bowe的answer。
这是我的实施......
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
将它与每个IanGilham的suggestion的XAML绑定和INotifyPropertyChanged结合起来可能会很好。
答案 3 :(得分:1)
这是我的代码(状态是带有x的WPF TextBlock:Name =“Status”)
(这是c#,但我认为它应该可以移植到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;
}
答案 4 :(得分:1)
你有没有试过这个?
TextBlockStatus.Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => { TextBlockStatus.Text = message; }));
我有同样的问题,出于某种原因使用DispatcherPriority.Loaded为我工作。此外,不需要UpdateLayout。
我希望它也适合你,干杯,
安德烈
答案 5 :(得分:1)
这很好用。但请注意: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);
}
}
答案 6 :(得分:0)
我会创建一个 public 属性来保存文本字段并实现INotifyPropertyChanged。您可以轻松地将bind TextBlock添加到xaml文件中的属性。
<TextBlock Text="{Binding Path=StatusText}"/>
然后您只需更改属性,UI将始终是最新的。
在我的工作项目中,表示Window的类只包含方法处理程序,它们调用ViewModel / Presenter类的公共接口。 ViewModel包含UI需要绑定的任何内容,并通过向ThreadPool添加作业来启动任何和所有功能。
当然,我目前的所有项目都涉及批处理,所以让ThreadPool处理并发问题在我的案例中是有意义的。
更新:您可以再次尝试属性方法,但要确保该属性是公开的。根据{{3}},您只能绑定到公共属性。
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
...