繁体   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