I want to control a buttons IsEnabled state based on the following conditions. The button should be active only when :
Checkbox 1 or Checkbox 2 is checked.
Listbox contains atleast 1 list item.
My issue is : I have to use separate MultiDataTrigger for achieving this behavior. When I add the code to the first multidata trigger, it doesn't work. ie even if list box doesn't have any items, the button is getting enabled.
So I need a solution for this. is there any possible way to include the 3rd multi data trigger condition along with the 2 conditions, so that few lines of code can be saved?
Thanks in advance. Please check my code below.
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=Checkbox1, Path=IsChecked}" Value="False" />
<Condition Binding="{Binding ElementName=Checkbox2, Path=IsChecked}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=ListBox, Path=Items.Count}" Value="0" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
The MultiTrigger applies the associated setters or actions when all of the conditions are true (binary AND operation).
As mentioned, the trigger will fire on all the conditions being AND'd, which was why it didn't work when you put all the conditions together. The easiest way to produce an OR (without implementing a IMultiValueConverter), is to have multiple datatriggers (which imply a binary OR operation).
If you are intent on saving lines of code, you can use a DataTrigger instead of the MultiDataTrigger.
<DataTrigger Binding="{Binding ElementName=listView, Path=Items.Count }" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
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 have a ListView that's bound to a simple array of objects (mainly getters / setters). It shows after the main code has run as a simple report. Initially the dialog just shows the items that changes. I have "Show All Details" checkbox that the user can check to see all items. I have a MultiTriggger setup on the checkbox and the "ZeroChanged" property. The issue seems to be that the ListView needs to be told to refresh when the checkbox changes state so that the MultiTrigger will then adjust the Visibility Value = "Collapsed" action. I'm starting to think that I'll need some supporting C# code to process the checkbox and then refresh the ListView. The array of objects doesn't change, it's fixed output from the main program. The check box defaults to false Is this possible with just XAML?
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=_showAllDetailsChkBox}" Value="False" />
<Condition Binding="{Binding ZeroChanged}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
If you bind to the IsChecked property of the CheckBox, the Visibility property of the ListView should get set as soon as you check or uncheck the CheckBox. You are missing a binding path:
<Condition Binding="{Binding Path=IsChecked, ElementName=_showAllDetailsChkBox}" Value="False" />
<Condition Binding="{Binding ZeroChanged}" Value="True" />
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 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.
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