WPF ScrollViewer滚动到FlowDocument的元素
我正在开发一个聊天应用程序。 有一个包含richtextbox的scrollviewer,其中包含一个flowdocument,其中包含块中的段落。 现在,当添加新消息时,滚动查看器会正确地向下滚动其内容(当滚动条位于最下方时自动滚动)。 流动文档总是最多有100个段落,同时向下滚动到底部。 (新消息到达时,顶部的旧段落将被删除。)
我想添加的是,当我滚动scrollviewer到其顶部,然后我想加载旧的消息(Facebook风格的一种)。 当我滚动到顶部的旧邮件正确加载,但我想实现在加载旧邮件之前最上面的段落仍将显示在顶部。 为此,我想我需要计算该段落的新位置,并将scrollviewer的垂直偏移设置为该段落的Y坐标。 (在加载新消息之后,滚动查看器仍保持滚动至其顶部。)
但是,如何在插入旧邮件后检测该段的位置?
不幸的是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;
}
链接地址: http://www.djcxy.com/p/61129.html
上一篇: WPF ScrollViewer scroll to element of FlowDocument
下一篇: How to Set inline Images Vertically Center in RichTextBox