WPF XAML - How to bind a DataTrigger to a ComboBox value - wpf

I currently have a ListView that takes in an item and displays a ComboBox and a Button.
I would like to dynamically show or hide the button based on the value of the ComboBox being equal to "BlahBlahBlah".
Currently the inside of my <GridView> looks like this:
<GridViewColumn Header="Property" Width="160">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="PropertyComboBox"
ItemsSource="{Binding Path=ArisingPropertyList, Mode=TwoWay}"
SelectedValue="{Binding ArisingProperty.PropertyName,
UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="PropertyName" Width="140" >
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="30" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="...">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedValue.PropertyName,
ElementName=PropertyComboBox}" Value="HideButton">
<Setter Property="Visibility" Value="BlahBlahBlah" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Can someone point out where I'm going wrong? All I'm getting is:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=PropertyComboBox'. BindingExpression:Path=SelectedValue.PropertyName; DataItem=null; target element is 'Button'; target property is 'NoTarget' (type 'Object')

You have already bounded SelectedValue property to your source class, so bind directly to that property instead of ComboBox. Issue is ComboBox and Button lies in different Visual trees so can't be bind using ElementName.
<DataTrigger Binding="{Binding Path=ArisingProperty.PropertyName}"
Value="HideButton">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>

Related

How to apply a KeyBinding in a CellTemplate of a DataGridTemplateColumn?

I've got a bit of a curly problem that I cannot find any complete and definitive information on...
My requirement is very simple: I want to apply a KeyBinding to a DataGridTemplateColumn. The purpose of this is to execute a command when the Delete key is hit for that column - a different command is executed if Delete is hit anywhere else on the grid. Both Delete commands exist in the VM of the grid.
While I could have a single keybinding defined at the grid level and then in the command test which column the active cell belongs to, this is a messy solution because:
that doesn't scale well when the grid is used on several different pages
the column in question is defined as a resource in a ResourceDictionary
Besides, I've seen nothing that indicates that this isn't possible... yet it eludes me.
I believe my problem is because focus is not automatically applied to the CellTemplate when that cell becomes the grid's current cell. There is a focus rectangle on the cell, but no focus events get triggered inside the cell (I've checked this). While readonly controls like TextBlocks can receive focus, if they don't then the keybinding cannot be triggered. The commands for the keybinding are correctly bound, so binding is not an issue.
The grid is defined like this:
<DataGrid x:Name="MyGrid"
ItemsSource="{Binding Path=FilteredSortedData}"
>
<DataGrid.Columns>
<StaticResource ResourceKey="StatusColumn" />
<StaticResource ResourceKey="MySpecialDeleteColumn" />
<StaticResource ResourceKey="...etc..." />
</DataGrid.Columns>
</DataGrid>
Then the column:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="MyNamespace.GridColumns"
>
<FrameworkElement x:Key="GridColProxy" Name="GridColProxy" DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=OneWay}" />
<DataGridTemplateColumn x:Key="MySpecialDeleteColumn"
x:Shared="False"
Header="My Special Delete Column"
IsReadOnly="False"
>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel >
<!-- keybinding doesn't work at this level -->
<StackPanel.Style >
<Style TargetType="StackPanel">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Style>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding SomeProperty}"
>
<TextBlock.Style>
<Style TargetType="TextBlock" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MySpecialFlag, FallbackValue=false}" Value="True">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Image Source="{Binding ...}"
Grid.Column="1"
/>
</Grid>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}" >
<Style.Triggers>
<!-- placing the keybinding here to apply to the grid cell doesn't have any effect... -->
<DataTrigger Binding="{Binding Path=MySpecialFlag, FallbackValue=false}" Value="True">
<Setter Property="localBehaviours:InputBindingsBehaviour.AttachedKeyBindings">
<Setter.Value>
<localBehaviours:KeyBindingObservable >
<KeyBinding Key="Delete" Command="{Binding Path=DataContext.DeleteItemsCommand, Source={StaticResource GridColProxy}, Mode=OneWay, NotifyOnTargetUpdated=True}" />
</localBehaviours:KeyBindingObservable>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</ResourceDictionary>
Notes:
ignore the localBehaviours:KeyBindingObservable, it is simply an attached property to assist with intercepting and inspecting keybindings before assigning them to the InputBindings property of the attached control. This works great, there is no issue here.
I have shown every place I have tried the keybindings, none of them have worked (been triggered). The DataTrigger bindings work correctly, so just ignore those.
CellEditingTemplate has been omitted for brevity
So the question I have is:
how can I force the CellTemplate items to receive (keyboard) focus, preferably with XAML? Or is there a way to apply the keybinding at cell level so that it works regardless of the template being shown?
If a XAML solution is not possible then a small amount of event triggered (PreviewKeyDown, etc) code in the code behind of the ResourceDictionary could be used.

Dynamically changing WPF ListView ItemsPanel & ItemsContainerStyle

