Matching vertical layout in different ItemsControls - wpf

I have ListView that uses a GridView to display several columns of data.
Two of the columns have cell templates containing ItemControl's to show a small collection in each cell.
The problem I have is that the vertical size of the ItemControls is not connected in any way since they are in different cells, and therefore they don't line up.
eg:(note this is ONE ROW in the parent listview)
XAML so far:
<ListView Margin="6,6,6,0" ItemsSource="{Binding Controllers}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" >
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<!-- snip other columns -->
<GridViewColumn Header="Mode">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Modes}" DisplayMemberPath="Name"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Parameters">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Modes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Blah:" Margin="5,0"/>
<ComboBox>
<ComboBoxItem>Hello</ComboBoxItem>
</ComboBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
My first thought was to use a grid as the ItemsPanel of the ItemsControls, so I could use SharedSizeScope on the rows to line them up. However, I can see no way of assigning items dynamically created by the ItemsControl to specific Grid rows.
Can anyone suggest a better solution?

Managed to fix this by setting ListView.ItemContainerStyle.VerticalContentAlignment to Stretch, and then using UniformGrid's for the ItemsControl panels:
Each cell now stretches vertically to match the largest cell, and using UniformGrid splits that vertical size evenly for each item.
eg
<GridViewColumn Header="Mode">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Modes}" DisplayMemberPath="Name">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Related

Putting ComboBox in a one special row in ListView in WPF

