WPF SharedSizeGroup does not adjust properly

331 views Asked by At

In my application I have two classes DataObj and SubDataObj. For those two classes I want to create a ListView in which I can edit objects of both classes. DataObj holds a List<SubDataObj> and some data modification have an impact on the SubDataObj properties.

My approach was to build my UI like this:

Kind of table view to edit

Structured like this:

View explanation

But I can not align the border of the 3 parts of the view and I can not figure out what I'm doing wrong.

The parent grid has Grid.IsSharedSizeScope="True" and all ColumnDefinitions, the ones in the Grid representing the headers, the ones in the ListView.DataTemplate use SharedSizeGroup="xxx".

My XAML-Code looks like this:

<UserControl
    x:Class="SharedSizeGroupSandBox.Views.SharedSizeGroupView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:SharedSizeGroupSandBox.Views"
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    xmlns:models="clr-namespace:SharedSizeGroupSandBox.Models.SharedSizeGroup"
    xmlns:vms="clr-namespace:SharedSizeGroupSandBox.ViewModels"
    xmlns:converter="clr-namespace:SharedSizeGroupSandBox.ValueConverter"
    mc:Ignorable="d"
    d:DesignHeight="450"
    d:DesignWidth="800"
    d:DataContext="{x:Type vms:SharedSizeGroupVM}">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolNotConverter
                x:Key="BoolNotConverter" />

            <Style
                x:Key="baseAlignStyle"
                TargetType="FrameworkElement">
                <Setter
                    Property="HorizontalAlignment"
                    Value="Stretch" />
                <Setter
                    Property="VerticalAlignment"
                    Value="Center" />
            </Style>
            <Style
                x:Key="baseBorderStyle"
                TargetType="Border"
                BasedOn="{StaticResource baseAlignStyle}">
                <Setter
                    Property="Margin"
                    Value="0,0" />
            </Style>
            <Style
                x:Key="headerBorderStyle"
                TargetType="Border"
                BasedOn="{StaticResource baseBorderStyle}">
                <Setter
                    Property="BorderBrush"
                    Value="Black" />
                <Setter
                    Property="VerticalAlignment"
                    Value="Stretch" />
                <Setter
                    Property="BorderThickness"
                    Value="0,0,1,0" />
            </Style>
            <Style
                TargetType="FrameworkElement"
                x:Key="baseMargingStyle"
                BasedOn="{StaticResource baseAlignStyle}">
                <Setter
                    Property="Margin"
                    Value="3" />
            </Style>
            <Style
                x:Key="textBlockHeaderStyle"
                TargetType="{x:Type TextBlock}"
                BasedOn="{StaticResource baseMargingStyle}">
                <Setter
                    Property="TextAlignment"
                    Value="Center" />

            </Style>
            <Style
                x:Key="checkBoxStyle"
                TargetType="CheckBox"
                BasedOn="{StaticResource baseMargingStyle}">
                <Setter
                    Property="HorizontalAlignment"
                    Value="Center" />
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>

    <!--#endregion  Buttons-->
    <ScrollViewer
        Grid.Row="1"
        HorizontalAlignment="Stretch"
        Grid.IsSharedSizeScope="True"
        HorizontalScrollBarVisibility="Auto">
        <Grid
            Grid.Row="1"
            HorizontalAlignment="Left"
            Margin="0">
            <Grid.RowDefinitions>
                <RowDefinition
                    Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <!--#region  Headers-->
            <Grid
                Margin="0,0,0,0"
                Grid.Row="0"
                HorizontalAlignment="Left"
                x:Name="grdlistHeader">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition
                        SharedSizeGroup="a" />
                    <ColumnDefinition
                        SharedSizeGroup="b" />
                    <ColumnDefinition
                        SharedSizeGroup="c" />
                    <ColumnDefinition
                        SharedSizeGroup="d" />
                    <ColumnDefinition
                        SharedSizeGroup="e" />
                    <ColumnDefinition
                        Width="Auto"
                        SharedSizeGroup="f" />
                </Grid.ColumnDefinitions>
                <Border
                    Style="{StaticResource headerBorderStyle}">
                    <TextBlock
                        Text="{Binding NameText}"
                        Style="{StaticResource textBlockHeaderStyle}" />
                </Border>
                <Border
                    Style="{StaticResource headerBorderStyle}"
                    Grid.Column="1">
                    <TextBlock
                        Text="{Binding DescriptionText}"
                        Style="{StaticResource textBlockHeaderStyle}" />
                </Border>
                <Border
                    Style="{StaticResource headerBorderStyle}"
                    Grid.Column="2">
                    <TextBlock
                        Text="{Binding MandatoryText}"
                        Style="{StaticResource textBlockHeaderStyle}" />
                </Border>
                <Border
                    Style="{StaticResource headerBorderStyle}"
                    Grid.Column="3">
                    <TextBlock
                        Text="{Binding ObsoleteText}"
                        Style="{StaticResource textBlockHeaderStyle}" />
                </Border>
                <Border
                    Style="{StaticResource headerBorderStyle}"
                    Grid.Column="4">
                    <TextBlock
                        Text="{Binding CountryText}"
                        Style="{StaticResource textBlockHeaderStyle}" />
                </Border>
                <TextBlock
                    Grid.Column="5"
                    Text="{Binding CityText}"
                    Style="{StaticResource textBlockHeaderStyle}" />
            </Grid>

            <!--#endregion  Headers-->
            <ListView
                Margin="0,0,0,0"
                Padding="0"
                Grid.Row="1"
                BorderThickness="0,0,0,0"
                BorderBrush="Black"
                SelectionMode="Single"
                SelectedItem="{Binding SelectedItem}"
                ItemsSource="{Binding Data}"
                HorizontalAlignment="Stretch"
                ScrollViewer.HorizontalScrollBarVisibility="Hidden"
                ScrollViewer.VerticalScrollBarVisibility="Hidden">
                <ListView.ItemContainerStyle>
                    <Style
                        TargetType="ListViewItem">
                        <Setter
                            Property="HorizontalAlignment"
                            Value="Stretch" />
                        <Setter
                            Property="Padding"
                            Value="0" />
                        <Setter
                            Property="Margin"
                            Value="0" />
                        <Setter
                            Property="BorderThickness"
                            Value="0,1,0,0" />
                        <Setter
                            Property="BorderBrush"
                            Value="Black" />
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.ItemTemplate>
                    <DataTemplate
                        DataType="{x:Type vms:DataObjVM}">
                        <Grid
                            Margin="0">
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <!--#region  DataObjVMs-->
                            <Grid
                                Margin="0">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition
                                        SharedSizeGroup="a"
                                        Width="Auto" />
                                    <ColumnDefinition
                                        SharedSizeGroup="b" />
                                    <ColumnDefinition
                                        SharedSizeGroup="c" />
                                    <ColumnDefinition
                                        SharedSizeGroup="d" />
                                    <ColumnDefinition
                                        SharedSizeGroup="e" />
                                    <ColumnDefinition
                                        SharedSizeGroup="f" />
                                    <ColumnDefinition
                                        SharedSizeGroup="g" />
                                    <ColumnDefinition
                                        SharedSizeGroup="h" />
                                    <ColumnDefinition
                                        SharedSizeGroup="i" />
                                </Grid.ColumnDefinitions>
                                <Border
                                    Style="{StaticResource headerBorderStyle}"
                                    Grid.Column="0">
                                    <TextBox
                                        Text="{Binding Path=Data.Name, UpdateSourceTrigger=PropertyChanged}"
                                        IsReadOnly="{Binding Path=Obsolete}"
                                        Style="{StaticResource baseMargingStyle}"
                                        Padding="0" />
                                </Border>
                                <Border
                                    Style="{StaticResource headerBorderStyle}"
                                    Grid.Column="1">
                                    <TextBox
                                        Text="{Binding Path=Data.Description}"
                                        IsReadOnly="{Binding Path=Obsolete}"
                                        Style="{StaticResource baseMargingStyle}" />
                                </Border>
                                <Border
                                    Style="{StaticResource headerBorderStyle}"
                                    Grid.Column="2">
                                    <CheckBox
                                        IsEnabled="{Binding Path=Obsolete, Converter={StaticResource BoolNotConverter}}"
                                        IsChecked="{Binding Path=Data.Mandatory}"
                                        Style="{StaticResource checkBoxStyle}" />
                                </Border>
                                <Border
                                    Style="{StaticResource headerBorderStyle}"
                                    Grid.Column="3">
                                    <CheckBox
                                        x:Name="cbObsolete"
                                        IsChecked="{Binding Path=Obsolete}"
                                        Style="{StaticResource checkBoxStyle}" />
                                </Border>
                                <Border
                                    Style="{StaticResource headerBorderStyle}"
                                    Grid.Column="4">
                                    <ComboBox
                                        Style="{StaticResource baseMargingStyle}"
                                        ItemsSource="{Binding Countries}"
                                        SelectedItem="{Binding Data.Country}"
                                        DisplayMemberPath="ExternalName"
                                        IsEnabled="{Binding Obsolete, Converter={StaticResource BoolNotConverter}}" />
                                </Border>
                                <ComboBox
                                    Grid.Column="5"
                                    Style="{StaticResource baseMargingStyle}"
                                    ItemsSource="{Binding Data.Country.Cities}"
                                    SelectedItem="{Binding Data.City}"
                                    DisplayMemberPath="ExternalName"
                                    IsEnabled="{Binding Obsolete, Converter={StaticResource BoolNotConverter}}" />
                            </Grid>
                            <!--#endregion  Temmplates-->
                            <!--#region  SubTemmplates-->
                            <ListView
                                Margin="0,0,0,0"
                                BorderThickness="0"
                                ScrollViewer.HorizontalScrollBarVisibility="Hidden"
                                ScrollViewer.VerticalScrollBarVisibility="Hidden"
                                IsEnabled="{Binding IsEditEnabled}"
                                Padding="0"
                                Grid.Row="1">
                                <ListView.Resources>
                                    <CollectionViewSource
                                        x:Key="SubDataObjs">
                                        <CollectionViewSource.Source>
                                            <Binding
                                                Path="Data.SubDataObjs"
                                                Mode="OneWay"
                                                UpdateSourceTrigger="PropertyChanged" />
                                        </CollectionViewSource.Source>
                                        <CollectionViewSource.SortDescriptions>
                                            <scm:SortDescription
                                                PropertyName="Name"
                                                Direction="Ascending" />
                                        </CollectionViewSource.SortDescriptions>
                                    </CollectionViewSource>
                                </ListView.Resources>
                                <ListView.ItemContainerStyle>
                                    <Style
                                        TargetType="ListViewItem">
                                        <Setter
                                            Property="HorizontalAlignment"
                                            Value="Stretch" />
                                        <Setter
                                            Property="Padding"
                                            Value="0" />
                                        <Setter
                                            Property="Margin"
                                            Value="0" />
                                        <Setter
                                            Property="BorderThickness"
                                            Value="0,1,0,0" />
                                        <Setter
                                            Property="BorderBrush"
                                            Value="Black" />
                                    </Style>
                                </ListView.ItemContainerStyle>
                                <ListView.ItemsSource>
                                    <Binding
                                        Source="{StaticResource SubDataObjs}" />
                                </ListView.ItemsSource>
                                <ListView.ItemTemplate>
                                    <DataTemplate
                                        DataType="{x:Type models:SubDataObj}">
                                        <Grid
                                            Margin="0">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition
                                                    SharedSizeGroup="a" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="b" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="c" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="d" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="e" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="f" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="g" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="h" />
                                                <ColumnDefinition
                                                    SharedSizeGroup="i" />
                                            </Grid.ColumnDefinitions>

                                            <Border
                                                Style="{StaticResource headerBorderStyle}">
                                                <TextBox
                                                    Style="{StaticResource baseMargingStyle}"
                                                    Text="{Binding Path=Name}"
                                                    IsReadOnly="{Binding Path=Obsolete}"
                                                    MaxLength="{Binding MaxNameLength}" />
                                            </Border>
                                            <Border
                                                Style="{StaticResource headerBorderStyle}"
                                                Grid.Column="1">
                                                <TextBox
                                                    Style="{StaticResource baseMargingStyle}"
                                                    IsReadOnly="{Binding Path=Obsolete}"
                                                    BorderThickness="1"
                                                    Text="{Binding Path=Description}" />
                                            </Border>
                                            <Border
                                                Style="{StaticResource headerBorderStyle}"
                                                Grid.Column="2"
                                                Padding="0, 0,0,0">
                                                <CheckBox
                                                    IsEnabled="{Binding Path=Obsolete, Converter={StaticResource BoolNotConverter}}"
                                                    IsChecked="{Binding Path=Mandatory}"
                                                    Style="{StaticResource checkBoxStyle}" />
                                            </Border>
                                            <Border
                                                Style="{StaticResource headerBorderStyle}"
                                                Grid.Column="3">
                                                <CheckBox
                                                    IsChecked="{Binding Path=Obsolete}"
                                                    IsEnabled="{Binding ElementName=cbObsolete, Path=IsChecked, Converter={StaticResource BoolNotConverter}}"
                                                    Style="{StaticResource checkBoxStyle}" />
                                            </Border>
                                            <Border
                                                Style="{StaticResource headerBorderStyle}"
                                                Grid.Column="4" />
                                        </Grid>
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
                            <!--#endregion  SubTemmplates-->
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>

            </ListView>
        </Grid>
    </ScrollViewer>
</UserControl>

I have written a full working sandbox application in case the XAML itself is not enough.

Thank you in advance.

1

There are 1 answers

0
Keith Stein On

Make a single collection that holds both DataObj and SubDataObj.

When you load the data, loop through each DataObj, and for each one add itself and all of its SubDataObjs to the overall list. Then you can use a normal ListView with a normal GridView and use the overall list as the ItemsSource.

As for the type of this "overall collection", I see three options:

  • DataObj and SubDataObj obviously share some properties (since they can both be displayed in the same grid), so it might make sense for them to both inherit from the same base class, or for one of them to inherit form the other. Then you could use a SomeCollection<BaseDataObj> (for example).
  • If inheritance is not appropriate, have them both implement the same interface which includes their shared properties. Then you would use a SomeBaseCollection<IDataInterface> (for example).
  • If for some reason you don't want to do either of the above, just use a List<Object>.