10

(WPF)にかなりの量のリッチ テキストを読み込んでRichTextBoxおり、コンテンツの最後までスクロールしたい:

richTextBox.Document.Blocks.Add(...)
richTextBox.UpdateLayout();
richTextBox.ScrollToEnd();

これは機能せずScrollToEnd、レイアウトが終了していないときに実行されるため、最後までスクロールせず、テキストの最初の 3 分の 1 程度までスクロールします。

実際にテキストの最後までスクロールするRichTextBoxように、ペイントとレイアウト操作が完了するまで強制的に待機させる方法はありますか?ScrollToEnd

ありがとう。

動作しないもの:

編集:LayoutUpdatedイベントを試してみましたが、すぐに発生します。同じ問題: コントロールは、起動時にリッチテキストボックス内にさらにテキストを配置しているため、ScrollToEnd機能しません... 私はこれを試しました:

richTextBox.Document.Blocks.Add(...)
richTextBoxLayoutChanged = true;
richTextBox.UpdateLayout();
richTextBox.ScrollToEnd();

そしてrichTextBox.LayoutUpdatedイベントハンドラー内:

if (richTextBoxLayoutChanged)
{
    richTextBoxLayoutChanged = false;
    richTextBox.ScrollToEnd();
}

イベントは正しく発生しますが、すぐにリッチテキストボックスが発生したときにまだテキストを追加しているため、レイアウトが完了していないため、ScrollToEnd再び失敗します。

EDIT 2:dowhileforの答えに続いて:InvalidateArrangeのMSDNは言う

無効化の後、要素のレイアウトが更新されます。これは、後で UpdateLayout によって強制されない限り、非同期に発生します。

それでも

richTextBox.InvalidateArrange();
richTextBox.InvalidateMeasure();
richTextBox.UpdateLayout();

待機しません: これらの呼び出しの後、richtextbox はさらにテキストを追加し、それを内部に非同期的に配置しています。ARG!

4

8 に答える 8

9

関連する状況がありました。派手なレンダリングを作成する印刷プレビュー ダイアログがあります。通常、ユーザーはボタンをクリックして実際に印刷しますが、ユーザーの関与なしで画像を保存するためにも使用したいと考えていました。この場合、画像の作成はレイアウトが完了するまで待たなければなりません。

私は以下を使用してそれを管理しました:

Dispatcher.Invoke(new Action(() => {SaveDocumentAsImage(....);}), DispatcherPriority.ContextIdle);

重要なのは、DispatcherPriority.ContextIdleバックグラウンド タスクが完了するまで待機する です。

編集:この特定のケースに適用可能なコードを含む、ザックの要求に従って:

Dispatcher.Invoke(() => { richTextBox.ScrollToEnd(); }), DispatcherPriority.ContextIdle);

このソリューションは信じられないほど脆弱に感じられるため、私はこのソリューションにあまり満足していないことに注意してください。ただし、私の特定のケースでは機能するようです。

于 2015-02-09T12:38:17.000 に答える
3

UpdateLayoutを見てください

特に:

レイアウトが変更されていない場合、またはレイアウトの配置も測定状態も無効でない場合、このメソッドを呼び出しても効果はありません

そのため、必要に応じて InvalidateMeasure または InvalidateArrange を呼び出してください。

しかし、あなたのコードを考えてみてください。それはうまくいかないと思います。多くの WPF の読み込みと作成が延期されるため、Document.Blocks に何かを追加しても、必ずしも UI を直接変更する必要はありません。しかし、これは単なる推測であり、おそらく私が間違っていると言わざるを得ません。

于 2011-07-07T17:52:35.063 に答える
1

@Andreasによる答えはうまくいきます。

ただし、コントロールが既に読み込まれている場合はどうなるでしょうか。イベントが発生することはなく、待機が永遠にハングアップする可能性があります。これを修正するには、フォームが既に読み込まれている場合はすぐに戻ります。

/// <summary>
/// Intent: Wait until control is loaded.
/// </summary>
public static Task WaitForLoaded(this FrameworkElement element)
{
    var tcs = new TaskCompletionSource<object>();
    RoutedEventHandler handler = null;
    handler = (s, e) =>
    {
        element.Loaded -= handler;
        tcs.SetResult(null);
    };
    element.Loaded += handler;

    if (element.IsLoaded == true)
    {
        element.Loaded -= handler;
        tcs.SetResult(null);
    }
        return tcs.Task;
}

追加のヒント

これらのヒントは、役立つ場合とそうでない場合があります。

  • 上記のコードは、添付プロパティで非常に役立ちます。添付プロパティは、値が変更された場合にのみトリガーされます。添付プロパティを切り替えてトリガーする場合task.Yield()は、呼び出しをディスパッチャー キューの後ろに置くために使用します。

    await Task.Yield(); // Put ourselves to the back of the dispatcher queue.
    PopWindowToForegroundNow = false;
    await Task.Yield(); // Put ourselves to the back of the dispatcher queue.
    PopWindowToForegroundNow = false;
    
  • 上記のコードは、添付プロパティで非常に役立ちます。添付プロパティを切り替えてトリガーする場合は、ディスパッチャーを使用して、優先度を次のように設定できますLoaded

    // Ensure PopWindowToForegroundNow is initialized to true
    // (attached properties only trigger when the value changes).
    Application.Current.Dispatcher.Invoke(
    async 
       () =>
    {
        if (PopWindowToForegroundNow == false)
        {
           // Already visible!
        }
        else
        {
            await Task.Yield(); // Put ourselves to the back of the dispatcher queue.
            PopWindowToForegroundNow = false;
        }
    }, DispatcherPriority.Loaded);
    
于 2016-05-16T15:45:32.310 に答える
1

Loadedイベントを使用できるはずです

これを複数回行っている場合は、LayoutUpdatedイベントを確認する必要があります。

myRichTextBox.LayoutUpdated += (source,args)=> ((RichTextBox)source).ScrollToEnd();
于 2011-07-07T17:39:08.760 に答える
1

.net 4.5 または async blc パッケージでは、次の拡張メソッドを使用できます

 /// <summary>
    /// Async Wait for a Uielement to be loaded
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    public static Task WaitForLoaded(this FrameworkElement element)
    {
        var tcs = new TaskCompletionSource<object>();
        RoutedEventHandler handler = null;
        handler = (s, e) =>
        {
            element.Loaded -= handler;
            tcs.SetResult(null);
        };
        element.Loaded += handler;
        return tcs.Task;
    }
于 2016-04-20T09:32:28.260 に答える
0

richTextBox.ScrollToEnd();を追加してみてください。RichTextBoxオブジェクトのLayoutUpdatedイベントハンドラーを呼び出します。

于 2011-07-07T17:53:59.997 に答える
0

これを試して:

richTextBox.CaretPosition = richTextBox.Document.ContentEnd;
richTextBox.ScrollToEnd(); // maybe not necessary
于 2015-01-12T08:59:42.930 に答える