Creating Scrollable TextBlock for WP7.


There could be many scenarios when you’d need to be able to dipslay a text in your WP7 application which doesn’t fit on the screen. The easiest way to approach to solve this would be to utilize the ScrollViewer control as a host for a TextBlock. Something like that:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
      <ScrollViewer>
            <TextBlock x:Name="textBlock" TextWrapping="Wrap" />
      </ScrollViewer>
</Grid>

You would think that we’re done but as usual the “devil is in the details”. If you assign a long text to the TextBlock you will see that part of this text could be truncated and an empty space would be displayed when scrolling to the end:

 The reason for this behavior is that any element that must be displayed beyond the area which is larger than 2048×2048 pixels would be clipped by the platform. I was told that this limitation had been dictated by a combination of hardware limitation and performance considerations. To workaround this limitation we’d need to break out the text into a separate blocks and create a TextBlock for each of this text blocks. So to make the life easier for myself and for you I created a custom control which I called ScrollableTextBlock which wraps this logic. So I created a custom control which derives from Control and implements Text property:

    public class ScrollableTextBlock : Control
    {
        private StackPanel stackPanel;
        
        public ScrollableTextBlock()
        {
            // Get the style from generic.xaml
            this.DefaultStyleKey = typeof(ScrollableTextBlock);           
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(ScrollableTextBlock),
                new PropertyMetadata("ScrollableTextBlock", OnTextPropertyChanged));        

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollableTextBlock source = (ScrollableTextBlock)d;
            string value = (string)e.NewValue;
            source.ParseText(value);
        }

            }

And here’s the ParseText method which is also a part of this control:

    private void ParseText(string value)
    {
         if (this.stackPanel == null)
         {
             return;
         }
         // Clear previous TextBlocks
         this.stackPanel.Children.Clear();
         // Calculate max char count
         int maxTexCount = this.GetMaxTextSize();

         if (value.Length < maxTexCount)
         {
             TextBlock textBlock = this.GetTextBlock();
             textBlock.Text = value;
             this.stackPanel.Children.Add(textBlock);
         }
         else
         {
             int n = value.Length / maxTexCount;
             int start = 0;
            // Add textblocks
             for (int i = 0; i < n; i++)
             {                    
                 TextBlock textBlock = this.GetTextBlock();
                 textBlock.Text = value.Substring(start, maxTexCount);
                 this.stackPanel.Children.Add(textBlock);
                 start = maxTexCount;
             }

             // Pickup the leftover text
             if (value.Length % maxTexCount > 0)
             {
                 TextBlock textBlock = this.GetTextBlock();
                 textBlock.Text = value.Substring(maxTexCount * n, value.Length - maxTexCount * n);
                 this.stackPanel.Children.Add(textBlock);                   
             }
         }
     }

In the code above I measure the text length and if it’s greater that maxTextCount I break the text into a separate blocks and create a new TextBlock with this text. After the TextBlock is created I add it to the StackPanel. To make a picture complete here’s how the control’s style looks like:

   <Style TargetType="local:ScrollableTextBlock" >
        <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMedium}"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="Width" Value="200" />
        <Setter Property="Height" Value="70" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:ScrollableTextBlock">
                    <ScrollViewer x:Name="ScrollViewer" Foreground="{TemplateBinding Foreground}" 
                                  Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" 
                                  BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <StackPanel Orientation="Vertical" x:Name="StackPanel" />
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

As you can see, the StackPanel is located inside of the ScrollViewer control which provides the scrolling functionality.  And after we execute the test project the result is that the text is not truncated:

 

The test project and the ScrollableTextBlock control is available for your peruse.

 

