Change DataTrigger binding reference at runtime - c#

I've a ListView with this DataTrigger attached:
<Style x:Key="HideShowStyle" TargetType="{x:Type ListViewItem}">
<Style.Resources>
<localConverters:ShowHideConverter x:Key="ShowHideConverter" />
</Style.Resources>
<Style.Triggers>
<DataTrigger Value="true">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource showHideConverter}">
<Binding Path="EndingDate" />
<Binding Path="UserName" />
<Binding ElementName="SearchBox" Path="Text" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Value="false">
(...)
</DataTrigger>
</Style.Triggers>
</Style>
I use this in a hardcoded scenario to hide/show some ListItems in the ListView. The binding source is specified in the ElementName tag, and the TextBox referred is declared a few lines before.
Now, I've the necessity to change that Binding at runtime with another field incapsulated in a istantiated class. I've tried to use WPF class instead of XAML, but nothing, I don't have any clue how to obtain this.
I'm open to any advice! :)

var style = (Style)FindResource("HideShowStyle");
var trigger = (DataTrigger)style.Triggers[0];
var multibinding = (MultiBinding)trigger.Binding;
var binding1 = (Binding)multibinding.Bindings[0];
var binding2 = (Binding)multibinding.Bindings[1];
var binding3 = (Binding)multibinding.Bindings[2];
Change bindings at will.

Related

How to Pass multiple parameter in Multivalue Converter Over WPF DataTrigger

I'm having four int Property ProOne, ProTwo, ProThree and ProFour
I have to Implement the Boolean Logic ((ProOne == ProTwo) || (ProThree == ProFour)) in the Multivalue Converter namely VisibilityCheckConverter. Based on the Logic the Multivalue Converter VisibilityCheckConverter returns True or False.
Now I need to pass the four properties to the Converter over DataTrigger, Based on the Value, I have to change the Buttons Visibility to Visible
How does one write the a DataTrigger using Multivalue Converter with multiple parameters?
Sample Piece of XAML Code:
<ControlTemplate.Triggers>
<DataTrigger Property="{Binding , Converter={StaticResource VisibilityCheckConverter,ConverterParameter=ProOne ProTwo ProThree ProFour}}" Value="true">
<Setter TargetName="Button" Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
You can do something like this
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource VisibilityCheckConverter}">
<Binding Path="ProOne" />
<Binding Path="ProTwo" />
<Binding Path="ProThree" />
<Binding Path="ProFour" />
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="Button" Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>

Per-item converter instance in style of treeview item container

I have ItemContainerStyle set for TreeView and use MultiBinding with converter in it:
<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected">
<Setter.Value>
<MultiBinding Converter="{StaticResource SelectedCategoryConverter}" Mode="TwoWay">
<Binding Path="."/>
<Binding Path="CurrentCategoryId" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
I need SelectedCategoryConverter to be created for every unique item in the tree view, so I declared it with x:Shared="False" in window resources:
<local:SelectedCategoryConverter x:Shared="false" x:Key="SelectedCategoryConverter"/>
but it doesn't help: only one instance of converter is created when 2 or more items passed to TreeView through ItemsSource. I tried to write converter as MarkupExtension, but it didn't help too.
I think there is only one way.
1. Move ItemContainerStyle to the resource and marked it as nonshared:
<Application.Resources>
<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}" x:Shared="False">
<Setter Property="IsSelected">
<Setter.Value>
<MultiBinding Converter="{local:SelectedCategoryConverterCreator}" Mode="TwoWay">
<Binding Path="."/>
<Binding Path="CurrentCategoryId" RelativeSource="{RelativeSource AncestorType=UserControl}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="IsExpanded" Value="True" />
</Style>
</Application.Resources>
<TreeView ItemContainerStyle="{StaticResource TreeViewItemStyle}">
Create your own MarkupExtension which will create a new instance of SelectedCategoryConverter:
public class SelectedCategoryConverterCreatorExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new SelectedCategoryConverter();
}
}
It will create a new instance of SelectedCategoryConverter for each item. But remember that it is not very memory efficient.

WPF DataTrigger that accepts parameter?

I have the following style defined in my XAML to enable buttons only when something in the DataContext has changed (IsDirty = true) :
<!-- Style for Buttons to enable based on IsDirty value -->
<Style x:Key="EnableWhenDirtyButtonStyle" TargetType="{x:Type Button}"
BasedOn="{StaticResource ButtonStyle}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<!-- Enable button when something has changed -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}, Path=DataContext.IsDirty}" Value="True">
<Setter Property="Button.IsEnabled" Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
This works as long as there is only one DataContext within the UserControl. I now have the situation where I have 3 different DataViews so I have 3 different IsDirty values (i.e., CustomerTableIsDirty, OrderTableIsDirty, OrderDetailTableIsDirty). In this case I can create three new *DisableWhenDirtyButtonStyle in the UserControl like:
<Style x:Key="CustomerTableEnableWhenDirtyButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource ButtonStyle}">
<Setter Property="Button.IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CustomerTableIsDirty}" Value="true">
<Setter Property="Button.IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
Is there a way to create a DataTrigger such that the binding value can be passed into the style as a parameter?
Alternatively, is there a way to add conditions to a MultiDataTrigger when inheriting a style via 'BasedOn' which would already have a MultiDataTrigger defined. For instance:
<Style x:Key="CustomerTableEnableWhenDirtyButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource EnableWhenDirtyButtonStyle}">
<!-- Add the following to existing MultiDataTrigger in EnableWhenDirtyButtonStyle -->
<Condition Binding="{Binding Path=CustomerTableIsDirty}" Value="true" />
</Style>
I CANNOT use MultiBinding as this style is part of a base project which gets used by multiple other projects (as a DLL). The users of this DLL would not be able to update the style to include the necessary Binding Path.
Instead of using 3 different names, just use a single name IsDirty.

