How to style ListViewItem with ControlTemplate? - wpf

I'm new to WPF, and this whole world of binding and styling is pretty confusing.
I have created a copy of default ControlTemplate for ListViewItem:
<Window.Resources>
<ControlTemplate x:Key="ListViewItemControlTemplate1" TargetType="{x:Type ListViewItem}">
...
</ControlTemplate>
</Window.Resources>
Then I have columns in my ListView, for example:
<ListView x:Name="lstFiles">
<ListView.View>
<GridView>
<GridViewColumn x:Name="clmName" Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
If I add new item in Designer, I can easily apply that ControlTemplate:
<ListViewItem Content="ListViewItem1" Template="{DynamicResource ListViewItemControlTemplate1}"/>
But how do I use that template when items are binded to ListView from code using lstFiles.ItemsSource?

I think you would like to override the style for your listviewitem. from: How to override ListViewItem style?
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">
<Style.Resources>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.ListBox.xaml" />
</Style.Resources>
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="ListViewItem_PreviewMouseLeftButtonDown" />
</Style>

Related

Disable ListView selection and hover

Usually I can disable ListView selection doing like this thread suggests - or something similar where you set the ItemContainerStyle.
However I have this ListView that is defined like this:
<ScrollViewer>
<ListView ItemsSource="{Binding List, Mode=OneWay}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumnHeader Style="{StaticResource header}"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<!-- Some data -->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridViewColumn>
...
</GridView>
</ListView.View>
</ListView>
</ScrollViewer>
And if I try to specify the ItemContainerStyle - the data in the list just disappears.
Giving the above ListView, how would I proceed just to removed the selection?
Can you try this ItemContainerStyle? This basically will not pick up any input events.
<ListView ItemsSource="{Binding List, Mode=OneWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>

WPF - Can't right-align a TextBlock when using StringFormat

I have a TextBlock within a GridViewColumn. I format to currency using StringFormat. This causes the field to be left aligned. I can't get it right aligned. Tried HorizontalAlignment and TextAlignment but nothing works.
<TextBlock HorizontalAlignment="Right" Text="{Binding Path=Amount, StringFormat={}{0:€ # ##0}}" />
<TextBlock TextAlignment="Right" Text="{Binding Path=Amount, StringFormat={}{0:€ # ##0}}" />
If I do this from this post, it works but right aligns all columns. I want numbers right aligned, dates centre and text left.
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Right" />
</Style>
</ListView.Resources>
Any clues please?
Set HorizontalContentAlignment to Stretch. This will work:
<ListView>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Right"
Text="{Binding Path=Amount, StringFormat={}{0:€ # ##0}}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Change your resource to HorizontalContentAlignment to Stretch, this will allow the contents of each cell in the ListView to take up the entire space. Then your HorizontalAlignment attributes will work.

Listview Automatic Scrolling using MVVM

I'm trying to implement automatic scrolling of a Listview using MVVM. I know that you can update the listview by calling ScrollIntoView but that requires Code Behind which I'm trying to avoid.
I've bound my ListView's ItemSource to an ObservableCollection and would like the Listview to automatically scroll down to the newest item added to the log.
<ListView ItemsSource="{Binding Log}"
SelectedIndex="{Binding SelectedLine}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Row="1">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
MVVM and Attached Behaviors go hand in hand. You can use attached behavior to scroll to new item after the ObservableCollection.Add() takes place...
This article is a good example that tries to bring a tree view item into scroll view while sticking to MVVM.

Setting event handlers inside a Setter.Value structure

I have a ListView and i'd like to set up a context menu which i can open not only when right-clicking some text in some column but anywhere on the ListViewItem, to do so i thought i'd just set my ContextMenu using a style setter since i cannot directly access the ListViewItem.
Unfortunately when you try to do it like this it won't compile:
<Style TargetType="ListViewItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Header" Click="Handler"/>
...
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
Error 102 'Handler' is not valid.
'Click' is not an event on
'System.Windows.Controls.GridView'.
I figured that you can avoid this by using an EventSetter for the Click-event. But it is apparent that the code gets quite inflated from all the wrapping tags you need.
My question is if there is some workaround so you do not have to deal with EventSetters.
Edit: See this question for an explanation on why this error occurs.
You can put the ContextMenu in the ListView's Resources and then use it as a static resource, that way you won't have to use a Style for the MenuItem's
<ListView ...>
<ListView.Resources>
<ContextMenu x:Key="listViewContextMenu">
<MenuItem Header="Header" Click="MenuItem_Click"/>
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ContextMenu" Value="{StaticResource listViewContextMenu}"/>
</Style>
</ListView.ItemContainerStyle>
<!--...-->
</ListView>
You can just ListBoxItem.HorizontalContentAlignment to Stretch and then put the ContextMenu in your ListBox.ItemTemplate. Here's an example:
<Grid>
<Grid.Resources>
<PointCollection x:Key="sampleData">
<Point X="10" Y="20"/>
<Point X="30" Y="40"/>
</PointCollection>
</Grid.Resources>
<ListBox Width="100" ItemsSource="{StaticResource sampleData}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Red">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Test" Click="MenuItem_Click"/>
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>

