I had a Dictionary of values that I wanted to display in an XAML UI as part of a UWP app. My initial foray was to use a ListView with a binding to the dictionary ListView.ItemTemplate/DataTemplate. The dictionary was then bound to the ListView’s ItemsSource. The problem with the ListView is that there is a lot of padding between items vertically and within items.I wanted to compact the rows of data vertically though as if I had manually placed each dictionary item (key and values) in a grid of rows of TextBlocks in a row (one row for each item). This article discusses my trials and tribulations with this issue and includes my final solution.
Whilst I could have explicitly created a grid with one row for each dictionary item and a TextBlock in each row for the item key and one for each of the item values, this would have been a pain for two reasons:
A ListView example with padding between items
The XAML code for a typical ListView with Bound data is :
<ListView x:Name="ListCharacteristics" ItemsSource="{Binding CharacteristicOverviewAndDetails}" SelectionChanged="ListChracteristics_SelectionChanged" HorizontalAlignment="Left" VerticalAlignment="Top" MaxHeight="300" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollMode="Enabled" > <ListView.ItemTemplate> <DataTemplate> ………… </DataTemplate> </ListView.ItemTemplate> </ListView>
The ListView gets its items from the CharacteristicOverviewAndDetails construct which is an ObservableCollection of a class with public properties AssignedNumber, Mandatory,InDevice, and Type_s. (This construct isn’t a dictionary). The ItemTemplate lays out how each bound property for each instance of the class in the collection o\is presented. The template in this case is:
<ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="400" /> <ColumnDefinition Width="150"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="200" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=Name }" TextAlignment="Left" /> <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=AssignedNumber_s}" TextAlignment="Center" /> <TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Path=Mandatory }" TextAlignment="Center" /> <TextBlock Grid.Row="0" Grid.Column="3" Text="{Binding Path=InDevice }" TextAlignment="Center" /> <TextBlock Grid.Row="0" Grid.Column="4" Text="{Binding Path=Type_s}" TextAlignment="Left" /> </Grid> </DataTemplate> </ListView.ItemTemplate>
When there is just one instance of an object to display you can explicitly lay it out in a grid as. In the following example. An instance of a class callled DeviceInfo, called Device is set out:
<Grid Background="Beige" Width="810" HorizontalAlignment="Left" > <Grid.ColumnDefinitions > <ColumnDefinition Width="200" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> …. <RowDefinition/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" TextAlignment="Right" Text="Name:" FontStyle="Italic" FontWeight="Bold" Foreground="Blue"/> <TextBlock Grid.Row="1" Grid.Column="0" TextAlignment="Right" Text="BTAddressAsString:" FontStyle="Italic" FontWeight="Bold" Foreground="Blue"/> <TextBlock Grid.Row="2" Grid.Column="0" TextAlignment="Right" Text="Manufacturer:" FontStyle="Italic" FontWeight="Bold" Foreground="Blue"/> …. <TextBlock Grid.Row="0" Grid.Column="2" TextAlignment="Left" Text="{Binding Path=Device.Name}" /> <TextBlock Grid.Row="1" Grid.Column="2" TextAlignment="Left" Text="{Binding Path=Device.BTAddressAsString}" /> <TextBlock Grid.Row="2" Grid.Column="2" TextAlignment="Left" Text="{Binding Path=Device.Manufacturer}" /> …. </Grid>
Hint: In this class, BTAddress is formatted in the desired format as a string as the readonly (get) property BTAddressAsString.
The UI for the above XAML code.
Binding to items in a Dictionary implemented by binding to the dictionary item key and dictionary item value/s.
<ListView.ItemTemplate> <DataTemplate> <Grid Background="Beige" > <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="250"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="20" /> </Grid.RowDefinitions> <TextBlock Height="20" Grid.Row="0" Grid.Column="0" TextAlignment="Right" Text="{Binding Path=Key}" FontStyle="Italic" FontWeight="Bold" Foreground="DarkGreen"/> <TextBlock Height="20" Grid.Row="0" Grid.Column="2" TextAlignment="Left" Text="{Binding Path=Value.Item1}" /> <TextBlock Height="20" Grid.Row="0" Grid.Column="3" TextAlignment="Left" Text="{Binding Path=Value.Item2}" /> </Grid> </DataTemplate> </ListView.ItemTemplate>
In this case the dictionary value is a three item tuple. Item3 is a byte array. Item1 is the array cast to string. Item2 is a string formatted representation of the array bytes, each as a hex value:
public static Dictionary<DeviceProperties.SensorTagProperties, Tuple<string, string, byte>> DeviceProperties { get; set; }
SensorTagProperties is an enum:
public enum SensorTagProperties { SysId, DeviceName, ModelName, SerialNumber, FirmwareDate, HardwareRevision, SoftwareRevision, ManufacturerId, BTSigCertification, PNPId, BatteryService, BatteryLevel, NOTFOUND };
:
The desired presentation for the DeviceProperties
Typically there is a gap between item rows. This can be removed by adding as in the following code. Place directly before the ListView.ItemTemplate:
<ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <Setter Property="Padding" Value="0" /> <Setter Property="Margin" Value="0" /> <Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderBrush" Value ="Black" /> <Setter Property="VerticalContentAlignment" Value="Top" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListViewItem"> <ListViewItemPresenter ContentMargin="0" Padding="0" /> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListView.ItemContainerStyle>
Whilst this does compact the list vertically by removing the padding between items, there is still padding within each item which I found hard to remove.
ListView items with vertical spacing between items
ListView Items with padding between items removed
What is desired is to remove the internal padding for each item:
ListView item without any internal padding
I found that I couldn’t reduce the number of lines per item to less than two without some negative vertical margins, which requires a dummy value at the top. I tried setting row height, TextBlock height etc to no avail. A solution is now presented in the Addendum that zeroes the spacing between items in a ListView.
Part 1: XAML Layout for a Dictionary-Part1 (This)
Part 2: XAML Layout for a Dictionary-Part2
Part 3: XAML Layout for a Dictionary-Addendum