ContentControl two way binding within DataTemplate not working? - wpf

I've setup a reusable datatemplate "DataGridCheckBoxEdit" for a datagrid column. Binding to it one way works like a charm through ContentControl. Binding directly works two way correctly. However, binding two way within that DataTemplate, from a ContentControl just won't work.
Here are the snippets:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=IsMadeAvailable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ContentTemplate="{StaticResource DataGridCheckBoxEdit}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
and the reusable template:
<DataTemplate x:Key="DataGridCheckBoxEdit">
<CheckBox Name="CheckBoxControl" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}, Path=DataContext.Content, Mode=TwoWay, BindsDirectlyToSource=True, UpdateSourceTrigger=PropertyChanged}" Margin="8,4,2,2" />
<DataTemplate.Triggers>
<Trigger SourceName="CheckBoxControl" Property="IsVisible" Value="True">
<Setter TargetName="CheckBoxControl" Property="FocusManager.FocusedElement" Value="{Binding ElementName=CheckBoxControl}"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
As I said, one way binding works like a charm...but getting the data back to the property doesn't.
Of course, putting it without being reusable:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="GasIsAvailableCheckBox" IsChecked="{Binding Path=IsMadeAvailable, UpdateSourceTrigger=PropertyChanged}" Margin="8,4,2,2" />
<DataTemplate.Triggers>
<Trigger SourceName="GasIsAvailableCheckBox" Property="IsVisible" Value="True">
<Setter TargetName="GasIsAvailableCheckBox" Property="FocusManager.FocusedElement" Value="{Binding ElementName=GasIsAvailableCheckBox}"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
also works great, and works two-way.
What am I doing wrong?
Thanks!
Vladan

Your binding is just broken (see the output window of Visual Studio for the respective errors), you do not want to bind to DataContext.Content but just Content, the DataContext would be the object in that row instead of the ContentControl itself.
Change that in the binding path of the reusable template and it will work. You also set a lot of properties to values they already have by default, this would be the minimal version:
{Binding Content, RelativeSource={RelativeSource AncestorType=ContentControl}}

Related

Triggers override binding

Why is it that the binding Marked stops working when the triggers are used? Is there some way to fix this?
The multi selection ListBox has ListBoxItems with CheckBoxes, and the checkbox in an item is ticked when the corresponding item is selected, using mouse or keyboard or touch or whatever. The point with this demo is to not tick the checkboxes directly, but just multiselect the items and see the checkboxes get ticked as a consequence of that. This synchronization in the XAML works well, but since the Marked binding isn't working, the model won't get updated.
If I remove the triggers, then the binding Marked starts working. I know, because then the model gets updated when the checkboxes are ticked.
If I attach event handlers for Checked and Unchecked to the CheckBox, they fire even when the triggers are there.
<ListBox ItemsSource="{Binding Lines}" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox x:Name="cb"
Content="{Binding DisplayText}"
IsChecked="{Binding Marked, Mode=TwoWay}">
</CheckBox>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="true">
<Setter TargetName="cb" Property="IsChecked" Value="true"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="false">
<Setter TargetName="cb" Property="IsChecked" Value="false"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EDIT:
Although the accepted solution below is good XAML, it crashes with Elmish.WPF, which I'm using. That's another issue, and will be solved through other forums.
The DataTriggers will obviously override the Binding, since the ListBoxItem's IsSelected property is always either true or false.
Replace the DataTriggers by an ItemContainerStyle:
<ListBox ItemsSource="{Binding Lines}" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding DisplayText}"
IsChecked="{Binding Marked}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Marked}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Switch ItemsSource dynamically in XAML

