Reputation: 5293
I've been trying to get something working in a windows 8 metro style application, and I just can't get the right properties for it to work. Here's what I want to happen:
I've got a ScrollViewer containing a StackPanel. RichTextBoxes are added to the stack panel (streaming text data from a server). It works like a console, effectively. So my layout roughly looks like this:
<ScrollViewer>
<StackPanel x:Name="outputStackPanel"/>
</ScrollViewer>
My desired behavior is this: I want the text to scale uniformly in the horizontal direction for the screen size, and then scroll vertically in the ScrollViewer. That is to say, the font I am using is a fixed width font, and no matter what the screen size is, I want the same number of ascii characters to appear.
In addition, I have an option in the app to wrap text or not to wrap text to the screen. This means, when the text is wrapping, it should wrap to the fixed screen width, but when it is not, the ScrollViewer should be scrollable horizontally, but at any point roughly the same number of characters should be visible horizontally while scrolling.
I've tried a couple of methods to do this, and none of them have been successful. I've tried adding a ViewBox, like so:
<ScrollViewer>
<Viewbox Stretch="UniformToFill" StretchDirection="UpOnly">
<StackPanel x:Name="outputStackPanel" />
</ViewBox>
</ScrollViewer>
While this seems to work in terms of getting the text to appear at the right size, it doesn't allow the text to wrap when text wrapping is on. The content is simply cut off on the right side.
My current workaround is to check the screen resolution and make a rough estimate for the font-size to get it to display correctly, but this is not ideal because it's not exact, so different resolutions get vastly different numbers of visible characters, and for the purposes of this application the number of visible characters is significant.
Upvotes: 2
Views: 2432
Reputation: 3550
I’ve been able to get it to work, but unfortunately I don’t think there’s an answer that will work automatically for any font. I could be wrong, but here’s how I got it to work:
I picked a fixed-width font (Consolas) and a particular size. I don’t think the size matters.
After picking the font size, I put the RichTextBlock into a ViewBox and figured out exactly what WIDTH I needed to set the RichTextBlock to get the exact number of characters (or columns) I wanted on the screen. At a font size of 13.333, to get exactly 60 columns I needed a width of 440.
Using 13.333 font size and a fixed width of 440 inside a ViewBox, I am always able to get exactly 60 columns no matter what screen resolution or orientation and the text wraps. Now that that was working, I needed to tackle your problem doing the same thing with word wrap disabled.
You wanted the text to be able to flow off the screen and allow the user to pan in either direction, but you always wanted a fixed number of columns (in my example, 60). To accomplish this, I removed the fixed width from the RichTextBox and figured out what zoom level the ScrollViewer needed to be set to in order to always get 60 columns wide. It turns out the zoom level is the current screen width divided by 440 (the magical width I came up with above for this particular font and size).
So, here is the xaml:
<ScrollViewer x:Name="Scroller" ZoomMode="Disabled" HorizontalScrollBarVisibility="Visible" >
<RichTextBlock x:Name="TextBlock">
<Paragraph FontSize="13.333" FontFamily="Consolas">
<Run Text="…" FontFamily="Consolas"/>
</Paragraph>
</RichTextBlock>
</Viewbox>
</ScrollViewer>
For word wrap ON:
TextBlock.Width=440
Scroller. ZoomToFactor(1.0f)
For word wrap OFF:
TextBlock.Width=Auto
Scroller.ZoomToFactor((float)Window.Current.Bounds.Width / 440);
It’s important to change the zoom level whenever the window size changes. This could be because the user rotated the device, because they connected an external monitor, because you’ve gone into snapped view, etc. You can get notified whenever this happens by subscribing to the Window.Current.SizeChanged event. Inside the event, be sure to use the parameter passed to you because the window hasn’t actually changed size yet at this point. So:
void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
Scroller.ZoomToFactor((float)e.Size.Width / 440);
}
Finally, obviously the Width of 440 is tied to that particular font, font size and the fact that I always want 60 columns displayed. Once I figured it out, it should never change on any display size as long as I use the same font and font size. If you want to allow the user to pick different fonts, you’re going to have to figure out a way to calculate that dynamically. I couldn’t find any answer for this in my 20 minutes of searching. Maybe someone else can find that.
Upvotes: 4