2011-08-25 62 views
2

我正在使用Silverlight編寫聊天應用程序。我將動態內容添加到富文本框中,並且需要向下滾動以顯示最後的消息。但是沒有辦法做到這一點。我在互聯網上找到的所有文檔/代碼都是舊的。Silverlight 4 RichTextBox滾動到底部

任何解決方案將很樂意接受。

+0

看到我編輯的答案。 – AnthonyWJones

回答

4

我在自己的應用程序中苦苦掙扎了很長時間。 Dispatcher.BeginInvoke方法對我來說並不可靠。有時它會滾動到底部,其他時間則不會。使其工作的唯一方法是將兩個Dispatcher.BeginInvoke調用嵌套在一起,但我不喜歡該解決方案。

而不是使用分派器,我濫用事件處理程序。在這個例子中,只有在添加了新的Inline時,文本框纔會滾動到底部(這是聊天框的一種有用的行爲,允許用戶在需要時閱讀以前的消息)。您可以傳遞一個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); 
} 

注意:GetChildByType只是獲取ScrollViewer的擴展方法。 (我沒有創建這種擴展方法。)

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); 
      } 
     } 
    } 
} 
+0

這工作相當不錯。謝謝@Sentient – Heka

3

您需要挖掘屬於RichTextBox模板的ScrollViewer。您可以在VisualTreeHelper的某些代碼的幫助下完成此操作。獲取我的VisualTreeEnumerationhere的代碼。

這個類在你的項目,你可以做到這一點

現在: -

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

完整的例子

創建一個新的應用程序,包括VisualTreeEnumeration類。

在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> 

在其後面的代碼補充一點: -

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(); 
     }); 
    } 

注意使用的BeginInvoke的允許RTB後自行解決添加了文字。使用BeginInvoke將UI線程分派器上的其餘代碼排隊。

+0

不想讓你失望或者其他任何東西,但你用'VisualTreeEnumeration'重新創造了這個輪子。 Silverlight工具包提供了完全相同的功能。命名空間'System.Windows.Controls.Primitives'將爲您提供擴展方法,如'myElement.GetVisualDescendants()'。 – herzmeister

+0

@herzmeister:儘管有許多使用該工具包的XAPP並不是每個人都需要它。我有一些項目使用我的枚舉工具,不需要該工具包,並且在351KB包括僅用於此功能的工具包似乎不是一個很好的折衷。我還特別討厭建議在甚至沒有提到工具包的存在的問題上增加工具包開銷。即使那樣我也會避免這種情況,以防其他讀者可能會選擇包含它,只是爲了獲得一個功能,即我的博客演示可以用很少的代碼完成。 – AnthonyWJones

+0

嗯,它有點不錯,但問題是滾動條向下滾動,然後立即回到以前的位置。我無法弄清楚原因。 – Heka

3

那麼,你既不需要擴展方法也不需要調度器。將RichTexBox控件滾動到底部最簡單的方法如下:

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

您可以用類似的方式實現「滾動到頂部」行爲。

希望有所幫助。