I'm trying to switch the itemssource of a treeview depending on a value of a property in my viewmodel. I've tried the code below and the trigger doesnt seem to be firing, can someone tell me where I went wrong?
<Window.Resources>
<Style x:Key="TreeViewItemSource" TargetType="TreeView">
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentReportRequested, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="TollFree">
<Setter Property="ItemsSource" Value="{Binding InsertTFSQueryList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<TreeView ItemsSource="{Binding Source={StaticResource TreeViewItemSource}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource DebugConverter}}" />
Set the ItemsSource in Style otherwise local set value will always holds priority.
Read more about it here - Dependency Property Value Precedence Order.
<Style x:Key="TreeViewItemSource" TargetType="TreeView">
<!-- Set ItemsSource here but you need to separate Style out of it. -->
<Setter Property="ItemsSource"
Value="{Binding Source={StaticResource TreeViewItemSource},
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource DebugConverter}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentReportRequested, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Value="TollFree">
<Setter Property="ItemsSource" Value="{Binding InsertTFSQueryList,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Most importantly you have set ItemsSource to the Style and somewhere in converter, you are converting it to actual value. This is the strangest thing I ever looked. How style is convertible to ItemsSource. Refactor the logic and separate out both so that above XAML works for you.
To solve my issue, I used good-old oop...polymorphism.
I used a polymorphic structure in my ViewModel. Now my ItemsSource is bound to one IEnumerable list which holds the base class...which i can equate to any of the derived types.

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.

WPF Highlight Item

I have a ViewModel that provides a collection of Items. There is also a ActiveItem propery. The Items collection may or may not contain ActiveItem.
What I want to do (in XAML) is display the items as a list and highlight any of the items that are equal to Active Item.
I have tried the following with no success:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="outerBorder" Background="Green">
<TextBlock Text="{Binding ItemId}" />
</Border>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding
Path=DataContext.Item.ItemId,
RelativeSource={RelativeSource TemplatedParent},
Mode=Default}"
Value="{Binding
Path=DataContext.ActiveItem.ItemId,
RelativeSource={RelativeSource AncestorType=Window},
Mode=Default}"
>
<Setter TargetName="outerBorder"
Property="Background" Value="Orange" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As you can see I attempted to use a DataTrigger to match the current item with the ActiveItem but it doesn't work. I think this is something to do with my trying to use a Binding in DataTrigger.Value - something I haven't seen any other examples of.
Any ideas how I might make this work?
Thanks,
Daniel
Since you're using MVVM, why not just have the view model expose a property telling the view whether it's active or not? That'll get that logic out of you view and into your VM.

Databinding and Triggers compatability in WPF

I have a problem. I made a DataTemplate for a TreeView and I need to set the initial value of the ToggleButton's IsChecked property depending on my model. But it turns out that setting this property using triggers/setters disables the databinding.
Is it so? If yes, give me a suggestion how it can be fixed?
<DataTemplate x:Key="CellTemplate_Name">
<DockPanel x:Name="dock">
<ToggleButton x:Name="Expander"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"> <--- Binding
...
<ToggleButton/>
...
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=ObjIsOpened, Converter={StaticResource DebugConverter}}" Value="true"> <--- Trigger
<Setter TargetName="Expander" Property="IsChecked" Value="true"></Setter>
</DataTrigger>
...
</DataTemplate.Triggers>
</DataTemplate>
Regards, Lerax.
First of all I suggest you read the excellent article by Josh Smith
Simplifying the WPF TreeView by Using the ViewModel Pattern
Based on that article, I would suggest defining a style for the TreeViewItem (using the ItemContainerStyle property of the TreeView) which binds its IsExpanded property to your model object's ObjIsOpened property. Then get rid of your trigger.
Example:
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded"
Value="{Binding ObjIsOpened, Converter={StaticResource DebugConverter}}"/>
</Style>
<DataTemplate x:Key="CellTemplate_Name">
<DockPanel x:Name="dock">
<ToggleButton x:Name="Expander"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource
AncestorType={x:Type TreeViewItem}}}"> <--- Binding
...
<ToggleButton/>
...
</DataTemplate>
I suspect they do not disable data binding, they just have higher priority. Instead of using binding and trigger at the same time, why don't you use one of them (either binding or trigger)? E.g. you could bind to model directly, and don't use trigger at all...

Resources