How to bind to a ViewModel Property from Application.Resources? - c#

I would like to bind to a Property ShowHat which is within a ViewModel called "HatViewModel" from Application.Resources.
I can obtain access to the ViewModel via a namespace like so:
xmlns:vm="clr-namespace:HatApp.ViewModels
<Application.Resources>
<vm:HatViewModel x:Key="HatVM"/>
</Application.Resources>
But when I try to use the ViewModel's Property in a binding, it does not work.
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding HatVM.ShowHat}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</ControlTemplate.Triggers>
Any help is greatly appreciated.

You should use Source when you trying to bind a property from a static resource:
<DataTrigger Binding="{Binding ShowHat, Source={StaticResource HatVM}}" Value="True">

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>

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.

Conditional Binding Without using Converter

How do I achieve the following:
<ComboBox
IsEnabled="{Binding bVisibilty = AnotherCollection.Count > 0 ? true:false}"/>
I can use a converter which will be converting count to boolen, but is there a better way of doing than overdoing converter everywhere.
You can use style triggers for that like so :
<ComboBox >
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding AnotherCollection.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
Obviously AnotherCollection needs to be an ObservableCollection so the UI will be notified every time item is being added\removed to it
You could bind to a Property on your ViewModel and put the boolean and INPC logic in the viewmodel

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

Categories