I have a button whose Content property keeps changing based on certain conditions which are set from the ViewModel. Essentially it gets set a value from 0 to 1000.
I also have another custom DP property on the same button whose value can be set to a certain enum.
What I am trying to solve is as follows (and unfortunately have no idea how I should go about it):
If my button content value is 0 - background of the button should be Gray.
If my button content value is 1 - background of the button should be Yellow.
If my button content value is 1 & the Custom DP has a value set (not the default value) - background should change to Red.
If my problem statement was just dealing with setting a background based on the integer set on the content - I could easily use Converters (String to Brush) and set my background. But the last condition which is now setting my background based on custom DP value coming in - have no idea how to solve...
Can somebody please suggest me an approach - sample code to solve such a problem.
<Button Content="0">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},Path=Content}" Value="0">
<Setter Property="Background" Value="Gray"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},Path=Content}" Value="1">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Content}" Value="1"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=YourDP}" Value="YourValue"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="Red"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
You can use multi-conditions triggers or multi data triggers. See this link and this link.
Related
I have a TextBox bound to a decimal value which I want to format to not display decimal values if they are zero. At the same time I want to change the Background to a color if the property value is zero and another boolean property is true.
I have managed to achieve both but not together. If I remove the StringFormat from the Text Binding below the Background works perfectly but a value of, for example, 10 is displayed as 10.00, which I don't want. With the StringFormat the Background is not set and the TextBox is empty when 0 and suppresses decimals if they are zero, as required (empty string for zero is acceptable). So I assume it has to do with the empty string. I did try to test for an empty string with Path=Text.IsEmpty and value="True" as suggested in another post but that gives a design time error that Property IsEmpty is not found on type String.
This is what I have so far:
<TextBox x:Name="FactorValueTextBox" Text="{Binding Value, StringFormat={}{0:# ###.##}}" IsReadOnly="{Binding CommodityGradingFactor.Total}" GotKeyboardFocus="GradeFactorGotKeyboardFocus">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Text}" Value="0"/>
<Condition Binding="{Binding CommodityGradingFactor.Manditory}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="LightPink"/>
</MultiDataTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsReadOnly" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="FontSize" Value="15"/>
<Setter Property="FontWeight" Value="Bold"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
For completeness the GotKeyboardFocus() function simply selects the current value when the user tabs into the TextBox:
private void GradeFactorGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (e.KeyboardDevice.IsKeyDown(Key.Tab))
((TextBox)sender).SelectAll();
}
Instead of binding the condition to the property Text
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Text}" Value="0"/>
Why not binding it to the property Value
<Condition Binding="{Binding Value}" Value="0"/>
Note:
Depending on your use case, you may want to implement INotifyPropertyChanged on your Value property, avoid naming your backfield property as "value" since it is a Contextual keywords
for INotifyPropertyChanged case.
I want to set focus on conditionally in a WPFform. Here I have two types of control. One is 'checkbox' and other one is button. The checkboxes are getting enabled on some condition, however the button control(s) are visible by default. I am able to put focus on button(s) whether checkboxes are getting enabled or not. Though checkboxes should get focus over button when checkboxes are getting enabled. Kindly, suggest an approach to achieve this please. I have used following code to achieve focus on button(s)
<Style x:Key="FocusElement" TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=chkCA, Path=IsVisible}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=chkCA}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=btnSaveAll, Path=IsVisible}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=btnSaveAll}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Thanks!
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'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
I created a WPF custom control, which works fine.
It has a style which sets some properties and a template.
Now i want to change the control, so it has a 'Active' property.
If this is true it should use the Property 'ActiveBrush' for the Stroke
of some Rectangles in the Template, else it should use 'InactiveBrush'.
I want to use the ActiveBrush as the default Stroke, and change it to InactiveBrush
with a Trigger.
This works fine with one Rectangle when i use this:
<Trigger Property="Active" Value="False">
<Setter TargetName="Rec1" Property="Stroke" Value="{Binding Path=InactiveBrush, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
But since I don't want to set each rectangle with a seperated setter, I am asking myself if it shouldn't be possible to set the property of all Rectangles in the Template with one Setter.
I already tried:
<Trigger Property="Active" Value="False">
<Setter Property="Rectangle.Stroke" Value="{Binding Path=InactiveBrush, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
But this didn't work.
Has anyone a suggestion, how to implement this?
Thanks in advance.
#Robert Rossney - this style will not run since a target type of Rectangle doesn't have a property Active. But that is the right path to go, with a minor change:
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding Active, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyControl}}}" Value="False">
<Setter Property="Stroke" Value="{Binding Path=InactiveBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyControl}}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
This style should be nested in Style.Resources of the control style or in ControlTemplate.Resources of the ControlTemplate for the control. This way you're localizing this style only to your control. Any Rectangle outside of your control will not be affected.
I'm sure you know this but are just overlooking it: If you want to apply a style to all controls of a type, create a style with a TargetType of that type and put it in your custom control's resource dictionary. If you still need to apply specific styles to individual controls of that type, define those styles using the BasedOn property.
So, in your MyControl.Resources element, you'd put:
<Style TargetType="Rectangle">
<Style.Triggers>
<Trigger Property="Active" Value="False">
<Setter Property="Stroke" Value="{Binding Path=InactiveBrush, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Style>
<Style>
and any Rectangle that needed its own style would start like this:
<Style TargetType="Rectangle" BasedOn="{StaticResource {x:Type Rectangle}}">