Applying a ControlTemplate Trigger to a CheckBox in a ListView - wpf

I am trying to override the appearance of a CheckBox in a ListView. The CheckBox is bound to a nullable bool (bool?).
The style does not seem to be applied at all. What am I doing wrong here?
<ListView Grid.Row="1" Margin="10" VerticalAlignment="Top" ItemsSource="{Binding Prerequisites}" d:DataContext="{d:DesignInstance Type=viewModels:MockPrerequisiteViewModel, IsDesignTimeCreatable=True}">
<ListView.Resources>
<Style x:Key="StyleCustomCheckBox" TargetType="{x:Type CheckBox}">
<Setter Property="FontSize" Value="14" />
<Setter Property="Margin" Value="10,0,0,0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Path x:Name="MyIcon" Width="18" Height="18" Stretch="Fill" Fill="#FF00FF00"
Data="M-150.204,626.126C-152.317,626.126 -154.429,626.126 -156.541,626.126 -167.642,633.42 -180.629,646.047 -189.668,657.238 -190.916,658.782 -192.945,662.362 -193.701,662.422 -194.041,662.448 -198.024,659.719 -198.614,659.297 -202.818,656.279 -205.779,653.709 -209.257,650.899 -211.248,652.172 -212.879,653.805 -214.153,655.797 -206.627,665.074 -200.283,675.534 -193.124,685.18 -181.491,665.11 -168.473,644.683 -152.796,629.006 -151.735,627.946 -149.817,626.933 -150.204,626.126z"/>
<ContentPresenter VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="MyIcon" Property="Data" Value="F1M-1774.05,-6263.81L-1787.51,-6277.27 -1773.22,-6291.56C-1769.23,-6295.55 -1769.24,-6302.03 -1773.21,-6306.01 -1777.19,-6309.98 -1783.67,-6309.99 -1787.66,-6305.99L-1801.95,-6291.71 -1816.79,-6306.55C-1820.79,-6310.55 -1827.26,-6310.54 -1831.24,-6306.55 -1835.22,-6302.58 -1835.23,-6296.11 -1831.24,-6292.12L-1816.39,-6277.27 -1830.4,-6263.25C-1834.4,-6259.26 -1834.38,-6252.8 -1830.4,-6248.82 -1826.42,-6244.84 -1819.96,-6244.82 -1815.96,-6248.82L-1801.95,-6262.83 -1788.49,-6249.37C-1784.5,-6245.38 -1778.03,-6245.39 -1774.06,-6249.37 -1770.07,-6253.35 -1770.06,-6259.82 -1774.05,-6263.81" />
<Setter TargetName="MyIcon" Property="Fill" Value="#FFFF0000" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" Header="Machine" DisplayMemberBinding="{Binding Context.Hostname}"/>
<GridViewColumn Width="Auto" Header="Prerequisite" DisplayMemberBinding="{Binding Label}"/>
<GridViewColumn Width="Auto" Header="Status" DisplayMemberBinding="{Binding Status}"/>
<GridViewColumn Width="Auto" Header="Result" DisplayMemberBinding="{Binding Result}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Result}" IsThreeState="True" IsEnabled="False" Style="{StaticResource StyleCustomCheckBox}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

You need to remove the DisplayMemberBinding="{Binding Result}" from the GridViewColumn.
As you are binding the checkbox to result already, and when you set DisplayMemberBinding property, GridView automatically uses a string representation.
<GridViewColumn Width="Auto" Header="Result">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Result}" IsThreeState="True" IsEnabled="False" Style="{StaticResource StyleCustomCheckBox}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Related

Datatrigger to change a ListView ItemsSource not firing

