none
Setting up a chat buffer using FlowDocumentScrollViewer RRS feed

  • Question

  • Hi,

    I'm new to WPF, trying to make a chat app to learn with. I've run into some walls with setting up the output buffer:

     

    -How can I make it scroll to the bottom all the time? FlowDocumentScrollViewer, despite its name, doesn't seem to provide any scrolling functionality.

    -Can I make it not update the buffer until the scrolling is done? It'd look really awkward if the text appears and then it scrolls.

    -Supposing I'll quickly build up a few hundred lines of text, is FlowDocumentScrollViewer viable in terms of performance?

    -How can I style the zoom thing to be gone? This thread has a link to a blog that explains how to do it, but I can't find the particular entry.

     

    These are questions that I think applies to making any sort of text output buffer, be it for chats, logs or consoles.

    Thanks in advance,

    Rei

    Tuesday, April 11, 2006 3:27 AM

Answers

  • ((Paragraph)sdv.Document.Blocks.LastBlock).Inlines.Add(new Bold (new Run("hello")));

    DependencyObject DO = sdv;

    bool val = false;

    ScrollViewer sv = null;

    while (val == false)

    {

    DO = VisualTreeHelper.GetChild(DO, 0) ;

    if (DO is ScrollViewer)

    {

    sv = DO as ScrollViewer;

    val = true;

    }

    }

    sv.ScrollToBottom();

    Thursday, April 13, 2006 11:20 PM
    Moderator

All replies

  • I'm assuming that your output buffer is readonly and you want it to scroll to the bottom before you append text to the buffer.

    There are a bunch of ways to do this the first two that come to mind. You could inherit from FlowDocumentScrollViewer, encapsulate it. I cant think of a markup only solution.

    //Encapulation

    public class MyOutputLog {
      FlowDocumentScrollViewer fdsv = new FlowDocumentScrollViewer();
      FlowDocument doc = new FlowDocument();
      Paragraph paragraph = new Paragraph();
     
      public MyOutputLog() {
          fdsv.Document = doc;
          doc.Blocks.Add(paragraph);
      }
     
      public void AppendBlockOfText(string text) {

          //scroll to end using Commanding infrastructure
          if(ComponentCommands.MoveToEnd.CanExecute(null, fdsv)) {
            ComponentCommands.MoveToEnd.Execute(null, fdsv);
          }

          //add content
          paragraph.Inlines.Add(new Run());
          paragraph.Inlines.Add(new LineBreak());
      }
     
      public UIElement Viewer { get { return fdsv; } }
    }
     

    -- Ifeanyi Echeruo [MSFT]
    This posting is provided "AS IS" with no warranties, and confers no rights.

    Thursday, April 13, 2006 7:13 PM
  • CanExecute always seems to return null... Maybe it hasn't been implemented in the betas yet?

    Thanks though :)

     

    Edit: Here's a somewhat quirky workaround. Run is a Run object that's assigned every time a new line's added. It doesn't work when I just call run.BringIntoView through the original event handler for some reason.

       System.Windows.Threading.DispatcherTimer tempTimer = new System.Windows.Threading.DispatcherTimer();
       tempTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
       tempTimer.Tick += delegate
       {
           if (run != null)
           {
               run.BringIntoView();
               run = null;
           }
       };
       tempTimer.Start();

     

    Thursday, April 13, 2006 8:03 PM
  • ((Paragraph)sdv.Document.Blocks.LastBlock).Inlines.Add(new Bold (new Run("hello")));

    DependencyObject DO = sdv;

    bool val = false;

    ScrollViewer sv = null;

    while (val == false)

    {

    DO = VisualTreeHelper.GetChild(DO, 0) ;

    if (DO is ScrollViewer)

    {

    sv = DO as ScrollViewer;

    val = true;

    }

    }

    sv.ScrollToBottom();

    Thursday, April 13, 2006 11:20 PM
    Moderator
  • Wow, that's weird... but it works! Thanks!

     

    For future reference, here's the code refracted into a property (call ViewerScroll.ScrollToBottom):

      ScrollViewer viewerScroll;
      ScrollViewer ViewerScroll
      {
       get
       {
        if (viewerScroll == null)
        {
         DependencyObject obj = viewer;
         do
          obj = VisualTreeHelper.GetChild(obj as Visual, 0);
         while (!(obj is ScrollViewer));
         viewerScroll = obj as ScrollViewer;
        }
        return viewerScroll;
       }
      }

    Friday, April 14, 2006 5:03 AM
  • Thank you for the code above.

    I added the following code to allow the user to interact with the scrollbar.  When Viewer increases in height is the only time it will scroll to the bottom.

    See any problems with this? Performance or other?


    public int ViewerHeight;

    ViewerHeight = 0;

    private void ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
                // Scroll only in view has increased in size
                if (ViewerHeight < Convert.ToInt32(e.ExtentHeight))
                {
                    ViewerHeight = x;
                    ViewerScroll.ScrollToBottom();
                }
    }

    <FlowDocumentScrollViewer x:Name="Viewer" Margin="5"  ScrollViewer.ScrollChanged="ScrollChanged">
    Monday, September 29, 2008 10:18 PM
  • Here's another alternative to avoid timer:

    Action<Paragraph> x = r => r.BringIntoView();
    Dispatcher.BeginInvoke(x, DispatcherPriority.SystemIdle, run);

    Sunday, February 15, 2009 11:37 PM