I have a ListView in XAML which displays its source collection in a GridView (with columns) fashion.
However I intend to use the same ListView to display the source collection may be in a grid of images, or some card view.
I want the ListView to change itself based on a ComboBox selection. So say for ComboBox value 1 ListView should display a GridView, for value 2 ListView should display card view.
Currently my ListView specifies a GridView set as its View property:
<ListView ItemsSource="{Binding PersonList}" Width="450" HorizontalAlignment="Right" IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="LastName" DisplayMemberBinding="{Binding LastName}" />
<GridViewColumn Header="Ip Address" DisplayMemberBinding="{Binding Path=IpAddress}" />
</GridView>
</ListView.View>
</ListView>
I would like to know how can I change the ListView to display different views based on ComboBox triggers.
ListView View is a DependencyProperty of type ViewBase. So, what you can do is create your own custom views and can set it via DataTrigger on combobox selected item.
Microsoft already has sample available for it online which you can download from here.
OR
May be you can define two separate ListView's in resources as two seperate DataTemplates.
<Window.Resources>
<DataTemplate x:Key="GridViewTemplate">
<ListView/> <!-- GridView -->
</DataTemplate>
<DataTemplate x:Key="CardViewTemplate">
<ListView/> <!-- CardView -->
</DataTemplate>
</Window.Resources>
and have one ContentControl in place and you can swap its Content based on selected value in combobox.
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content"
Value="{StaticResource GridViewTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue}" Value="Value2">
<Setter Property="Content"
Value="{StaticResource CardViewTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Property 'Content' was not found in type 'FrameworkElement' using DataTrigger

I'm new to WPF.
I'm trying to display a ToolTip on a ListBox item only when the grouping is equal to "Search Results".
I'm getting an error that says :
"Property 'Content' was not found in type 'FrameworkElement'."
Can anyone tell me what's wrong with the code below?
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=code}">
<TextBlock.ToolTip>
<ToolTip>
<ToolTip.Triggers>
<DataTrigger Binding="{Binding Path=grouping}" Value="Search Results">
<Setter Property="Content" Value="{Binding Path=grouping}"/>
</DataTrigger>
</ToolTip.Triggers>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It works ok without the trigger like the code below so it confuses me why it says that the property was not found.
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=code}">
<TextBlock.ToolTip>
<ToolTip Content="{Binding Path=grouping}" />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The Triggers collection of FrameworkElement is only for event triggers, not for DataTriggers or PropertyTriggers. Define a style for the ToolTip which contains the DataTrigger:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=code}">
<TextBlock.ToolTip>
<ToolTip>
<Tooltip.Style>
<Style TargetType="ToolTip">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=grouping}" Value="Search Results">
<Setter Property="Content" Value="{Binding Path=grouping}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ToolTip.Style>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
MSDN says:
Note that the collection of triggers established on an element only
supports EventTrigger, not property triggers (Trigger). If you require
property triggers, you must place these within a style or template and
then assign that style or template to the element either directly
through the Style property, or indirectly through an implicit style
reference.
That doesn't describe your problem directly, but read it as: Set Triggers in styles.
This article gets more specific: Dr. WPF Blog
There is also a Triggers collection on FrameworkElement, but it can
only contain event triggers… not property or data triggers.

Controls with TemplateSelector property

Now I have ListView and in one column has:
<GridViewColumn CellTemplateSelector="{StaticResource messagerEditorTemplateSelector}"/>
And everything is fine: cell is filled with content based on item. But now I want to place in this cell 2 controls: for one template must be selected based on binding and other is control with name, say TimeRangeView. But I cannot understand how it can be implemented? So I must have code like:
<GridViewColumn>
<DataTemplate>
<StackPanel>
<SomeControlWhichSupportTemplateSelector ... />
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn>`
Which control should I use for template? I've found only listbox but it must be bound to collection. Of course, I could bind like:
<ListBox ItemsSource="{Binding Converter=ItemToCollectionConverter}" />
but it doesn't look elegant. May be there is another way to do it?
You can use a ContentControl and set its ContentTemplateSelector property:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ContentControl ContentTemplateSelector="{StaticResource messagerEditorTemplateSelector}" />
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Note that for Binding to work within your ContentControl, you will have to set the Content property to whichever object is used in the Bindings of the DataTemplate returned by your selector.
So that's for option 1.
You can also just use DataTriggers:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ContentControl Content="{Binding MyBoundObject}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MyBoundObject.ABooleanProperty}" Value="True">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource myFirstTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=MyBoundObject.ABooleanProperty}" Value="False">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource mySecondTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Which is what i do and it works like a charm =)

How to have style applied for wpf datagrid row

I am Having requirement on WPF Datagrid row,Whenever user selects a DatagridRow,corresponding datagrid Cells borders should have thickness of 1 or 2 .
or Provide margin for textboxes/Textblocks within in DatagridCell.
With regards,
Mahens
I'm not sure if this is exactly what you're looking for, but here's an example of modifying the default listboxitem style for a gridview (NOTE the top level Grid is the top-level element in a xaml file):
<Grid>
<Grid.Resources>
<Style x:Key="itemstyle" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="BorderBrush" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ListView Name="grid" ItemContainerStyle="{StaticResource itemstyle}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}"/>
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Path=Age}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
I just created a generic Person type with a Name string property and an int Age property. I added a few of these to a list and set the ItemsSource of grid to the List.

Resources