4

ステータスバー付きの 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 をパブリック プロパティにバインドし、IanGilhamが提案したようにINotifyPropertyChangedを実装しようとしました。これは機能しません

XAML:

<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

    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

7 に答える 7

6

BackgroundWorkerを使用する必要があります。作業は別のスレッドで行われます。つまり、UI スレッドは解放され、アプリケーションは引き続き応答します。

コード的には非常にコンパクトなソリューションにはなりませんが、最も堅牢でユーザーにとって使いやすいソリューションです。

于 2009-04-23T17:53:58.243 に答える
1

これが私のコードです(ステータスはx:Name = "Status"のWPFTextBlockです)

(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;
    }
于 2010-06-08T15:26:35.667 に答える
1

ユーザーRayは、別の質問でこの問題を解決する回答を提出しました。彼の答えは、3 番目の質問でのShaun Bowe答えに基づいています。

これは私の実装です...

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提案に従って、これを XAML バインディングと INotifyPropertyChanged と組み合わせるとよいでしょう。

于 2009-04-24T13:10:21.113 に答える
1

このようにしてみましたか?

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

私は同じ問題を抱えていましたが、何らかの理由で DispatcherPriority.Loaded を使用するとうまくいきました。また、UpdateLayout も必要ありません。

あなたにもうまくいくことを願っています、乾杯、

アンドレ

于 2010-08-26T19:35:26.647 に答える
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);
    }
}
于 2012-02-21T11:38:52.423 に答える
1

簡単に呼び出すことができます

TextBlockStatus.UpdateLayout();

コントロールを更新して画面上のテキストを変更する Text プロパティを変更した直後。

私も使っています

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

リフレッシュが完了した後にタスクが実行されることを確認します。

認めざるを得ませんが、それは 90% ~ 95% の確率で機能します (タスクが終了した後にのみテキストが変更されるか、わずかに遅れて変更される場合があります) が、これ以上のものは見つかりませんでした。

質問の編集の編集:

私は VB の専門家ではありませんが、匿名のインライン メソッドをサポートしていない場合は、2 番目の方法が有効です。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
于 2009-04-23T16:05:18.673 に答える
0

テキスト フィールドを保持するパブリックプロパティを作成し、INotifyPropertyChangedを実装します。TextBlock を xaml ファイルのプロパティに簡単にバインドできます。

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

次に、プロパティを変更するだけで、UI は常に最新の状態になります。

現在の私の作業プロジェクトでは、ウィンドウを表すクラスには、ViewModel/Presenter クラスのパブリック インターフェイスを呼び出すメソッド ハンドラーのみが含まれています。ViewModel には、UI がバインドする必要があるすべてのものが含まれており、ThreadPool にジョブを追加することで、ありとあらゆる機能を起動します。

もちろん、現時点ではすべてのプロジェクトにバッチ処理が含まれているため、ThreadPool に同時実行の問題を処理させることは理にかなっています。

更新:プロパティ アプローチをもう一度試してみてください。ただし、プロパティが公開されていることを確認してください。このページによると、パブリック プロパティにのみバインドできます。

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

...

于 2009-04-23T18:28:02.763 に答える