我可以在让用户等待之前更新WPF StatusBar文本吗?

时间:2009-04-23 15:50:27

标签: .net wpf multithreading backgroundworker statusbar

我有一个带状态栏的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

更新

灵感来自Alexandraanswer ...

如果我这样做的话,

它的工作,但我对这个解决方案并不满意。有更简单的方法吗?

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
...

7 个答案:

答案 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 Boweanswer

这是我的实施......

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

将它与每个IanGilhamsuggestion的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

...

相关问题