views:

259

answers:

1

Hi all,

I am a wpf newbie & loving it. However I have a layout problem which I hope someone can help me with. I need to build a persons attributes editor. These consist of 2 fixed attributes - first name & lastname, plus a variable bucket of other attributes such as age, sex etc.

I have build a dialog consists of grid which contains 2 textboxes for the fixed attributes and a listbox for the variable attributes.

<Grid Name="mainGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="10"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <Label Grid.Column="0" Grid.Row="0" VerticalAlignment="Center">First Name:</Label>  
    <TextBox Name="tbFirstName" Grid.Column="2" Grid.Row="0" MinWidth="100" Margin="5" Text="{Binding Path=FirstName}"/>

    <Label Grid.Column="0" Grid.Row="1" VerticalAlignment="Center">Last Name:</Label>
    <TextBox Name="tbLastName" Grid.Column="2" Grid.Row="1" MinWidth="100" Margin="5" Text="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged}"/>       

    <ListBox Name="lstAttributes" Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding Path=Attributes, UpdateSourceTrigger=PropertyChanged}"/>

    <StackPanel Orientation="Horizontal" Grid.Row="3" Grid.ColumnSpan="3" HorizontalAlignment="Right">
        <Button Name="btnOk" IsDefault="True" Click="btnOk_Click" Grid.Column="0" Grid.Row="2" MinWidth="60" Margin="5">Ok</Button>
        <Button Name="btnCancel" IsCancel="True" Grid.Column="0" Grid.Row="2" MinWidth="60" Margin="5">Cancel</Button>
    </StackPanel>

</Grid>

I have a data layer that returns a person object which is bound. This contains a list of attributes that binds to the listbox. To support attributes of different types these derive from a common base class. i.e. IntegerAttribute : AttributeBase is used to represent the 'Age' attribute.

I then use data templates to render the correct controls depending on the type of attribute:

<Window.Resources>
        <DataTemplate DataType="{x:Type reg:IntegerAttribute}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Grid.Column="1" Text="{Binding Name}"/>
                <TextBox Grid.Column="3" Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type reg:TextAttribute}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>         
        </DataTemplate>
        <DataTemplate DataType="{x:Type reg:SingleChoiceAttribute}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <ComboBox ItemsSource="{Binding Path=Choices, UpdateSourceTrigger=PropertyChanged}" SelectedValue="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>

But here is the problem. I want the variable set attributes to appear in the same columns as the fixed attributes. I tried using SharedSizeGroup but this does not seem to work.

Many thanks,

NickD

A: 

In your custom property data templates, instead of using a stackpanel, try using a grid with 3 columns, like the containing grid, set IsSharedSizeScope to false on the inner grid, and to true on the outer grid, and use SharedGroupSize on the two grids.

Example:

<Window.Resources>
    <DataTemplate DataType="{x:Type reg:IntegerAttribute}">
        <Grid IsSharedSizeScope="False">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="groupa" Width="*"/>
                <ColumnDefinition SharedSizeGroup="groupb" Width="10"/>
                <ColumnDefinition SharedSizeGroup="groupc" Width="*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="1" Text="{Binding Name}"/>
            <TextBox Grid.Column="3" Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
        </Grid>
    </DataTemplate>
...
<Grid Name="mainGrid" IsSharedSizeScope="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition SharedSizeGroup="groupa" Width="*"/>
        <ColumnDefinition SharedSizeGroup="groupb" Width="10"/>
        <ColumnDefinition SharedSizeGroup="groupc" Width="*"/>
    </Grid.ColumnDefinitions>
...

Mind you, shared size groups only work with auto width columns if I remember correctly, so some of the column definitions of the above example might need to be changed to auto width.

On the other hand, since none of your columns are auto width, you may as well not use shared group sizes at all since column width will match anyway.

Here's a complete example (no code behind needed) that demonstrates this:

<Window x:Class="gridsh.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <x:ArrayExtension x:Key="data" Type="{x:Type sys:String}">
            <sys:String>Test1</sys:String>
            <sys:String>Test2</sys:String>
            <sys:String>Test3</sys:String>
            <sys:String>Test4</sys:String>
        </x:ArrayExtension>
        <DataTemplate DataType="{x:Type sys:String}">
            <Grid Grid.IsSharedSizeScope="False">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition SharedSizeGroup="groupa" Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Border BorderBrush="Black" BorderThickness="1">
                    <TextBlock Text="xxxxxx"/>
                </Border>
                <TextBlock Text="{Binding}" Grid.Column="1"/>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <Grid Grid.IsSharedSizeScope="True">
        <Grid Grid.IsSharedSizeScope="False" Height="30" VerticalAlignment="Top" Margin="3">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="groupa" Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Border BorderBrush="Black" BorderThickness="1">
                <TextBlock Text="WWWWWWWWWWWWWWWWWWWWWWW"/>
            </Border>
            <TextBlock Text="dgdfsg" Grid.Column="1"/>
        </Grid>
        <ListBox ItemsSource="{StaticResource data}" Margin="0,36,0,0" BorderThickness="0" Padding="0"/>
    </Grid>
</Window>
Aviad P.
Thanks for the reply. I just tried these changes, unfortunately whilst this works with fixed width columns it does not work with either Auto or *. If I set either first or last column to auto or * the dialog opens correctly with the column aligned, but then the last column moves across the dialog from left to right eventually disappearing. Any ideas why this might be happening, assume it is redrawing the column each time ...
NickUk
I believe it's a matter of setting `IsSharedSizeScope` correctly... I'll take a look again and update.
Aviad P.
Thanks, I have set the mainGrid to true and the data templates to false as you suggested.
NickUk
I added an example demonstrating how this solution works, it requires no code behind.
Aviad P.