I am trying to change the ItemsSource Property of a ListView, depending on if a collection contains elements or is null.
The ListView currently looks like this:
<Control.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="GridFiles_MouseDoubleClick" />
</Style>
<CollectionViewSource x:Key="DirectoryFiles" Source="{Binding Path=CurrentDirectory.Files}"/>
<CollectionViewSource x:Key="DirectorySubDirs" Source="{Binding Path=CurrentDirectory.SubDirectories}"/>
<CompositeCollection x:Key="CombinedCollection">
<CollectionContainer Collection="{Binding Source={StaticResource DirectorySubDirs}}" />
<CollectionContainer Collection="{Binding Source={StaticResource DirectoryFiles}}" />
</CompositeCollection>
</Control.Resources>
<Grid>
<ListView x:Name="gridFiles" Grid.Row="2" Grid.Column="1" Margin="2"
SelectedItem="{Binding Path=SelectedFileGridItem}"
ItemsSource="{StaticResource CombinedCollection}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Extension}" />
<GridViewColumn Header="Checksum" DisplayMemberBinding="{Binding Checksum}" />
</GridView>
</ListView.View>
</ListView>
<Grid>
In my viewmodel there is an IEnumerable<FileNodeViewModel> property called SearchResults which is filled with results after performing a search, otherwise its null.
If I manually set the ItemsSource of the ListView property to {Binding SearchResults} the search results are displayed as intended, so I am sure that the data is correctly formated and available to the Listview.
In this thread I found a similar question and appended the xaml code:
<Control.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="GridFiles_MouseDoubleClick" />
</Style>
<Style TargetType="ListView">
<Style.Triggers>
<DataTrigger Binding="{Binding SearchResults}" Value="All">
<Setter Property="ItemsSource" Value="{Binding SearchResults}" />
</DataTrigger>
</Style.Triggers>
</Style>
<CollectionViewSource x:Key="DirectoryFiles" Source="{Binding Path=CurrentDirectory.Files}"/>
<CollectionViewSource x:Key="DirectorySubDirs" Source="{Binding Path=CurrentDirectory.SubDirectories}"/>
<CompositeCollection x:Key="CombinedCollection">
<CollectionContainer Collection="{Binding Source={StaticResource DirectorySubDirs}}" />
<CollectionContainer Collection="{Binding Source={StaticResource DirectoryFiles}}" />
</CompositeCollection>
</Control.Resources>
<Grid>
<ListView x:Name="gridFiles" Grid.Row="2" Grid.Column="1" Margin="2"
SelectedItem="{Binding Path=SelectedFileGridItem}"
ItemsSource="{StaticResource CombinedCollection}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Extension}" />
<GridViewColumn Header="Checksum" DisplayMemberBinding="{Binding Checksum}" />
</GridView>
</ListView.View>
</ListView>
<Grid>
However, nothing changes. No matter if the SearchResults property is null or not, the ListView always displays the combined collection. I also tried to create a boolan property to indicate if the second collection should be displayed or not, with the same result:
<Style TargetType="ListView">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowSearchResults}" Value="True">
<Setter Property="ItemsSource" Value="{Binding SearchResults}" />
</DataTrigger>
</Style.Triggers>
</Style>
Does anybody know how what I should do to correctly change the ItemsSource property?
So you want to bind to SearchResults only if it's not null, or else you wan to bind to CombinedCollection? You could then use a DataTrigger with a value of {x:Null}:
<ListView x:Name="gridFiles" Grid.Row="2" Grid.Column="1" Margin="2"
SelectedItem="{Binding Path=SelectedFileGridItem}">
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemsSource" Value="{Binding SearchResults}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SearchResults}" Value="{x:Null}">
<Setter Property="ItemsSource" Value="{StaticResource CombinedCollection}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Extension}" />
<GridViewColumn Header="Checksum" DisplayMemberBinding="{Binding Checksum}" />
</GridView>
</ListView.View>
</ListView>
Make sure that you only set the ItemsSource property in the Style Setter.
So, after some trial and error I found the issue. Everything was pretty much set up correctly, but I was not aware that styles can only append properties to an element, not replace existing ones.
To test this, I created a new ListView:
<ListView Grid.Row="2" Grid.Column="1" Margin="2"
ItemsSource="{StaticResource CombinedCollection}"
Foreground="Blue">
<ListView.Style>
<Style TargetType="ListView">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowSearchResults}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
The data was displayed correctly, with blue foreground color. If I changed ShowSearchResults to True, nothing happened, the color still was blue. So I removed the foreground property:
<ListView Grid.Row="2" Grid.Column="1" Margin="2"
ItemsSource="{StaticResource CombinedCollection}">
I retried the test, and suddenly the ListView items would turn red. Knowing this, I removed the explicit data source from the ListView definition and used two data triggers instead.
For reference, here is the working markup. Thanks a lot to mm8 for providing some great input about the formatting!
<ListView Grid.Row="2" Grid.Column="1" x:Name="gridFiles" Margin="2"
SelectedItem="{Binding Path=SelectedFileGridItem}">
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemsSource" Value="{StaticResource CombinedCollection}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ShowSearchResults}" Value="True">
<Setter Property="ItemsSource" Value="{Binding SearchResults}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Extension}" />
<GridViewColumn Header="Checksum" DisplayMemberBinding="{Binding Checksum}" />
</GridView>
</ListView.View>
</ListView>

Cannot select ListView item when clicking on some parts

I create a ListView having a GridView like below:
<ListView ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder}">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="SteelBlue" BorderThickness="1" x:Name="Border" Padding="10">
<GridViewRowPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightBlue"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.Resources>
<ListView.View>
<GridView d:DataContext="{d:DesignInstance entities:ExecReport}">
<GridViewColumn Header="Symbol" DisplayMemberBinding="{Binding Instrument.Symbol}" />
<GridViewColumn Header="Side" DisplayMemberBinding="{Binding Side}" />
<GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" />
<GridViewColumn Header="Total Size" DisplayMemberBinding="{Binding Qty}" />
<GridViewColumn Header="Open Size" DisplayMemberBinding="{Binding OpenQty}" />
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding State}" />
<GridViewColumn Header="Validity" DisplayMemberBinding="{Binding Validity}" />
<GridViewColumn Header="Order Seq" DisplayMemberBinding="{Binding OrderSeq}" />
</GridView>
</ListView.View>
</ListView>
I can select items by clicking on them in most places. But when I click on some places they do not get selected. The backing field is not set either. This problem does not happen if I remove the ListViewItem Style.
The below image points out the sort of place where an item does not get selected.
How do I fix this?
Set the Background property of your Border(named "Border") to Transparent. Since controls without background will be considered as hollow and hit test may not be possible. More information on HitTesting.

Unable to apply trigger on TextBlock within ListView/GridView