Comments (43)

  1. mashi says:

    Nice,

    Very useful, and the article too. about Google Instant, lol

    Thumbs way up!.

  2. mashi says:

    Just a heads up, it looks like that if their is a newline (e.g. multiple newlines) in the string it won't showup correctly.

  3. Robert says:

    Haha, I was wondering why my TextBlock I put in a ScrollViewer was being trucated a couple weeks ago! 🙂

  4. Tyler says:

    Awesome thanks a lot for this very useful! (Not sure why it isn't an official default control!)

  5. skyrace says:

    Hi all,

    I download the source code, it is working fine, but when i want to bind data it is not working, here is my source code :

               <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

               <my:ScrollableTextBlock  Text="{Binding LineOne}"        

                   HorizontalAlignment="Left"  Name="scrollableTextBlock1"

                     VerticalAlignment="Top" Height="618" Width="427" Margin="12,-11,0,0" />

               </Grid>

    Could you help me ??

    Thanks

    Best regards

  6. stan_impkiss says:

    Bro, legend!

    Wasn't aware of the 2048×2048 thingy…it was killing me.  Your control is working sweet.

    Thanks

  7. Andrew says:

    start = maxTexCount should probably start += maxTexCount;

    else it wont work for more then one page.

  8. Andreas Balzer [msp] says:

    Hi,

    really great control!

    Unfortunately it doesn't work if there are newlines (n) or if the fontsize is larger than normal.

    A patch would be nice.

    Greetings,

    Andreas Balzer

    Microsoft Student Partners

  9. andreasbalzer says:

    Hi,

    really great control!

    Unfortunately it doesn't work if there are newlines (n) or if the fontsize is larger than normal.

    A patch would be nice.

    Greetings,

    Andreas Balzer

    Microsoft Student Partners

  10. john says:

    if I wanted to place the long textblock inside a scoller with other elements, how should I modify it?

    thank you in advance

  11. himanshu says:

    sir i wanted to reverse a string in wimdows phone7, how can i do this, ie..

    ..

    input box – "you are intelligent" (user type this in the input box)

    ..

    press enter. (user presses enter) (stirng is readed and dispalyed)

    ..

    ouput box display – "tnegilletni era uoy" (will be displayed to user)..

    ..

    please help me – himansh3045@gmail.com

  12. dev_oo says:

    Anyone knows how to make this work with databinding?

  13. Hey, as the others have mentioned, this is nice but has some problems if there are multiple sequential newlines. The measuring part calculates the height as too short and text is truncated.

    I've tried to take a look at the code and will keep doing it, but in the meantime, anybody found a fix for this ?

  14. quick follow up: it appears the problem is solved by linking the release dll to a release project and debug dll to a debug project. Or at least it seems so…

  15. Richard Woo says:

    I have a fix for the sequential newline issue. I also fixed another problem I came across. ScrollableTextBlock tries to do some optimization, so that if the input text is below a certain character count, it will use it directly instead of parsing and splitting the text. However the formula doesn't work when the lines of text are very short.

    I posted the fixes in the developer forum:

    forums.create.msdn.com/…/482173.aspx

  16. Dharmang says:

    I am having issues while aligning text. how do i center align the text.

  17. Joel says:

    It's a good post.But i have some problems: when there is a long text to display. there are a lot of blank line.

    Is any idea to fix it?

  18. madsum says:

    How to use this controller if I want scroll textblock horizzontally?

  19. could anybody tell me how to link ScrollableTextblock project to my project. I added a reference Phone.control.dll to my project. but I still cannot add the toolbox into my xaml page ie <my:ScrollableTextblock…………..>

    thanks a lot

  20. to use the Phone.Control

    1, add reference to your project

    2. add

    shell:SystemTray.IsVisible="True" xmlns:my="clr-namespace:Phone.Controls;assembly=Phone.Controls">

    to your xaml file.

    I want to point two things here. one when the context is too long the GUI will stuck. I think a thread may be used to push content to the stack and another will show on the screen. this is good generally.

  21. Fabrice says:

    An easier way for me has been to set the TextBlock Height property to Auto:

    For instance:

           <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

               <ScrollViewer>

                   <TextBlock Height="Auto" HorizontalAlignment="Left" Margin="12,6,0,0" Name="tbScroll" VerticalAlignment="Top" Width="438" TextWrapping="Wrap" TextAlignment="Center" />

               </ScrollViewer>

           </Grid>

  22. Mugu says:

    Is it possible to set the ScrollToVerticalOffset using the above control. I want to scroll the text automatically without any user input. Please let me know

  23. this.GetTextBlock() doesn't seem to work. is there a full working sample that I can copy from?

  24. Cojocaru Serghei says:

    Hello, it is great component, but there is a small bug:

    if a text contains 2 or more consecutive newlines and text is big, ScrollableTextBlock will not show them.

    I've fixed it in this way:

    In ParseLine function replace

    textBlock.Text = sbLine.ToString()

    with:

    if (!string.IsNullOrEmpty(sbLine.ToString()))

                   textBlock.Text = sbLine.ToString() + Environment.NewLine;

  25. Michael says:

    How can I implkement this logic for RichTextBox?

  26. Ann says:

    useful control and good contribution ( Cojocaru ) to solve the newline issue.

  27. Carla says:

    Where I can find the fix of of the newline issue? 🙁

  28. mintoo says:

    How to use RichTechBox in place of TextBlock in That ScrollableTextBlock.cs . I try to use but its not supporting here any one please help?..me

  29. Danilo Ercoli says:

    The workaround proposed in the article works fine if you only need to display long content of text.

    Take a look at the following workaround if you need to edit long content of text in Windows Phone: daniloercoli.wordpress.com/…/edit-long-content-of-text-in-win-phone-7

  30. Here is yet another implementation of the ScrollableTextBlock that focuses on performance optimization.

    blogs.msdn.com/…/yet-another-scrollable-textblock-for-windows-phone.aspx

  31. atif says:

    It's takes lots of time to load content, How can I implement a loading indicator for this textblock control ?

  32. naveen says:

    when it comes to this scenario how can you increase the font size by giving it to a button click

    private void ApplicationBarIconButton_Click_3(object sender, EventArgs e)

           {

               if (ScrollableTextBlock.FontSize < 21.5)

               {

                   ScrollableTextBlock.FontSize += 1;

               }

            }

    this does not increase the text size  i'm using the same data template

  33. navroop says:

    agreed with naveen neither font size changing or text alignment works with it.

  34. navroop says:

    Another problem that i am facing is that if we use two scrolltextblock first update correctly but second stuck to previous value

  35. Tanmay Panchal says:

    We got the solution for Textblock control but we have problem using long HTML content to be displayed in the Windows Phone application. We have used richtextbox and displaying HTML content in it by generating multiple richtextbox. But it has very poor performance and not loading HTML content till 8-10 seconds.

    We are splitting content in small blocks and storing it in phone isolated storage and loading those content dynamically by generating richtextbox. But it has very poor and bad performance.

    If you have any better suggestion, please mail it to tanmay@innodel.com

    – Tanmay.

  36. naveen says:

    can you please upload this solution once again!!!!

  37. Harald-René Flasch says:

    The odd thing: Works well on the 256MB emulator ("the small one") but on a real device (e.g. Lumia 920) I get that issue. Deploying the same XAP ….

  38. Harald-René Flasch says:

    Just for information: MessageBox seems not having such a (small) limitation.

  39. hoang says:

    i read website here

    blogs.msdn.com/…/creating-scrollable-textblock-for-wp7.aspx

    and

    social.msdn.microsoft.com/…/create

    but I can not find the function GetTextBlock

    GetTextBlock function, anyone can help me

  40. Caudam16 says:

    i read website here

    blogs.msdn.com/…/creating-scrollable-textblock-for-wp7.aspx

    and

    social.msdn.microsoft.com/…/create

    but I can not find the function GetTextBlock

    GetTextBlock function, anyone can help me

  41. Jatin says:

    Where is the function GetTextBlock? Also the hyperlink in "is available for your peruse" isn't working.

    Please help.

  42. Santosh says:

    My textblock text is static….(I mean I will put there) and it must be scrollable….

    Is it possible ??

  43. hesaom says:

    Hi thnx for this great article.

    I have the same problem but this time with a text box rather than a text block and unfortunately this method is impossible for a editable element such as text box, I thought it's impossible till I found rate and review page of apps in wp8 which contains a scrollable text box without 2048 px limitation !! But how??