Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 我可以在让用户等待之前更新WPF状态栏文本吗?_.net_Wpf_Multithreading_Backgroundworker_Statusbar - Fatal编程技术网

.net 我可以在让用户等待之前更新WPF状态栏文本吗?

.net 我可以在让用户等待之前更新WPF状态栏文本吗?,.net,wpf,multithreading,backgroundworker,statusbar,.net,Wpf,Multithreading,Backgroundworker,Statusbar,我有一个带状态栏的WPF应用程序 <StatusBar Grid.Row="1" Height="23" Name="StatusBar1" VerticalAlignment="Bottom"> <TextBlock Name="TextBlockStatus" /> </StatusBar> 更新 受…的启发 如果我这样做,它会起作用,但我对这个解决方案一点也不满意。有没有更简单

我有一个带状态栏的WPF应用程序

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

更新 受…的启发

如果我这样做,它会起作用,但我对这个解决方案一点也不满意。有没有更简单的方法

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绑定到公共属性并实现为。此不起作用

XAML:

你可以很容易地打电话

TextBlockStatus.UpdateLayout();
在更改Text属性之后,该属性将刷新控件并更改屏幕上的文本

我也用

this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate {
    /* your code here */
}));
(尝试)确保我的任务在刷新完成后运行

我必须承认,它在90%-95%的时间内都能正常工作(有时文本只有在任务完成后才会更改,或者稍微延迟一下才会更改),但我找不到更好的方法

编辑问题的编辑:

我不是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
你应该使用。这项工作将在一个单独的线程中进行,这意味着您的UI线程将是自由的,并且您的应用程序仍将是响应的


在代码方面,它不会是一个非常紧凑的解决方案,但它对用户来说是最健壮和友好的。

我会创建一个public属性来保存文本字段并实现它。您可以轻松地将TextBlock添加到xaml文件中的属性

<TextBlock Text="{Binding Path=StatusText}"/>
…用户在另一个问题中提交了一个。他的回答基于第三个问题中的

这是我的实现

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
将其与XAML绑定和INotifyPropertyChanged per结合起来可能会更好。

这是我的代码(状态是一个带有x:Name=“Status”的WPF文本块)

(它是c#但我认为它应该可以移植到VB…)

你试过这样吗

TextBlockStatus.Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => { TextBlockStatus.Text = message; }));
我也有同样的问题,出于某种原因,使用DispatcherPriority.Loaded对我有效。另外,不需要UpdateLayout

我希望它对你也有用,干杯


安德烈

这个很好用。但请注意:DispatcherPriority.Render“


请参阅我关于Dispatcher的更新。我认为VB不支持语句体中的匿名方法。在此上下文中,我会小心使用Dispatcher.Invoke。您基本上依赖于Invoke的副作用来更新UI。它是有效的,但它不是合同中始终有效的一部分。请注意,您可以移动Sleep c只要调用Invoke(具有足够高的优先级,它将立即完成工作),就可以使用Invoke方法(将其放在Invoke调用之后)然后做任何事情,UI都会得到更新。像这样?很好的帖子+1。我是一个WPF新手,我的状态栏文本也没有更新。我认为告诉用户为每件事旋转一个新线程是站不住脚的。我正在尝试这些解决方案……按照您的建议使用绑定和MVVM当然是个好主意,但它并不能解决问题。
<TextBlock Text="{Binding Path=StatusText}"/>
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
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
    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;
    }
TextBlockStatus.Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => { TextBlockStatus.Text = message; }));
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);
    }
}