Common datatrigger in Wpf - c#

Is there any way to make this common?
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentEnum}"
Value="{x:Static Enum.Smth}">
<Setter Property="Header"
Value="Other header" />
</DataTrigger>
</Style.Triggers>
Or
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentEnum}"
Value="{x:Static Enum.Smth}">
<Setter Property="Content"
Value="Other title" />
</DataTrigger>
</Style.Triggers>
Would be great if there is some way to pass parameter like "Title" to trigger and use it on every framework element.
I tried to make attached properties:
behaviors:ChangeContentOnCondition.ContentToChange="NewHello"
behaviors:ChangeContentOnCondition.DefaultContent="Hello"
behaviors:ChangeContentOnCondition.ChangeWhenObjectEqualsTo="{x:Static Enum.Smth}"
behaviors:ChangeContentOnCondition.Value="{Binding CurrentEnum}"
This failed when i didnt know how to listen when all properties are set.
Any help would be great. Thanks.

You'll probably find this easier if you create a single Behavior, with all of the attached properties contained within the Behavior class. This will allow you to change the necessary property.
Also, for the "ContentToChange", I'd set the value using a Binding rather than a string value to avoid you having to try to do a property lookup on the Attached object.

Related

How to use a datatrigger that needs a static global variable?

I have a static class that has a static property that I need to set the visilibity of a button.
I am trying something like that:
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=x:Static vg:GlobalResources.MyProperty, Path=IsAdmin}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
Also, in the header of the xaml I include the name space:
xmlns:vg="clr-namespace:MyApplication.resources"
But when the property IsAdmin is true, the button is not visible.
I have tried another options like this:
<DataTrigger Binding="{Binding ElementName=x:Static vg:GlobalResources, Path=MyProperty.IsAdmin}" Value="True">
But it doesn't solve the problem.
So, how could I use the global variable?
Thanks.
Use the Source property of the Binding, i.e.
<DataTrigger Binding="{Binding Source={x:Static vg:GlobalResources.MyProperty}, Path=IsAdmin}" Value="True">
Important: IsAdmin must be either a dependency property or it must fire INotifyPropertyChanged.PropertyChanged when it is changed.
Since WPF 4.5 there is also this syntax for binding to static properties:
Binding="{Binding Path=(vg:GlobalResources.MyProperty).IsAdmin}"
You can bind a static as you would any other property, i.e. just make it static, or do something like this:
<TextBlock Text="{x:Static vm:MyViewModel.MyStaticText}" />
I suspect the real problem here is that your static isn't a property i.e. it doesn't have a "get" accessor. No way around that one I'm afraid, it's gotta at the very least look something like this:
public static string MyStaticText { get; } = "Hello World!";

using triggers as resources

I want to define triggers as resources to use them later in my controls.
Like this:
<Window.Resources>
<DataTrigger x:Key="Trigger1" Binding="{Binding ViewModelProperty1}" Value="Val1">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger x:Key="Trigger2" Binding="{Binding ViewModelProperty2}" Value="Val2">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
...
</Window.Resources>
However, when I try to run the code, the compiler complains that IsEnabled is not a valid member. I think this is because it cannot know if the control in question will even have the property "IsEnabled". Like with styles, I think I need to somehow specifiy the TargetType (which would be, in my case, FrameworkElement). But how?
NOTE:
Please do not suggest to use styles instead of triggers as resources. Since a control can only have ONE style, but I need to give SEVERAL triggers to one control, styles are no option here:
In my actual code I have a Button that should have trigger 1, 2 and 4 and a TextBox that should have trigger 1 and 3 and a Label that should have trigger 2, 3 and 4... I think you get it.
You can do it like this (note how I prepend IsEnabled with FrameworkElement and also how I reference those resources from style triggers):
<Window.Resources>
<DataTrigger x:Key="Trigger1"
Binding="{Binding ViewModelProperty1}"
Value="Val1">
<Setter Property="FrameworkElement.IsEnabled"
Value="False" />
</DataTrigger>
<DataTrigger x:Key="Trigger2"
Binding="{Binding ViewModelProperty2}"
Value="Val2">
<Setter Property="FrameworkElement.IsEnabled"
Value="False" />
</DataTrigger>
</Window.Resources>
<Button>
<Button.Style>
<Style>
<Style.Triggers>
<StaticResource ResourceKey="Trigger1" />
<StaticResource ResourceKey="Trigger2" />
</Style.Triggers>
</Style>
</Button.Style>
</Button>

