Apply Style From Resource to ListView.ItemContainerStyle - wpf

I'm working with XAML/WPF in VS 2012. I'll admit I don't really understand templating and styling very well yet.
I've defined a style in my application.xaml file like this:
<Style x:Key="ContactGroups" TargetType="ListViewItem">
<!-- Styling omitted here -->
</Style>
Now I want to apply this style to my list view but I cannot figure out where to apply this styling, i.e., where to put the code to set the style. I've omitted lots of attributes here to keep things shorter:
<ListView ItemsSource="{Binding Groups}" SelectedItem="{Binding Path=SelectedGroup, Mode=OneWayToSource}" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="140" Height="25">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Label Content="{Binding Name}" ToolTip="{Binding Name}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Use StaticResource markup extension to set Style on ItemContainerStyle of ListBox:
<ListView ItemsSource="{Binding Groups}"
SelectedItem="{Binding Path=SelectedGroup, Mode=OneWayToSource}"
ItemContainerStyle="{StaticResource ContactGroups}" >

I'm not sure if you only want this style to be applied to this list, but if not you could simply remove the x:Key="ContactGroups" from the style and it should be applied to all list items.
If you are looking to only target this listview an option would be to add the style to the resources of the list view:
<ListView ItemsSource="{Binding Groups}" SelectedItem="{Binding Path=SelectedGroup, Mode=OneWayToSource}" >
<ListView.Resources>
<Style TargetType="ListViewItem">
<!-- Styling omitted here -->
</Style>
</ListView.Resources>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="140" Height="25">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Label Content="{Binding Name}" ToolTip="{Binding Name}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Edit: Based on your comment below this may be the approach you want to take:
<ListView.Resources>
<Style TargetType="ListViewItem" BasedOn="{StaticResource ContactGroups}" />
</ListView.Resources>
This way your style stays defined in the App.xaml.

Related

Use CollectionViewSource with TabControl

I'm trying to group and display the items of an ObservableCollection, just by using XAML code. It works well using a simple CollectionViewSource and a ListBox[1].
Actually, I would prefer to display the group's content in a tabcontrol. Google led me to the following social.msdn article wich presents a workaround to display the groups as a TabControl using code behind:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e073f275-0826-4fca-b9da-e310ccf1713e/wpf-grouping?forum=wpf
However, as I'm using MVVM and must rely on xaml only, I can't get it to work. Actually, the CollectionViewSource populates the groups (the TabControl shows the correct tabItemHeaders), but clicking on any of these TabItems freezes the application. Here's what I've tried:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]: This peace of xaml does work as expected, but uses a wrappanel to display groups contents:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
I think there's something wrong with your binding your code should work.
To get the same items in both ListBoxes try to bind the second ListBox Itemssource to the first ListBox Itemssource like this :
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks to Joh who helped with the appropriate DataBinding. However, the reason was totally different, a quick and dirty solution is given below[1]:
Basically, I was missing that the above mentioned tab control is nested within an outer Tab Control in my main window. I am not toally sure if the following description is entirely correct[1], but to my mind the reason is the following:
The outer TabControl uses a style to display its content. This content applies to a ViewModel which holds the above mentioned observable collection, which in turn should be the ItemsSource of the CollectionViewSource that feeds the inner tabControl with the groups.
As I have defined this style only in the outer TabControl.Resources, and missed to define a separate style for the inner tab Control, the inner tabcontrol inherits the outer style and tries to display its data using the same content.
This content is again another inner tabControl, which calls another inner tabControl and so on.
[1] Defining an empty style in the inenr tabControl.Resources solved the problem:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
I would be happy if someone could confirm this idea or provide some links to well known issues with shared styles in nested controls.

Change element inside Template

I want to reuse the the ItemsControl from my ListView and its not really hard to put the ItemsControle into an Template.
The problem is, I want to change the DataTemplate when I use my ItemsControlTemplate.
<ListView DataContext="{Binding Input}">
<ItemsControl ItemsSource="{Binding Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Label Content="{Binding ColumnHeader}"></Label>
</StackPanel>
<ListView Grid.Row="1">
<ItemsControl ItemsSource="{Binding ColumnData}">
<ItemsControl.ItemTemplate>
<!--This should be changed when I reuse my ItemsControl-->
<DataTemplate>
<Button Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListView>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListView>
You can use a Resource to set that ItemTemplate dynamically. Just provide a default Resource of DataTemplate, when you need to change it, override that resource (using the same key and place in a closer place to the control).
<ListView Grid.Row="1">
<ItemsControl ItemsSource="{Binding ColumnData}"
ItemTemplate="{StaticResource templateKey}"/>
</ListView>
Then define a resource with x:Key of templateKey like this:
<Window.Resources>
<DataTemplate x:Key="templateKey">
<Button Content="{Binding}"/>
</DataTemplate>
<Window.Resources>
Here is an example of overriding the template, in which we place another DataTemplate inside the ListView's Resources:
<ListView DataContext="{Binding Input}">
<ListView.Resources>
<DataTemplate x:Key="templateKey">
<CheckBox Content="{Binding}"/>
</DataTemplate>
</ListView.Resources>
<!-- ... -->
</ListView>
If you want to update the template at runtime, you need to use DynamicResource instead of StaticResource.

