簡體   English   中英

WPF ScrollViewer滾動到FlowDocument的元素

[英]WPF ScrollViewer scroll to element of FlowDocument

我正在研究聊天應用程序。 有一個滾動瀏覽器,其中包含一個richtextbox,該richtextbox包含一個flowdocument,該flowdocument的塊中包含段落。 現在,當添加新消息時,scrollviewer會正確向下滾動其內容(滾動條位於底部時自動滾動)。 在向下滾動到底部時,flowdocument始終最多包含100個段落。 (收到新郵件后,頂部的舊段落將被刪除。)

我想添加的是,當我向上滾動scrollviewer到其頂部時,我想加載較舊的消息(有點像Facebook風格)。 當我滾動到頂部時,舊消息已正確加載,但我想實現的是,加載舊消息之前的最上面的段落仍將顯示在頂部。 為此,我認為我需要計算該段落的新位置,並將scrollviewer的垂直偏移設置為該段落的Y坐標。 (在加載新消息后,scrollviewer仍保持向上滾動。)

但是,在插入舊消息后如何檢測該段落在哪里?

不幸的是BringIntoView()對此不起作用。 我認為原因可能是該段落位於FlowDocument中,而不是直接在ScrollViewer中。 而且我不知道您是否可以知道(或它的工作原理是這樣。)BringIntoView將ScrollViewer滾動到該段落在頂部的位置。

另一個鏈接解決方案不是很好,因為Paragraph對象不能轉換為FrameworkElement,而只能轉換為具有TranslatePoint方法的對象。

我要做的是:因為我知道在FlowDocument中插入了多少行,所以我對插入的段落的大小進行求和,這樣我便得到了需要滾動ScrollViewer的段落的要點。 然后,我使用該參數調用sw.ScrollToVerticalOffset,它將在此處滾動。 :)

這是完整的代碼,希望對某些人有所幫助。 該函數已預訂到ScrollViewer的ScrollChanged事件。

但是請注意,此解決方案僅在一行中顯示的段落中才能正常工作!

    bool StopLoading = false; // This is required to ensure that only the specified amount of messages will be loaded at once.
    // If we scroll upper the messages, then don't scroll. If we scroll to bottom, scroll the messages
    private void MessageScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        var obj = sender as ScrollViewer;
        // The scrollbar is at the bottom
        if (obj.VerticalOffset == obj.ScrollableHeight)
        {
            // The while loop removes loaded messages when we have scrolled to the bottom
            Channel ch = (Channel)((ScrollViewer)sender).Tag;
            while (ch.TheFlowDocument.Blocks.Count > GlobalManager.MaxMessagesDisplayed)
            {
                ch.TheFlowDocument.Blocks.Remove(ch.TheFlowDocument.Blocks.FirstBlock);
                if (ch.MessagesLoadedFrom + 1 + GlobalManager.MaxMessagesDisplayed < GlobalManager.MaxMessagesInMemory)
                    ch.MessagesLoadedFrom++;
            }

            // The automatic scroll when the scrollbar is at the bottom
            obj.ScrollToEnd();
        }
        // The scrollbar is at the top
        else if (obj.VerticalOffset == 0) // Load older messages
        {
            if (!StopLoading)
            {
                Channel ch = (Channel)((ScrollViewer)sender).Tag;
                if (ch.MessagesLoadedFrom != 0)
                {
                    int loaded = LoadMessages(ch, GlobalManager.NumOfOldMessagesToBeLoaded);
                    double plus = first.Padding.Top + first.Padding.Bottom + first.Margin.Bottom + first.Margin.Top; // Since all of my paragraphs have the same Margin and Padding I can do this
                    double sum = 0;
                    Block temp = ch.TheFlowDocument.Blocks.FirstBlock;
                    for (int i = 0; i < loaded; i++)
                    {
                        sum += temp.FontSize + plus;
                        temp = temp.NextBlock;
                        if (temp == null)
                            break;
                    }

                    obj.ScrollToVerticalOffset(sum);
                }
                StopLoading = true;
            }
        }
        // The scrollbar is not at the top and not at the bottom. We can enable loading older messages
        else
        {
            StopLoading = false;
        }
        e.Handled = true;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM