views:

792

answers:

3
+5  Q: 

WPF font scaling

I have a WPF application where the user interface should be scaled so that it should become larger if the windows is made larger. In one of the dialogs, I need to present a list of items to a user and the user should click one of them. The list will contain from 1 to around 15-20 items. I want the font size for each individual item to be as big as the font size for the other items in the list, but at the same time I want the font size to increase if the window is made larger.

At the moment, my test code looks like below.

<Window x:Class="WpfApplication4.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WpfApplication4"
    Title="Window1" Height="480" Width="640">


    <ScrollViewer>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="30*" MinHeight="30"/>
                <RowDefinition Height="30*" MinHeight="30"/>
                <RowDefinition Height="30*" MinHeight="30"/>
            </Grid.RowDefinitions>

            <Button Grid.Row="0" MaxHeight="100"><Viewbox><TextBlock>T</TextBlock></Viewbox></Button>
            <Button Grid.Row="1" MaxHeight="100"><Viewbox><TextBlock>Test</TextBlock></Viewbox></Button>
            <Button Grid.Row="2" MaxHeight="100"><Viewbox><TextBlock>Test Longer String</TextBlock></Viewbox></Button>

        </Grid>

    </ScrollViewer>

</Window>

If the application is started and the Window is made wide, everything looks OK. If the window width is decreased, the font size of the text Test Longer String is made smaller, but the font size for T and Test remains the same. I do understand why this happens - the viewbox will scale the contents to its maximum size. What I want to know is what method I should use to solve this problem.

I don't want to give the controls specific font sizes because some people will run this on low-resolution screens such as 640x480 and others will use larger widescreens.

EDIT:

I've tried to modify my code to the following:

<ScrollViewer>
    <Viewbox>
        <ItemsControl>
            <Button>Test 2</Button>
            <Button>Test 3</Button>
            <Button>Test 4 afdsfdsa fds afdsaf</Button>
            <Button>Test 5</Button>
            <Button>Test 5</Button>
            <Button>Test 5</Button>
            <Button>Test 5</Button>
            <Button>Test 5</Button>
        </ItemsControl>
    </Viewbox>
</ScrollViewer>

But with the size of the button borders are increased as well, so on large screens, the button borders become a centimeter wide.

A: 

Try this: Render your text items like you would do it any other time:

  • TextBlocks contained in an 'ItemsControl'?
  • ListBox?

Place the whole shebang into a ViewBox and set it to scale appropriate to your needs. Look for Horizontal, Vertical or combined scaling properties.

Pieter Breed
I've updated my post. See under "Edit". In short, if I use a TextBlock rather than a Button, I loose the "Button"-user interface (click-effect and so on). If I use a Button and place that inside of a Viewbox, the entire button - including its border - will be resized. I don't want a 1cm button border just because the program is maximized.
Martin
+3  A: 

This workaround might help:

    <ScrollViewer>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30*" MinHeight="30"/>
            <RowDefinition Height="30*" MinHeight="30"/>
            <RowDefinition Height="30*" MinHeight="30"/>
        </Grid.RowDefinitions>

        <Button Grid.Row="0" MaxHeight="100">
            <Viewbox>
                <TextBlock Width="{Binding ElementName=tbLonger,Path=ActualWidth}">T</TextBlock>
            </Viewbox>
        </Button>
        <Button Grid.Row="1" MaxHeight="100">
            <Viewbox>
                <TextBlock Width="{Binding ElementName=tbLonger,Path=ActualWidth}">Test</TextBlock>
            </Viewbox>
        </Button>
        <Button Grid.Row="2" MaxHeight="100">
            <Viewbox>
                <TextBlock Name="tbLonger">Test Longer String</TextBlock>
            </Viewbox>
        </Button>
    </Grid>
</ScrollViewer>

The key is to set all textblocks the same width. In this case they all follow the longest textblock through binding.

Jacob Reyes
A: 

The simplest way is probably to construct a decorator to do the job. You might call it "VerticalStretchDecorator" or something like that.

Here is how it would be used:

<UniformGrid Rows="3" MaxHeight="300">
  <Button>
    <my:VerticalStretchDecorator>
      <TextBlock>T</TextBlock>
    </my:VerticalStretchDecorator>
  </Button> 
  <Button>
    <my:VerticalStretchDecorator>
      <TextBlock>Test</TextBlock>
    </my:VerticalStretchDecorator>
  </Button> 
  <Button>
    <my:VerticalStretchDecorator>
      <TextBlock>Test Longer String</TextBlock>
    </my:VerticalStretchDecorator>
  </Button>
</UniformGrid>

I used UniformGrid instead of Grid, but it would work the same way with Grid.

It would be implemented something like this:

public class VerticalStretchDecorator : Decorator
{
  protected override Size MeasureOverride(Size constraint)
  {
    var desired = base.Measure(constraint);
    if(desired.Height > constraint.Height || desired.Height==0)
      LayoutTransform = null;
    else
    {
      var scale = constraint.Height / desired.Height;
      LayoutTransform = new ScaleTransform(scale, scale); // Stretch in both directions
    }
  }
}
Ray Burns