WPF List control with header

I'm new to WPF. I trying to display information in a scrollable list/grid that has a header and the user is NOT allowed to click on the items in the list. Also I need a divider between each item in the list.
Right now I'm using a HeaderedContentControl and putting a ListView in the HeaderedContentControl.Content. I can't seem to get the ListView to scroll without setting a specific height on it and it would be nice to have the control be able to resize.
<UserControl ...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<HeaderedContentControl Margin="10">
<HeaderedContentControl.Header>
<Label ... />
</HeaderedContentControl.Header>
<HeaderedContentControl.Content>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Info1}" />
<TextBlock Text="{Binding Info2}" />
<Separator />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</HeaderedContentControl.Content>
</HeaderedContentControl>
</Grid>
</UserControl>
In the past I've been able to get a ListView scrolling if I put it as a direct child of Grid, but here it's not picking up the height of * as the constraint.
Have you considered using a datagrid? Based on your description I created the following xaml and tested and I believe it satisfies your requirements.
<DataGrid ItemsSource="{Binding Items}" HeadersVisibility="Column"
VerticalScrollBarVisibility="Auto" IsReadOnly="True"
CanUserSortColumns="False" AutoGenerateColumns="False"
IsHitTestVisible="True"
CanUserReorderColumns="False" Height="100"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Header1" Binding="{Binding Info1}"/>
<DataGridTextColumn Header="Header2" Binding="{Binding Info2}"/>
</DataGrid.Columns>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
The IsHitTestVisible must be set false in the cell style and column header style to allow the scroll bar to still work.
Datagrids have a lot more properties which you may be able to exploit to get exactly what you are looking for.

Tabbing User Control In Listbox DataTemplate

I have a User Control in a ListBox and when I use the tab key to focus I'd like to get the focus on the TextBox in my Control instead of the Custom Control. How can I do it?
I simplified the code because In my control I have other UI Elements.
User Control Code:
<Grid>
<TextBox Name="txtFreeTextDescription" Style="{StaticResource TextBoxStyleLargeDynamic}" Text="{Binding Description, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
</Grid>
ListBox Code:
<ListBox Name="lsbItems" DataContext="{Binding}" KeyboardNavigation.TabNavigation="Local">
<ListBox.ItemTemplate>
<DataTemplate>
<local:SectionDynamicItem x:Name="ucSectionDynamicItem" Description="{Binding SectionItem.Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works for me....
<ListBox ItemsSource="{Binding EmployeeList}"
KeyboardNavigation.TabNavigation="Continue">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Focusable" Value="False"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5" Focusable="False">
<TextBox Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
Check below link.
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/98d8423c-9719-4291-94e2-c5bf3d80cd46/
Thanks
Rajnikant

WPF Grid as ItemsPanel for a list dynamically bound to an ItemsControl

I am using a Grid as ItemsPanel for a list dynamically bound to an ItemsControl. The code below is working - with a remaining problem: I can’t find a way to dynamically initialize the ColumnDefinitions and RowDefinitions of the grid. As consequence all values are placed on top of each other.
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Please be aware, that I am searching an answer according the MVVM pattern. Therefore sub classing and code behind are just workarounds, but no solutions.
You'll need some way to tell the Grid how many Rows/Columns it has. Perhaps as each Item loads you could check the value of RowIndex and ColumnIndex and add Rows/Columns to the Grid if needed.
As another alternative, perhaps you can expose RowCount and ColumnCount properties in your ViewModel that return the max RowIndex and ColumnIndex, and in the Grid's Loaded event add however many Columns/Rows you need.
I find it perfectly acceptable to use code-behind in MVVM IF the code is related to the UI only.
Another idea would be to arrange your items in your code-behind into a 2D grid before returning it to the View, and then bind that Grid to a DataGrid with AutoGenerateColumns=True and the headers removed
Update
My current solution to this problem is to use a set of AttachedProperties for a Grid that allow you to bind RowCount and ColumnCount properties to a property on the ViewModel
You can find the code for my version of the attached properties on my blog here, and they can be used like this:
<ItemsPanelTemplate>
<Grid local:GridHelpers.RowCount="{Binding RowCount}"
local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>
Your grid has zero rows and columns so everything will be displayed on top of each other. Do something like below and it will work.
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
i found this from here The ItemsControl
<ItemsControl ItemsSource="{Binding VehicleMakes}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="300" Height="100" Click="ButtonOption_Click" Tag="{Binding Name}">
<StackPanel Orientation="Vertical">
<Image
Initialized="Image_Initialized"
Tag="{Binding ResourseImageName}"
Width="116"
Height="30"
Margin="0,0,0,10" >
</Image>
<TextBlock Text="{Binding Name}" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Resources