Unable apply the Style on GridViewDataTemplate field.
I just need to change the Forground of the TextBlock based on its contect.
If pull this textblock outside the list view it works perfectly. But inside the ListView it's not working.
Please let me how to fix this issue.
<ListView Margin="5,15,0,5"
ItemsSource="{Binding}" Background="Transparent" Foreground="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderThickness="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
<DataTemplate x:Key="DataColumnStyle" DataType="GridViewColumn.CellTemplate">
<StackPanel>
<TextBlock Text="{Binding}" Foreground="White" FontSize="16" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="StatusColumnStyle" DataType="GridViewColumn.CellTemplate">
<StackPanel>
<TextBlock Text="{Binding}" FontSize="16" TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="In Progress">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="Text" Value="Complete">
<Setter Property="Foreground" Value="Green"/>
</Trigger>
<Trigger Property="Text" Value="Failed">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Activity}" CellTemplate="{StaticResource DataColumnStyle}" x:Name="ActivityColumn"/>
<GridViewColumn DisplayMemberBinding="{Binding Status}" CellTemplate="{StaticResource StatusColumnStyle}" Width="100" x:Name="StatusColumn"/>
</GridView>
</ListView.View>
</ListView>
I believe if you set the DisplayMemberBinding property, the CellTemplate property will be ignored; DisplayMemberBinding is fine if you're only looking to specify text, rather than a template.
So you'd need to change your code to something along these lines:
Altering the Text binding within the `DataTemplate':
Text="{Binding}" => Text="{Binding Activity}"
Removing the DisplayMemberBinding from the ListView:
<GridViewColumn DisplayMemberBinding="{Binding Activity}" CellTemplate="{StaticResource DataColumnStyle}" x:Name="ActivityColumn"/> => <GridViewColumn CellTemplate="{StaticResource DataColumnStyle}" x:Name="ActivityColumn"/>
Roughly:
<!-- Your Code -->
<GridView>
<GridViewColumn CellTemplate="{StaticResource DataColumnStyle}" x:Name="ActivityColumn"/>
<GridViewColumn CellTemplate="{StaticResource StatusColumnStyle}" Width="100" x:Name="StatusColumn"/>
</GridView>
<!-- Your Code -->
If you have a look at the MSDN info for DisplayMemberInfo (also CellTemplate / CellTemplateSelector), you can see that there is an order of precedence:
The following properties are all used to define the content and style
of a column cell, and are listed here in their order of precedence,
from highest to lowest:
DisplayMemberBinding
CellTemplate
CellTemplateSelector

How can I change the fontsize of a GridviewColumnHeader?

I included everything below since it's not that much. I wasn't sure if something I put elsewhere was causing my setter to not work properly.
<ListView x:Name="lvReports"
SelectionMode="Single"
ItemsSource="{Binding reportsCollection}" Height="432" ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<ListView.Resources>
<Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="TextElement.FontSize" Value="30pt"/>
<Setter Property="Width" Value="800"/>
</Style>
</ListView.Resources>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel>
<DockPanel Height="30" VerticalAlignment="Bottom">
<Image Source="\Images\ProductivityByEmployeesReport.png"/>
<TextBlock FontWeight="Bold" FontSize="18pt" Text="{Binding Path=Name}"/>
</DockPanel>
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn x:Name="colName" HeaderContainerStyle="{StaticResource myHeaderStyle}" Header="Reports">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<Rectangle Width="18"/>
<CheckBox>
<TextBlock Text="{Binding displayName}"/>
</CheckBox>
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Thanks in advance.
Add this to <ListView.Resources>
<Style TargetType="GridViewColumnHeader" x:Key="ColumnHeaderLarge">
<Setter Property="FontSize" Value="14"/>
</Style>
To set Fontsize to 14, use the style as:
<GridView ColumnHeaderContainerStyle="{StaticResource ColumnHeaderLarge}">

How do I group items in a WPF ListView

I have a ListView that I want to group results into, however the examples I am finding are not working. How can I group my results?
I want to group on the Status property of a custom object.
This is what I have:
<ListView IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Background="Transparent" SelectionChanged="ListView_SelectionChanged"
Name="lstShelvedOrders">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="15"
Text="{Binding Path=Status}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Width" Value="Auto" />
<Setter Property="FontSize" Value="10.4" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=Number}" Header="Shelve ID" />
<GridViewColumn DisplayMemberBinding="{Binding Path=Customer}" Header="Customer" />
<GridViewColumn DisplayMemberBinding="{Binding Path=PurchaseOrderNo}" Header="PO Number" />
<GridViewColumn DisplayMemberBinding="{Binding Path=SubmittedBy}" Header="Shelved By" />
<GridViewColumn DisplayMemberBinding="{Binding Path=OrderDate, StringFormat=MMM dd\, yyyy}" Header="Date" />
<GridViewColumn DisplayMemberBinding="{Binding Path=CustomerTerms.Description}" Header="Order Terms" />
<GridViewColumn DisplayMemberBinding="{Binding Path=ShippingMethod.Description}" Header="Shipping" />
<GridViewColumn DisplayMemberBinding="{Binding Path=TotalPrice, StringFormat=c}" Header="Order Total" />
</GridView>
</ListView.View>
</ListView>
And this is the code that I have:
void ShelvedOrderList_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
AddGrouping();
}
private void AddGrouping()
{
if ( lstShelvedOrders.ItemsSource == null)
{
return;
}
CollectionView myView = (CollectionView)CollectionViewSource.GetDefaultView(lstShelvedOrders.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Status");
myView.GroupDescriptions.Add(groupDescription);
}
I notice one thing right away - the GroupStyle.HeaderTemplate will be applied to a CollectionViewGroup, so your DataTemplate should probably look like this:
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontSize="15" FontWeight="Bold" Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
CollectionViewGroup.Name will be assigned the value of Status for that group.
I think this can also be better, using a GroupStyle with a new ControlTemplate:
<ListView ItemsSource="{Binding Path=ContactsView}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template" Value="{StaticResource ContactsGroupItemTemplate}" />
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
...
<ControlTemplate TargetType="{x:Type GroupItem}" x:Key="ContactsGroupItemTemplate">
<Expander IsExpanded="False">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock FontWeight="Bold" Text=" Items"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>

Resources