Star sizing Grid columns in an ItemsControl? - wpf

I have a ListBox with an ItemsControl bound to a collection in my view model. I'm trying to use star sizing on a grid column within the DataTemplate and set the element within that column (a progress bar) to stretch. This normally would take up all available horizontal space in the grid however nested in an ItemsControl this does not seem to be the case. I've done a bit of reading around & there seems to be known issues using these controls together. Is there a solution to this?
<ListBox>
<ItemsControl ItemsSource="{Binding WebMappingSourcesCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid
ShowGridLines="True"
Grid.IsSharedSizeScope="true"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox
Grid.Column="0"
HorizontalAlignment="Center" />
<TextBlock
Grid.Column="1"
Text="{Binding Name}"/>
<ProgressBar Grid.Column="2"
Minimum="0"
Maximum="100"
Value="30"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
MaxHeight="15"
/>
<!-- etc. etc. -->

Try setting HorizontalContentAlignment on your listbox item by giving ItemContainerStyle like below.
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Related

Listbox with Grid as ItemPanelTemplate: Problem with SharedSizeGroup

I have set up a ListBox where the items are another ListBox.
This allows me to bind an observable collection that contains a list.
I used an horizontal StackPanelas the ItemsPanelTemplate of the second ListBox but it was ugly so I tried to use a Grid instead (for the moment I fixed the number of columns to test if everything is working).
Everything is in the correct cell, but it looks like the SharedSizeGroup is not taken in account, what I would like is that the cells of the same columns have the same width (the one of the widest element)
See picture of what is happenning with my XAML code:
And what I would like to achieve
I think that I did not place the Grid.IsSharedSizeScope="True" to the proper place or maybe I did something else wrong.
I know this looks like a DataGrid for now, but in the next step some lines may not have the same number of items.
XAML Code
<ListBox Grid.Row="1" ItemsSource="{Binding LibraryGroupedValuesList}"
SelectedItem="{Binding SelectedGroupedValues}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Position}" VerticalAlignment="Center">
</TextBlock>
<ListBox Grid.IsSharedSizeScope="True" Grid.Column="1" ItemsSource="{Binding PositionValues}"
ItemTemplateSelector="{StaticResource VariableTypeTemplateSelector}"
HorizontalContentAlignment="Stretch" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="1"></ColumnDefinition>
<ColumnDefinition Width="auto" SharedSizeGroup="2"></ColumnDefinition>
<ColumnDefinition Width="auto" SharedSizeGroup="3"></ColumnDefinition>
<ColumnDefinition Width="auto" SharedSizeGroup="4"></ColumnDefinition>
<ColumnDefinition Width="auto" SharedSizeGroup="5"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Setters>
<Setter Property="Grid.Column" Value="{Binding Index}"/>
</Style.Setters>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
You have an outer ListBox that contains items which are itself ListBoxes, which have a Grid as ItemsPanel. This Grid defines shared size groups. Putting the Grid.IsSharedSizeScope="True" attached property on the inner ListBox does not work, because this scope only contains a single Grid, the items panel. The way it is right now means each inner ListBox with its own Grid as items panel defines its own shared size scope, which does not affect the others. This would work if you had e.g. an ItemTemplate containing a Grid, since then each item inside the ListBox and hence inside the shared size scope would be targeted.
This is also implies the solution to your issue, just move the shared size scope declaration to the outer ListBox, which is the scope containing all inner ListBoxes an their Grids.
<ListBox Grid.Row="1"
ItemsSource="{Binding LibraryGroupedValuesList}"
SelectedItem="{Binding SelectedGroupedValues}"
Grid.IsSharedSizeScope="True">

Sharing column width between grids defined in a template