WPF DataGridTextColumn binding and style

here's the deal: styling the DataGridTextColumn's textblock for a datagrid.
I need to format the textblock by it's value, by comparing it from another binded value.
What i want to achieve is something like this:
<Style x:Key="ExpeditionerCellStyle" BasedOn="{StaticResource RightAlignStyle}" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=BEST}" Value="{RelativeSource Mode=Self}">
<Setter Property="Background" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
Which is not possible, because the Value of Datatrigger cannot be a relative source.
So i tried with multibinding
<Style x:Key="ExpeditionerCellStyle" BasedOn="{StaticResource RightAlignStyle}" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource IsValueEqualParameterConverter}">
<Binding Path="BEST" />
<Binding RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</DataTrigger.Binding>
</DataTrigger>
</Style.Triggers>
</Style>
with no luck: textblock Text property is empty, probably because the style is applied before the actual binding is performed.
I have no more ideas.
Please help me!
I'm not sure if this will work, but I noticed that you forgot to specify the Binding.Path in your MultiConverter example... try this:
<Style x:Key="ExpeditionerCellStyle" BasedOn="{StaticResource RightAlignStyle}" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource IsValueEqualParameterConverter}">
<Binding Path="BEST" />
<Binding Path="Text" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</DataTrigger.Binding>
</DataTrigger>
</Style.Triggers>
</Style>
If you only want to change the style of the textblock based on the content of the textblock why not just add a bidning to the textblock background and use a converter to check the content of the text?
In other words bind both the text and the background to the same source and use a converter with the background and in the converter match the content and return the correct background style.
If you need something else leave a comment so we better know what the problem is.
I ended modifying a bit my code.
Instead of relying on the value of the cell, in the field "BEST" i passed the name of the column.
Then using the converter I check
Current textblock (as suggested by #Sheridan) -> DatagridCell -> Column's header
if it match, i change the style on this textblock.
Thanks everyone!!!

Value Converter not getting called when data changes

I'm working on a user control right now in which I have a path as part of the control. There are 3 possible paths I might want to display based on the values of certain data. To determine which Path I want to use, I have a value converter that takes in the data and returns a number to represent which of the paths I should use.
My first thought was to just use the property changed callback from the two dependency properties I am getting data from, but those callbacks must be static and the XAML code is always non-static.
My second attempt is to now use datatriggers with the value converter described above. Below is the code I have.
<Path x:Name="path" Stretch="Fill" Width="111.75" Height="118.718" Data="F1M205.917,103.0088C189.333,93.8108,170.128,88.9998,150,88.9998C129.873,88.9998,110.584,93.8108,94.167,102.8408L116.1,144.2508L150,208.7178L183.9,144.2508z" Canvas.Left="0" Canvas.Top="0">
<Path.Resources>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Value="-1">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ToleranceRangeTypeChecker}">
<Binding ElementName="UserControl" Path="ToleranceZoneLowerBound" />
<Binding ElementName="UserControl" Path="ToleranceZoneUpperBound" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Data" Value="F1M205.917,103.0088C189.333,93.8108,170.128,88.9998,150,88.9998C129.873,88.9998,110.584,93.8108,94.167,102.8408L116.1,144.2508L150,208.7178L183.9,144.2508z" />
<Setter Property="Width" Value="111.75" />
<Setter Property="Height" Value="118.718" />
<!--<Setter Property="Canvas.SetLeft"-->
</DataTrigger>
<DataTrigger Value="0">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ToleranceRangeTypeChecker}">
<Binding ElementName="UserControl" Path="ToleranceZoneLowerBound" />
<Binding ElementName="UserControl" Path="ToleranceZoneUpperBound" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Data" Value="F1M150,88.9998C129.873,88.9998,110.584,93.8108,94.167,102.8408L150,208.7178C150,208.7178,150,114.157407529625,150,88.9998z" />
<Setter Property="Width" Value="55.917" />
<Setter Property="Height" Value="118.718" />
<!--<Setter Property="Canvas.SetLeft"-->
</DataTrigger>
<DataTrigger Value="1">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ToleranceRangeTypeChecker}">
<Binding ElementName="UserControl" Path="ToleranceZoneLowerBound" />
<Binding ElementName="UserControl" Path="ToleranceZoneUpperBound" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Data" Value="F1M205.917,103.0088C189.333,93.8108 170.128,88.9998 150,88.9998 150,113.365662567029 150,208.7178 150,208.7178L183.9,144.2508z" />
<Setter Property="Width" Value="111.75" />
<Setter Property="Height" Value="118.718" />
<!--<Setter Property="Canvas.SetLeft"-->
</DataTrigger>
</Style.Triggers>
</Style>
Another thought I had was to instead actually have 3 different paths and use setters to change the visibility of each, but I think that having one path and changing it's propertise would be more logical. I would also prefer one path because my goal would be to eventually animate between the paths rather than have them change instantly.
Thanks!
You can get the instance by casting the sender parameter in the property change callbacks.

Categories