简体   繁体   English

Silverlight 4 RichTextBox滚动到底部

[英]Silverlight 4 RichTextBox scroll to bottom

I am writing a chat application with Silverlight. 我正在使用Silverlight编写聊天应用程序。 I am adding the content dynamically to the rich text box and I need to scroll it down to show the last messages. 我将内容动态添加到富文本框中,我需要向下滚动以显示最后的消息。 But there is no way to do that. 但是没有办法做到这一点。 All documentation/codes that I have found on internet are old. 我在互联网上找到的所有文档/代码都是旧的。

Any solution will be gladly accepted. 任何解决方案都会很乐意接受。

I struggled with this for a long time in my own application. 在我自己的应用程序中,我很长时间都在努力。 The Dispatcher.BeginInvoke method wasn't reliable for me; Dispatcher.BeginInvoke方法对我来说不可靠; sometimes it would scroll to the bottom, other times it wouldn't. 有时它会滚动到底部,有时则不会。 The only way to make it work was to nest two Dispatcher.BeginInvoke calls inside each other, but I didn't like that solution. 让它工作的唯一方法是在彼此内部嵌入两个Dispatcher.BeginInvoke调用,但我不喜欢这个解决方案。

Instead of using Dispatcher, I abuse event handlers. 我滥用事件处理程序,而不是使用Dispatcher。 In this example, the text box will scroll to the bottom only if it was there at the time the new Inline was added (a useful behavior for chat boxes, to allow users to read previous messages if they want). 在此示例中,只有在添加新内联时,文本框才会滚动到底部(聊天框的有用行为,允许用户在需要时阅读以前的消息)。 You can pass a Run object as the Inline parameter. 您可以将Run对象作为Inline参数传递。

private void AddInline(Inline inline)
{
    var viewer = textBox.GetChildByType<ScrollViewer>(item => item.Name == "ContentElement") as ScrollViewer;
    bool scrollToBottom = viewer.VerticalOffset == viewer.ScrollableHeight;

    // Creating the paragraph isn't necessary.
    // In my own application, I only add a single paragraph to the RichTextBox that displays my chat messages.
    // Then I just add new Inlines to the single paragraph.
    Paragraph p = new Paragraph();
    p.Inlines.Add(inline);

    if (scrollToBottom)
        textBox.LayoutUpdated += ScrollRichTextBoxToBottom;

    // Adding the paragraph triggers a layout update.
    // In the LayoutUpdated handler, the viewer will be scrolled to the bottom, triggering a second layout pass.
    textBox.Blocks.Add(p);
}

private void ScrollRichTextBoxToBottom(object sender, EventArgs e)
{
    // The LayoutUpdated handler needs to be removed, otherwise the RichTextBox becomes unscrollable.
    textBox.LayoutUpdated -= ScrollRichTextBoxToBottom;

    var viewer = textBox.GetChildByType<ScrollViewer>(item => item.Name == "ContentElement") as ScrollViewer;
    viewer.ScrollToVerticalOffset(viewer.ScrollableHeight);
}

Note: GetChildByType is just an extension method to get the ScrollViewer. 注意:GetChildByType只是获取ScrollViewer的扩展方法。 (I did not create this extension method.) (我没有创建这种扩展方法。)

public static class Extensions
{
    public static T GetChildByType<T>(this UIElement element, Func<T, bool> condition)
        where T : UIElement
    {
        List<T> results = new List<T>();
        GetChildrenByType<T>(element, condition, results);
        if (results.Count > 0)
            return results[0];
        else
            return null;
    }

    private static void GetChildrenByType<T>(UIElement element, Func<T, bool> condition, List<T> results)
        where T : UIElement
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            UIElement child = VisualTreeHelper.GetChild(element, i) as UIElement;
            if (child != null)
            {
                T t = child as T;
                if (t != null)
                {
                    if (condition == null)
                        results.Add(t);
                    else if (condition(t))
                        results.Add(t);
                }
                GetChildrenByType<T>(child, condition, results);
            }
        }
    }
}

You need to dig out the ScrollViewer that is part of the RichTextBox template. 您需要挖掘出作为RichTextBox模板一部分的ScrollViewer。 You can do this with the help of some code based of VisualTreeHelper . 您可以借助一些基于VisualTreeHelper代码来完成此操作。 Get the code for my VisualTreeEnumeration class here . 这里获取我的VisualTreeEnumeration类的代码。

Now with this class in your project you can do this:- 现在,在项目中使用此类,您可以执行以下操作: -

ScrollViewer sv = rtb.Descendents().OfType<ScrollViewer>().FirstOrDefault();

Full Example 完整的例子

Create a new application and include the VisualTreeEnumeration class. 创建一个新的应用程序并包含VisualTreeEnumeration类。

In MainPage.xaml use the following xaml:- 在MainPage.xaml中使用以下xaml: -

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <RichTextBox x:Name="rtb" />
    <Button Content="Click Me" Click="Button_Click" Grid.Row="1" />
</Grid>

In its code behind add this:- 在它的代码背后添加这个: -

    int i = 0;
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Paragraph p = new Paragraph();
        p.Inlines.Add(new Run() { Text = "Hello " + (i++).ToString() });
        rtb.Blocks.Add(p);

        Dispatcher.BeginInvoke(() =>
        {
            ScrollViewer sv = rtb.Descendents().OfType<ScrollViewer>().FirstOrDefault();
            sv.ScrollToVerticalOffset(sv.ScrollableHeight);
            sv.UpdateLayout();
        });
    }

Note the use of BeginInvoke to allow the RTB to sort itself out after having added the text. 请注意使用BeginInvoke允许RTB在添加文本后自行排序。 Using BeginInvoke queues up the rest of the code on the UI thread dispatcher. 使用BeginInvoke将UI线程调度程序上的其余代码排队。

Well, you need neither extensions methods nor dispatchers. 好吧,你既不需要扩展方法也不需要调度程序。 The easiest way to scroll the RichTexBox control to the bottom is as follows: 将RichTexBox控件滚动到底部的最简单方法如下:

textBox.Selection.Select(textBox.ContentEnd, textBox.ContentEnd); textBox.Selection.Select(textBox.ContentEnd,textBox.ContentEnd);

You can achieve the "scroll to top" behaviour in the similar fashion. 您可以以类似的方式实现“滚动到顶部”行为。

Hope that helps. 希望有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM