5

The XAML is a page with a list of items on it inside a Viewbox. The goal is to have a department name and them a paragraph of notes underneath that. I set the background of my itemscontrol to be Red to make it easy to see how it fills in the page.

<Grid >
    <Viewbox x:Name="slideViewBox">
        <ItemsControl x:Name="itemsControl" Background="Red" ItemsSource="{Binding JournalEntries}" Grid.IsSharedSizeScope="True">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid x:Name="ParentGrid">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Row="0" HorizontalAlignment="Left" Foreground="White" Text="{Binding Department}" FontSize="32"/>
                        <TextBlock x:Name="detailsTextBlock" Grid.Row="1" Foreground="White" Text="{Binding DetailedExplaination}" HorizontalAlignment="Left" TextWrapping="Wrap" FontSize="20" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Viewbox>
</Grid>

What it generates: enter image description here What I ideally want: enter image description here

Notice that in the first picture the details TextBlock didn't word wrap at all and a lot of usable space on the page was wasted.

I did write some code in the Size_Changed event handler that got me closer to my goal but not all the way there. I manually play with the itemscontrol width to force the text to wrap. I just need to get it to do this automatically, so that it looks like the second picture, right when the page loads.

private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
{
    var child = VisualTreeHelper.GetChild(slideViewBox, 0) as ContainerVisual;
    var scale = child.Transform as ScaleTransform;

    double pageheight = e.NewSize.Height;
    double pagewidth = e.NewSize.Width;
    double textHeight;
    double textWidth;

    textHeight = itemsControl.ActualHeight * scale.ScaleY;
    textWidth = itemsControl.ActualWidth * scale.ScaleX;

    if (textWidth == pagewidth)
    {
        itemsControl.Width = itemsControl.ActualWidth * .9;
    }
    else if (textHeight == pageheight)
    {
        itemsControl.Width = itemsControl.ActualWidth * 1.1;
    }
}
Jon Dosmann
  • 667
  • 7
  • 20

2 Answers2

3

I took @user3284736's idea of applying a fixed width to the inner content, and expanded it.

First, add a property "ContainerWidth" to the view-model, and keep it updated by handling the SizeChanged event of the view:

public MyUserControl()
{
    SizeChanged += OnSizeChanged;
}

void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
    ViewModel.ContainerWidth= e.NewSize.Width;
}

Now bind the width of the item template's container ("ParentGrid" in the example) to the overall container size:

<Grid x:Name="ParentGrid" 
      Width="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl},Path=DataContext.ContainerWidth}">

Setting a fixed size has the effect of forcing the text to wrap before the ViewBox applies its scaling.

Viewbox

Edit Here's the XAML I used to test:

<Grid x:Name="LayoutRoot" Background="Red">
    <Viewbox Stretch="Fill" StretchDirection="Both">
        <ItemsControl x:Name="itemsControl" ItemsSource="{Binding JournalEntries}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid x:Name="ParentGrid" 
                          Width="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl},Path=DataContext.ContainerWidth}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Row="0" HorizontalAlignment="Left" Foreground="White" Text="{Binding Department}" 
                                   FontSize="32"/>
                        <TextBlock x:Name="detailsTextBlock" Grid.Row="1" Foreground="White" Text="{Binding DetailedExplanation}" HorizontalAlignment="Left" TextWrapping="Wrap" FontSize="20" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Viewbox>
</Grid>
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • Can you add more of your code. I'm still not clear on what you are using the ContainerWidth property for in the ViewModel. – Jon Dosmann Sep 08 '14 at 18:10
  • 1
    @JonD my mistake - I had "WindowWidth" in there instead of "ContainerWidth". It should be correct now. – McGarnagle Sep 08 '14 at 18:28
2

The following changes worked in the simplified example I included (no data binding but that shouldn't matter):

  1. Update the Viewbox, setting its VerticalAlignment to Top
  2. Update the "inner" grid, setting its VerticalAlignment to Stretch and binding its MaxWidth to the ActualWidth of the parent (in my example, a Window... not sure what your parent control is - you'll have to specify the x:Name on the parent)

This won't stretch all of the content vertically, but it does make the grid honor the width of the Window and it does wrap the text.

More info on binding to the parent control's width here - https://stackoverflow.com/a/157780/3284736

<Window x:Class="StretchBoxWithWrapping.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" 
    x:Name="MyWindow"
    Height="350" 
    Width="525">
<Grid>
    <Viewbox x:Name="MyViewBox" VerticalAlignment="Top">
        <Grid x:Name="MyParentGrid" Background="Red" VerticalAlignment="Stretch"
              MaxWidth="{Binding ElementName=MyWindow,Path=ActualWidth}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" HorizontalAlignment="Left" Foreground="White" Text="Department A" FontSize="32"/>
            <TextBlock x:Name="detailsTextBlock" Grid.Row="1" Foreground="White" HorizontalAlignment="Left" TextWrapping="Wrap" FontSize="20"
                       Text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. " />
        </Grid>
    </Viewbox>
</Grid>

Community
  • 1
  • 1
  • I tried out your code and it seems to do the same as mine, the only difference being it's on the top of the page instead of the middle. The text did not wrap. – Jon Dosmann Sep 08 '14 at 17:26
  • @JonD I think this is the right idea, though. If you can just restrict the width of the item template ("ParentGrid") to the window size, then that's all you need. – McGarnagle Sep 08 '14 at 17:29
  • This is not going to cause the text to wrap. If I take the grid and set it's width to smaller than the width it stretches out to inside the viewbox then it will cause the height to increase as the text wraps inside the narrower grid, then the whole thing is blown up to fit the page better by the ViewBox – Jon Dosmann Sep 08 '14 at 17:49