ItemsControl with DataTriggers

Can someone provide a simple example how could you use DataTriggers on an ItemsControl?
For example if i say something like this:
<ItemsControl.Triggers>
<DataTrigger Binding="{Binding Items.Count}" Value="2">
<Setter TargetName="DocHost" Property="UniformGrid.Rows" Value="2"/>
</DataTrigger>
</ItemsControl.Triggers>
It gives me an error saying that ItemsControl expects an event trigger. Sadly i must use DataTriggers inside and ItemsControl. How can i do that?
You cannot use a DataTrigger in a TriggerCollection... yes, yes, I know... it's madness. However, you can put one in the TriggerCollection of a Style:
<ItemsControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count}" Value="2">
<Setter TargetName="DocHost" Property="UniformGrid.Rows" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
UPDATE >>>
Sorry, I didn't see that TargetName in there. The answer is to move this DataTrigger into the UnifrmGrid.Style instead and remove the TargetName property, but then you might have some trouble Binding to the Items property... let me know if you have any more problems.

Datagrid multitrigger referencing values from code-behind

I'm still bloody green in WPF and have not yet fully grasped the concept behind it. I've got the following problem:
I want to set triggers in a datagrid depending on a precondition.
Example:
In my code-behind, I have a string variable, let's call it variableString. Now depending on the the value of variableString, I want to enable/disable the triggers inside a datagrid, that I have defined in XAML like:
if(variableString == "a")
then
XAML
<DataGrid AutoGenerateColumns="False" Margin="5,5,0,75" Name="dataGrid1" ItemsSource="Binding}">
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SomeColumnName}" Value="someValue">
<Setter Property="Background" Value="White"/>
<DataTrigger Binding="{Binding Path=SomeColumName}" Value="someOtherValue">
<Setter Property="Background" Value="Red"/>
</Style.Triggers>
</Style>
</DataGrid.ItemContainerStyle>
Otherwise, if
if(variableString == "b")
then
Do Nothing`
I've already tried binding the string to the datacontext of the datagrid, but that was rather contra-productive, as it removes my binding to the database.
Can anyone help me here. An example, a push in the right direction etc...
I really like the options that WPF gives you, however it's that fundamental things, that were so easy to handle in WinForms, that drive me mad in WPF.
Thanks
I think you want a MultiDataTrigger, which allows you to base your trigger off of multiple values
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=SomeColumnName}" Value="someValue" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=variableString}" Value="A" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="White" />
</MultiDataTrigger>
To find your string in the code behind, you'll probably have to use some kind of RelativeSource binding to find the class containing that property. My example assumes there is a public property called variableString on the Window class

Is it possible to reload XAML properties of a UserControl?

I have some frameworkElements inside XAML, and I define some properties like background, and cursor.
In code behind, I change these properties, and when an event triggers, I want to reload these initial properties defined in XAML. Is this possible or I need to redifine manually in code behind?
Thanks.
A control defined in XAML is essentially defining an instance. Once you have the instance, the object is just like every other object you deal with. Having access to the instance defined in XAML within your code behind is akin to creating a new object in the code behind and then adjusting its properties at run time.
When you want the property value to change; you don't revert your property changes, you simply change them to what you desire.
I would suggest looking into DataTriggers for making temporary changes based on some value. This will change the value of a property while a specific condition is true, and revert it to its original value when the condition is false.
For example, here's a style that will change the cursor to a Wait cursor while loading, and change the background to Red if it is invalid.
<Style TargetType="{x:Type local:MyUserControl}">
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="True">
<Setter Property="Cursor" Value="Wait" />
</DataTrigger>
<DataTrigger Binding="{Binding IsValid}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
Of course, you'll have to define the IsLoading and IsValid properties behind your UserControl, and set them to true/false at the appropriate times in your code-behind.

Categories