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
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!";
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>
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.
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
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