1

私はC#で小さなwpfアプリを構築しています。ボタンがクリックされると、サードパーティのdll関数がオブジェクトのようなツリーを構築します。このオブジェクトはツリービューにバインドされています。これは正常に機能しますが、ロードには少し時間がかかります。dll関数がオブジェクトを作成すると、進行状況情報がコンソールに出力されます。これをTextBlockにリダイレクトして、ユーザーが進行状況メッセージを表示できるようにします。

私のウィンドウコンストラクタは次のようになります。

InitializeComponent();
StringRedir s = new StringRedir(ref ProgressTextBlock);
Console.SetOut(s);
Console.SetError(s);
this.DataContext = s;

xaml:

<TextBlock Text = "{Binding Path = Text}" Width = "244"
x:Name = "ProgressTextBlock" TextWrapping = "Wrap" />
<TreeView> ... </ TreeView> 

StringRedirクラスを以下に示します。問題は、TreeViewがロードされるまで、何らかの理由でTextBlockがメッセージで更新されないことです。ステップスルーすると、Textプロパティが更新されているのがわかりますが、TextBlockが更新されていません。テキストが更新される時点でMessageBox.Show()を追加しました。これにより、ウィンドウが毎回更新されるようで、各メッセージを表示できます。したがって、画面を明示的に更新する方法が必要だと思います...しかし、これは意味がありません。プロパティが変更されたときに、データバインディングによって視覚的な更新が発生すると思いました。ここで何が欠けていますか?どうすれば更新できますか?アドバイスをいただければ幸いです。

public class StringRedir : StringWriter , INotifyPropertyChanged 
{ 
    private string text; 
    private TextBlock local; 


    public string Text { 
        get{ return text;} 
        set{ 
            text = text + value; 
            OnPropertyChanged("Text"); 
        } 
    } 


    public event PropertyChangedEventHandler PropertyChanged; 
    protected void OnPropertyChanged(string name) 
    { 
        PropertyChangedEventHandler handler = PropertyChanged; 
        if (handler != null) 
        { 
            handler(this, new PropertyChangedEventArgs(name)); 
        } 
    } 


    public StringRedir(ref TextBlock t) 
    { 
        local = t; 
        Text = ""; 
    } 


    public override void WriteLine(string x) 
    { 
        Text = x +"\n"; 
        //MessageBox.Show("hello"); 
    } 


} 
4

3 に答える 3

1

そのため、WPF スレッド モデル ( http://msdn.microsoft.com/en-us/library/ms741870.aspx ) を読んだ後、Dispatch の優先度を Render に設定して Dispatcher Invoke() を呼び出して、最終的に更新しました。Kent が上記で示唆したように、ディスパッチャー キューの UI 更新はおそらく優先度が低かったでしょう。私は最終的にこのようなことをしました。

XAML

<TextBox VerticalScrollBarVisibility="Auto"  
         Text="{Binding Path=Text, NotifyOnTargetUpdated=True}"
         x:Name="test" TextWrapping="Wrap" AcceptsReturn="True" 
         TargetUpdated="test_TargetUpdated"/>

C# ターゲットの更新されたハンドラー コード

private void test_TargetUpdated(オブジェクト送信者、DataTransferEventArgs e)
{
    TextBox t = TextBox としての送信者。
    t.ScrollToEnd();
    t.Dispatcher.Invoke(new EmptyDelegate(() => { }), System.Windows.Threading.DispatcherPriority.Render);
}

注:以前はTextBlockを使用していましたが、スクロールが付属しているためTextBoxに変更しました

全体の流れはまだ不安だけど。これを行うより良い方法はありますか?Matt と Kent のコメントに感謝します。私がポイントを持っていれば、彼らの答えが役に立ったとマークします。

于 2008-12-20T09:42:21.130 に答える
1

のデータをロードするコードは含まれていませんがTreeView、UI スレッドで実行されていると思います。その場合、完了するまで UI の更新 ( への変更を含むTextBlock) がブロックされます。

于 2008-12-19T09:27:34.393 に答える
0

問題は StringRedir クラスのコンストラクターにあると思います。あなたは ProgessTextBlock を渡しています、そしてあなたはそれにこれをやっています:

local.Text = "";

これは、ProgressTextBlock.Text の以前に設定された値を効果的に上書きしています。これは次のとおりです。

{Binding Text}

私が何を意味するか分かりますか?TextBlock の Text プロパティに値を明示的に設定することで、バインドをキャンセルしました。

私が正しく読んでいれば、TextBlock を StringRedir の ctor に渡すという考えは、直接バインディングを試みる前からの二日酔いのようです。私はそれを捨てて、WPFの「精神」にあるバインディングのアイデアに固執します。

于 2008-12-19T06:50:43.103 に答える