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:
Structured like this:
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.
Make a single collection that holds both
DataObj
andSubDataObj
.When you load the data, loop through each
DataObj
, and for each one add itself and all of itsSubDataObj
s to the overall list. Then you can use a normalListView
with a normalGridView
and use the overall list as theItemsSource
.As for the type of this "overall collection", I see three options:
DataObj
andSubDataObj
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 aSomeCollection<BaseDataObj>
(for example).SomeBaseCollection<IDataInterface>
(for example).List<Object>
.