How to autosize a silverlight 3 usercontrol based on its contents

autosizesilverlightuser-controlsxaml

Using Silverlight 3 I am trying to recreate the class object visualization as seen in Visual Studio's class diagram. However, my objects will represent database objects rather than class definitions. I have been partially successful as seen in the following two images.

alt text http://img41.imageshack.us/img41/5669/tablestructurenotexpandb.png a http://img41.imageshack.us/img41/7634/tablestructurenotexpand.png

Both of these have been created with fixed sizes, and so they will not autosize to the content, and rightly so.

However, I would like to be able to autosize the height and width of the control based on the number of rows and columns in the control respectively. I would prefer to be able to do this completely declaratively. Additionally, I would like it to autosize properly even if I change the font.

Yet, I am not sure how I would go about doing this. I am not sure if I am using controls that are more or less suited to autosizing. Any help would be much appreciated.

Here is the XAML that creates this control.

<UserControl x:Class="SchemaDesigner.TableBox2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Border BorderThickness="1" BorderBrush="#FF716F6E" CornerRadius="10" Width="252" Height="202">  <!-- Height="202"-->
        <Canvas>
            <Canvas.Clip>
                <RectangleGeometry RadiusX="10" RadiusY="10" Rect="0,0,250,200"/>
            </Canvas.Clip>
            <StackPanel Width="250"  Background="White">
                <StackPanel Width="250" Height="45" Orientation="Vertical">
                    <StackPanel.Background>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                            <GradientStop Color="#FFD3DCEF"  Offset="0.0" />
                            <GradientStop Color="#FFFFFFFF" Offset=" 1.0"/>
                        </LinearGradientBrush>
                    </StackPanel.Background>
                    <StackPanel Width="250" Orientation="Horizontal">
                        <StackPanel Width="210" Height="45" Orientation="Vertical">
                            <TextBlock Text="{Binding Name}" Width="190" HorizontalAlignment="Left" FontFamily="Verdana" Margin="10,10,0,0" VerticalAlignment="Top" FontWeight="Black" FontSize="12"/>
                            <TextBlock Text="{Binding Type}" Width="190" HorizontalAlignment="Left" FontFamily="Verdana" Margin="10,0,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
                        </StackPanel>
                        <Button Width="20" Height="20" VerticalAlignment="Top" Margin="10" Content="^" FontFamily="Verdana" />
                    </StackPanel>
                </StackPanel>

                <ItemsControl x:Name="items" ItemsSource="{Binding Rows}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                                <StackPanel Width="250" Orientation="Horizontal">
                                    <Image Width="16" Height="16" VerticalAlignment="Center" Source="/SchemaDesigner;component/column.png"/>
                                    <TextBlock Text="{Binding ColumnName}" Width="100" Height="20"/>
                                    <TextBlock Text="{Binding ColumnType}" Width="130" Height="20"/>
                                </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>

            </StackPanel>
        </Canvas>
    </Border>

</UserControl>

Best Solution

I've gone through the code and there is a lot of places in which the height is being set explicitly, which will interfere with the layout calculations.

The changes made are as follows :

  • The user control has an explicit height / width which I have removed.
  • The canvas was only being added to provide the clip feature, and it is not necessary (not to mention that canvas has no size specified which will cause issues)
  • Horizontal and Vertical alignment settings are specifically set as centered to avoid it stretching the content to fit the screen, it will fit the contents.
  • the rectangle providing the clipping to the top background to avoid it overwriting the border has been set to the height of the background +10 (the radius) so the bottom of the background is not also clipped to a curve (and 10 keeps it within a sensible height.)
  • I took out the heights in the item control, this isn't required, but I prefer the contents to ask for the height they need instead of being given an arbitary height. /edit : given you want it to change based on font size, this would become necessary, you can not explicitly set the height, and also want it to auto size the height at the same time /edit.

Created a dummy local class with the right properties / child collection and bound to it, the results look like I think you are wanting. If you want the bottom of the blue background to also curve, the height of the stackpanel clip should be set to 45.

Dont forget to rename the class back, my version was just mainpage1.

hope that helps

A.

    <UserControl x:Class="SilverlightApplication1.MainPage"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
                 mc:Ignorable="d">

    <Border BorderThickness="1" BorderBrush="#FF716F6E" CornerRadius="10" VerticalAlignment="Center" HorizontalAlignment="Center">
        <StackPanel Width="250" Orientation="Vertical" VerticalAlignment="Center">
             <StackPanel Width="250" Height="45" Orientation="Vertical" >
                <StackPanel.Clip>
                   <RectangleGeometry RadiusX="10" RadiusY="10" Rect="0,0,250,55"/> 
                </StackPanel.Clip>
                    <StackPanel.Background>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                        <GradientStop Color="#FFD3DCEF"  Offset="0.0" />
                        <GradientStop Color="#FFFFFFFF" Offset=" 1.0"/>
                    </LinearGradientBrush>
                </StackPanel.Background>
                <StackPanel Width="250" Orientation="Horizontal">
                    <StackPanel Width="210" Height="45" Orientation="Vertical">
                        <TextBlock Text="{Binding Name}" Width="190" HorizontalAlignment="Left" FontFamily="Verdana" Margin="10,10,0,0" VerticalAlignment="Top" FontWeight="Black" FontSize="12"/>
                        <TextBlock Text="{Binding Type}" Width="190" HorizontalAlignment="Left" FontFamily="Verdana" Margin="10,0,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
                    </StackPanel>
                    <Button Width="20" Height="20" VerticalAlignment="Top" Margin="10" Content="^" FontFamily="Verdana" />
                </StackPanel>
            </StackPanel>
            <ItemsControl x:Name="items" ItemsSource="{Binding Rows}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Width="250" Orientation="Horizontal">
                            <Image Width="16" Height="16" VerticalAlignment="Center" Source="/SchemaDesigner;component/column.png"/>
                            <TextBlock Text="{Binding ColumnName}" Width="100" />
                            <TextBlock Text="{Binding ColumnType}" Width="130" />
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Border>
</UserControl>
Related Question