I am trying to layout a couple of controls:
The light gray vertical lines are grid splitters. Each line in this table is a grid with three columns, the label on the left, the splitter in the middle and the control on the right, including the text boxes and the image.
I want all columns to be aligned. Here is the data template I use for each line:
<DataTemplate x:Key="PropertyLineTemplate">
<Grid Margin="0,0,0,1">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Name"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Value"/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Title}" />
<GridSplitter Grid.Column="1" Width="2"/>
<SomeUserControl
Grid.Column="2"
Content="{Binding}"
Margin="4, 0, 0, 0"/>
</Grid>
</DataTemplate>
and the template for the view itself:
<DataTemplate DataType="{x:Type vm:PropertiesViewModel}">
<DockPanel>
<!-- Properties -->
<ScrollViewer>
<StackPanel Margin="10">
<ItemsControl ItemsSource="{Binding Properties}"
ItemTemplate="{StaticResource PropertyLineTemplate}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</DataTemplate>
and the usage of both:
<ContentControl Content="{Binding SelectedItem}" Grid.IsSharedSizeScope="True">
</ContentControl>
where SelectedItem is of type PropertiesViewModel.
I also tried to place Grid.IsSharedSizeScope="True" in each individual grid directly and on the panels containing the control, without effects. How can I achieve that all columns share the width?
Grid.IsSharedSizeScope should be set on the first parent ItemsControl of a Grid, in your case it would be the ItemsControl in the ViewModel template. Then the columns width will be adjusted.
However, if you move one GridSplitter, then it will only adjust that row, not the others (See WPF SharedSizeGroup GridSplitter Issue for a potential solution, if you need resizing.)
Other stuff: you don't need the Stackpanel wrapper around the ItemsControl, since the ItemsControl is the only child. If you need to adjust the panel the ItemsControl uses, then set it on the Property ItemsPanel, like this:
<ItemsControl ItemsSource="{Binding Properties}"
Grid.IsSharedSizeScope="True"
ItemTemplate="{StaticResource PropertyLineTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/> <!-- Or <WrapPanel/> or <UniformGrid/> etc. -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
However, the Stackpanel is the default panel of the ItemsControl.
Based on Nautious' answer I've solved the problem by defining two ColumnDefinitions as resources and binding the Width property of all columns in question to the Width property of these 'master column definitions':
<ColumnDefinition x:Key="MasterColumnDefinition" />
<ColumnDefinition x:Key="MasterColumnDefinition2" />
<DataTemplate x:Key="PropertyLineTemplate">
<Grid Margin="0,0,0,1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Source={StaticResource MasterColumnDefinition}, Path=Width, Mode=TwoWay}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource MasterColumnDefinition2}, Path=Width, Mode=TwoWay}"/>
</Grid.ColumnDefinitions>
...
</Grid>
</DataTemplate>
Might be a little hacky, but it's simple and works. Not, however, that the shared size scope of this solution is the scope in which the resources are defined. I.e. placing these template in the app's resources will lead to all columns in the application being resized synchronously.

Wpf datagrid column header as Grid

I have DataGrid with customized column like that
<DataGridTemplateColumn Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding SomeProperty}"/>
<TextBlock Grid.Column="0" Text="{Binding OtherProperty}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="*" Header="Const" Binding="{Binding ConstantValue}" />
As you can see, first column has double width of second column and contains two texblocks. So I need make header consists from two words. Every word above corresponding TexBlock. And I try to use Grid with two columns (because I have textblocks in two columns) as HeaderTemplate. I try to set HeaderStyle like that
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Style.Setters>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">Header1</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">Header2</TextBlock>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
and I try to use HeaderTemplate like that
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">Header1</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">Header2</TextBlock>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
But in both cases I have the same wrong result. Namely. Header1 and Header2 placed togeter without any spaces like Header1Header2. And looks like Header1Header2 has HorizontalAligment="Left" relatively whole header.
I.e.
column border-->|Header1Header2___________|<--column border
So Header1 and Header2 do not placed above corresponding TextBlocks in DataGrid.
How can I solve my problem? How should I set HeaderTemplate to take what I want?
Would customizing the ControlTemplate work for you?
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">Header1</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">Header2</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</DataGridTemplateColumn.HeaderStyle>
Your other option is to create your own DataGridColumnHeadersPresenter which will hint the available size to the headers. By default, headers' ContentPresenters won't be told the available size and will calculate the width from their content (DataTemplate).
In your case, the width is not set explicitly (e.g. <Grid Width="200">) so it'll simply be the combined size of the TextBoxes.

How can a ListBox fill its parent width?

I have a ListBox and need that it fills the parent's width but have not found a way to do it, the ListBox has always the ListBox width and not the 100%.
Here is my XAML code:
<ListBox ItemsSource="{Binding anagSearchResults}" BorderThickness="0" Background="Gray"
SelectedItem="{Binding selectCustomer}"
FontSize="14"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding FirstName}"></TextBlock>
....
What am I doing wrong?
I would check the HorizontalAlignment of the parent(s) and make sure none of them are set to left, right or centre.
I would go with a ListView GridView.
Then for width you need to use a converter.
Change the converter in this sample to be parent width / 4
enter link description here
See the checked answer from me

How to set width to 100% in WPF

Is there any way how to tell component in WPF to take 100% of available space?
Like
width: 100%;
in CSS
I've got this XAML, and I don't know how to force Grid to take 100% width.
<ListBox Name="lstConnections">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="LightPink">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=User}" Margin="4"></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Password}" Margin="4"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Path=Host}" Margin="4"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Result looks like
alt text http://foto.darth.cz/pictures/wpf_width.jpg
I made it pink so it's obvious how much space does it take. I need to make the pink grid 100% width.
It is the container of the Grid that is imposing on its width. In this case, that's a ListBoxItem, which is left-aligned by default. You can set it to stretch as follows:
<ListBox>
<!-- other XAML omitted, you just need to add the following bit -->
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
You could use HorizontalContentAlignment="Stretch" as follows:
<ListBox HorizontalContentAlignment="Stretch"/>

Resources