How to put a Combobox to a special row of a listview (WPF, VB.net)?
There is a ListView that has two columns. The columns are "Parameter" and "Values". The second column has different types (such as numbers, strings and "Selectable values") and is editable. The "Selectable values" have to appear in a Combobox.
One of the ways that I think about, is conditional XAML tags (I don`t know if it is possible or not ?) .
I tried this code but it shows Combobox for all rows in listview :
<ListView x:Name="listView"
HorizontalAlignment="Left"
Height="237"
Margin="40,34,0,0"
VerticalAlignment="Top"
Width="447"
ItemsSource="{Binding Values}">
<ListView.View>
<GridView>
<GridViewColumn Header="Parameter"
DisplayMemberBinding="{Binding Path=LName}"
Width="100" />
<GridViewColumn Header="Value"
Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=ItemsSource}"
Width="120">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I only want to use Combobox for one row while the type of the rest of rows are double or string. How can I do this?

how to make ListView nonselectable, width like "*", and use DataTemplate

I am learning to use ListView to list data with multiple columns (i.e. column 1 is filename, column 2 is number of pages, etc.), and encountered several problems:
The code below shows 2 columns, where the column width is specified by <GridViewColumn Width="Auto">. How can I specify the columns to use all available width? I tried to set Width="*" like the one for Grid, but that generates an error.
How to make ListView nonselectable? I saw this post on making ListBox nonselectable, but a similar approach doesn't work for ListView (shown in code).
UPDATE: solved question #2. see code.
Can you show a simple example of a ListView using DataTemplate? I just want to have something like <ListView ItemsSource="{Binding }" ItemTemplate="{StaticResource myTemplate}"/> in the MainWindow, so things stay clean.
(4.ish. i learned the code below from this post. Please let me know if there is a simpler way.)
Thank you!
<ListView HorizontalContentAlignment="Stretch" Grid.Row="0" ItemsSource="{Binding }">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="False"/> <--- disable selection
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="Auto">
<GridViewColumnHeader Content="Filename"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Filename}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto">
<GridViewColumnHeader Content="Pages"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Pages}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView>
I'm not sure if there is any other straight-forward solution (although I'm almost sure there is not any direct way to go). The following solution can be considered as a work-around but it's actually a beautiful one.
You can make the second column (or generally the last column) fill the remaining space by using some Binding to bind the ListView's Columns' Width to the Grid's Columns' Width. Yes we know that only Grid has some features which no other controls have. Here are the detailed steps:
Firstly, we need a Grid having the same number of columns with the ListView. The ListView of course should be placed inside this Grid. By using Grid.ColumnSpan we can make the ListView fill the whole Grid.
Secondly, bind the Width of all the Grid columns to the ActualWidth of the corresponding ListView columns except the last Column. The last column of the Grid should have Width set to * (fill the remaining space).
Lastly, you need some dummy element (such as a Border) put in the last column of the Grid. We need this dummy element to get its ActualWidth, so that we can bind the last ListView column's Width to that ActualWidth. A ColumnDefinition of the Grid also has the property ActualWidth but it is not a dependency property and also it does not have any property changed notifying mechanism (so we cannot use that property).
Now is the working code:
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ActualWidth,ElementName=firstCol}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- the dummy element -->
<Border Grid.Column="1" Name="dummy" Margin="5"></Border>
<ListView HorizontalContentAlignment="Stretch" Grid.ColumnSpan="2"
ItemsSource="{Binding }">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" x:Name="firstCol">
<GridViewColumnHeader Content="Filename"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Filename}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="{Binding ActualWidth, ElementName=dummy}">
<GridViewColumnHeader Content="Pages"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Pages}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView>
</Grid>
Note that your original ListView may be placed in some Grid (you set its Grid.Row="0"). However when using this trick, the ListView should always be placed inside a wrapper Grid and you have to set Grid.Row="0" (although this is set by default) for this wrapper Grid instead. I mean this wrapper Grid will be placed in your original Grid (if any present).
Update: To make it dynamic like as a Grid, you need more dummy elements (we use just 1 dummy Border for the last column in the code above). Each dummy elements will be put in each Grid's column. As I explained, we need this to proxy the ActualWidth of the Grid's column, although ColumnDefinition also has an ActualWidth property but it cannot be used in Binding (does not support any change notification). Here is the code:
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<!-- the dummy elements -->
<Border Grid.Column="0" Name="dummy1" Margin="5"></Border>
<Border Grid.Column="1" Name="dummy2" Margin="5"></Border>
<!-- your ListView -->
<ListView HorizontalContentAlignment="Stretch" Grid.ColumnSpan="2"
ItemsSource="{Binding }">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="{Binding ActualWidth, ElementName=dummy1}">
<GridViewColumnHeader Content="Filename"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Filename}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="{Binding ActualWidth, ElementName=dummy2}">
<GridViewColumnHeader Content="Pages"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Pages}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView>
</Grid>

Is there any simplier/better way to auto-size the content of one-column ListView?

Alright, sorry for my english in advance, here what I need:
I got a ListView in XAML (WPF) where there items is located and what I need is that items to have the width equal to the ListView's width.
So far, I did this:
<ListView Name="lvFiles" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid Width="{Binding ElementName="lvFiles", Path=ActualWidth}">
<!-- All the controls inside I need to present an item -->
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
But as it turned out, the ListView items become clipped on the right when the vertical scrollbar appears. And it appears when the total height of all items exceeds the ListView.ActualHeight, so I needed to user bindings more complex and hence more complicated.
Also, my items, while I was spending hours to solve the problem, my items sometimes were clipped on the right with no obvious reason.
So I came up with this:
<ListView Name="lvFiles" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.View>
<GridView>
<GridViewColumn Width="{Binding ElementName=lvFiles, Path=ActualWidth}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Decorator Width="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=ListViewItem},
Path=ActualWidth}">
<Grid Margin="0,0,18,0">
<!-- All the controls inside... -->
</Grid>
</Decorator>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
It solved clipping problem and also it solved the problem with one-columned ListViewItem auto-size. But now it seems to be not the simpliest way. Is there any such?
You're not using a header so you can use ListView directly without using GridView. As for the width, don't adjust the content width, but adjust the ListViewItem width.
Here's an example:
<Style x:Key="singleListViewItemStyle"
BasedOn="{StaticResource {x:Type ListViewItem}}"
TargetType="{x:Type ListViewItem}">
<Setter Property="Width"
Value="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type VirtualizingStackPanel}}}" />
</Style>
If you do not have an existing style targeting ListViewItem or do not want to inherit from it, you can remove BasedOn="{StaticResource {x:Type ListViewItem}}".
AncestorType={x:Type VirtualizingStackPanel} is used because by default, that is what ListView displays its content in. If you have your own ListView theme, then see the following example:
<ListView Name="myListView"
ItemContainerStyle="{StaticResource ResourceKey=singleListViewItemStyle}"
ItemsSource="{Binding Path=MyItems}"
SelectedItem="{Binding Path=MySelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<!--Your favorite controls-->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is the link for a demo project I made for you. You can also see my other controls.
I hope this helps.

C# WPF Dynamic UniformGrid

I have a textbox where you can input an integer (which will the size of the UniformGrid). On a button click, a UniformGrid is generated with each grid contains some textblock, textbox, and a button, generated under different ElementName. I went through so many tutorial and all simply Add something to the children. And I can't seem to set the binding logic worked out - which bind to what, and which is being itemcontrolled. I went through wpftutorial and it just confuses me further. Appreciate if anyone can explain the logic in simple terms.
UniformGrid cannot really be used as an ItemsHost for an ItemsControl, mainly because the DataTemplate can only take a single child, which prohibits its use in this context.
Here's an alternative approach that might help you achieve what you want (I think)
<ListView ItemsSource="{Binding MyData}">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridView.Columns>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SomeLabelText}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding SomeInputText}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="{Binding SomeButtonLabel}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>

Make WPF ComboBoxes fill a whole column width

I'm having problems making a ComboBox stretch to fill the whole column width in a GridViewColumn. It should also resize when the column is resized.
In the following example I have a StackPanel with a ComboBox inside. This is set to stretch and will in fact stretch to fill the StackPanel width.
Then I add a ListView with one column, containing a StackPanel with a ComboBox. Both the StackPanel and the ComboBox are set to stretch, but they don't. I use background colors to identify the size of the StackPanels, and there is no red unless I set a width or add elements to the ComboBox such that it needs more width.
I also tried playing around with the HorizontalContentAlignment property without success.
<StackPanel Height="59" Margin="45,12,38,0" VerticalAlignment="Top" Background="Green">
<ComboBox HorizontalAlignment="Stretch" />
</StackPanel>
<ListView x:Name="MyListView" Margin="0,106,0,0">
<ListView.View>
<GridView>
<GridViewColumn Header="Num" Width="70">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Background="red" Orientation="Horizontal" HorizontalAlignment="Stretch">
<ComboBox HorizontalAlignment="Stretch" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListViewItem></ListViewItem>
</ListView>
Try setting the Style of the ListViewItem. I also removed your StackPanel.
<ListView x:Name="MyListView" Margin="0,106,0,0">
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Num" Width="170">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListViewItem></ListViewItem>
</ListView>

Resources