WPF UserControl exposing inner listitems

I'm trying to write a UserControl to display a list of items where each of these items is a title and a group of checkboxes. This whole will represent a form of data where the person filling it in is answering a list of questions with a 1 to 4 value. This all works and binds nicely to the window's ViewModel.
But I've currently got the answers hardcoded in the UserControl as follows:
<ListBox
ItemsPanel="{StaticResource HorizontalScores}"
Style="{StaticResource styleOuterListBox}"
ItemContainerStyle="{StaticResource styleOuterListBoxItem}">
<ListBoxItem>Never</ListBoxItem>
<ListBoxItem>Sometimes</ListBoxItem>
<ListBoxItem>Often</ListBoxItem>
<ListBoxItem>Always</ListBoxItem>
</ListBox>
I would like to set these from the window's XAML or from the ViewModel as they will be different for other forms but can't see the correct incantation. How do I remove the ListBoxItems from the UserControl and use databinding instead?
BigEdit ...
Ok, this is the actual user control (it looks hideous but that's not the point):
<UserControl x:Class="BussPerry.Scorer" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:BussPerry.ViewModel" xmlns:local="clr-namespace:BussPerry">
<UserControl.Resources>
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="Gray" />
<SolidColorBrush x:Key="SelectedForegroundBrush" Color="Red" />
<ItemsPanelTemplate x:Key="HorizontalScores">
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<Style x:Key="styleListBox" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<ItemsPresenter Margin="2" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="styleListBoxItem" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox Name="CheckBox" Padding="1" Width="60"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter HorizontalAlignment="Center"/>
</CheckBox>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="CheckBox" Property="Background" Value="{StaticResource SelectedBackgroundBrush}" />
<Setter TargetName="CheckBox" Property="Foreground" Value="{StaticResource SelectedForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ListBox ItemsPanel="{StaticResource HorizontalScores}" Style="{StaticResource styleListBox}"
ItemContainerStyle="{StaticResource styleListBoxItem}" SelectedIndex="{Binding Path=Score}">
<ListBoxItem>Never</ListBoxItem>
<ListBoxItem>Sometimes</ListBoxItem>
<ListBoxItem>Often</ListBoxItem>
<ListBoxItem>Always</ListBoxItem>
</ListBox>
</UserControl>
And it's being called as follows:
<ListView
Name="listviewScores"
ItemsSource="{Binding Path=Scores}"
Margin="5"
BorderThickness="0"
Background="Transparent"
Focusable="False"
Grid.Row="3">
<ListView.View>
<GridView
ColumnHeaderContainerStyle="{StaticResource styleHiddenHeader}">
<GridView.Columns>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Path=Index}"
HorizontalAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn
DisplayMemberBinding="{Binding Path=Title}" />
<GridViewColumn >
<GridViewColumn.CellTemplate>
<DataTemplate>
<local:Scorer >
</local:Scorer>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
What I want to do is to move the Never/Sometimes/Often/Always listboxitems from being hard coded in the user control to be databound.
(Suggestions of "you don't want to do it like that" are also welcome!)
(one year later...)
I think your question is similar to mine. I have come up with a technique to expose the ItemsSource of an inner control on a UserControl. The link to my question is here:
Exposing inner Control properties for binding in WPF
I know that my solution works. What I don't know is if it violates some sacred 'best practices' out there in WPF. It 'feels' right though.
Do you want to bind a collection to a listbox?
It's pretty simple...
<ListBox ItemsSource="{Binding Answers}" />
where Answers is your collection exposed in your ViewModel.
If you're having trouble creating a custom control that exposes an ItemsSource, then you just need to inherit from ItemsControl instead of just UserControl.
EDIT:
Some assumptions:
the DataContext of the ListBox, custom control, or higher parent element is set to your
ViewModel.
the ViewModel has a
property called "Answers".
the Answers property implements
IEnumerable<>.

Resources