【问题标题】:WPF ScrollViewer scroll to element of FlowDocumentWPF ScrollViewer 滚动到 FlowDocument 的元素
【发布时间】:2014-08-15 20:32:40
【问题描述】:

我正在开发一个聊天应用程序。有一个滚动查看器,其中包含一个富文本框,其中包含一个流文档,其中包含其块中的段落。 现在,当添加新消息时,滚动查看器会正确向下滚动其内容(当滚动条位于底部时自动滚动)。流文档在向下滚动到底部时始终最多包含 100 个段落。 (当有新消息到达时,顶部的旧段落将被删除。)

我想补充的是,当我将滚动查看器向上滚动到顶部时,我想加载较旧的消息(一种 Facebook 风格)。当我滚动到顶部时,旧消息已正确加载,但我希望在加载旧消息之前最顶部的段落仍将显示在顶部。为此,我认为我需要计算该段落的新位置并将滚动查看器的垂直偏移设置为该段落的 Y 坐标。 (加载新消息后,滚动查看器仍会向上滚动到其顶部。)

但是插入旧消息后如何检测该段落在哪里?

【问题讨论】:

标签: c# wpf offset scrollviewer flowdocument


【解决方案1】:

不幸的是,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;
    }

【讨论】:

  • 那么不要把它放在滚动查看器中。我用它来BringIntroView() 来显示搜索词,效果很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-07
  • 2013-10-24
  • 2010-11-11
  • 2015-01-26
相关资源
最近更新 更多