How to multi-trigger on property and binding in wpf style - wpf

I have datagrid in my view and I am trying to trigger a style for the DataGridRowHeader so that it has a particular background when both of the following are true:
IsDirty=True (Property on the DataContext of the row)
IsRowSelected=True (Property on the DataGridRowHeader)
How do I write a multi-trigger that triggers for the above paired conditions as my following style code throws InvalidOperationException/{"Must have non-null value for 'Property'."}:
<Style x:Key="DataGridStandardRowHeaderStyle" TargetType="DataGridRowHeader">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Binding="{Binding IsDirty}" Value="True" />
<Condition Property="IsRowSelected" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="LightYellow" />
</MultiTrigger>
</Style.Triggers>
</Style>
Kindly help me out.

The mistake in my style code finally got across to me and the correct one that now works for me is given below:
<Style x:Key="DataGridStandardRowHeaderStyle" TargetType="DataGridRowHeader">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsDirty}" Value="True" />
<Condition Binding="{Binding IsRowSelected, RelativeSource={RelativeSource Self}}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="LightYellow" />
</MultiDataTrigger>
</Style.Triggers>
</Style>

Related

How to have alternative background color of the selected rows on WPF DataGrid via MultiDataTrigger

I would like to display the odd vs. even selected rows with different background color (gray) in a WPF DataGrid. I already use the RowBackground="#DDD", AlternatingRowBackground="#EEE" and AlternationCount=2 properties withing DataGrid itself to alternate the color of non-selected rows.
I've already browsed many threads on StackOverflow, but can't find why the Style.Triggers has no effect. I use this attached property to determine whether it is an odd or even row.
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<!-- Changes the row style template to have more advanced "borders" (dotted, dashed, etc.) based on value converters -->
<Setter Property="Template">
<!-- ... -->
</Setter>
<!-- Changes the background color of odd rows (will do the same for even one with a slightly different color) -->
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}"
Value="True"/>
<Condition Binding="{Binding (behaviors:DataGridBehavior.IsOddRow), RelativeSource={RelativeSource Self}}"
Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background"
Value="LightYellow"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<!-- Same principle for even rows -->
<!-- ... -->
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Same (no) effect if I use only one Condition in the MutliDataTrigger.
Thanks for any insights :-)
You do not have to use a custom behavior for this, you can use the built-in alternation mechanism.
<DataGrid AlternationCount="2"
...>
<!-- ...other properties -->
</DataGrid>
Create a style for DataGridRow with triggers for the IsSelected and AlternationIndex attached property. Note, that we have to set the row background color and the alternating background color here, too, because setting the RowBackground and AlternatingRowBackground on DataGrid will overwrite the background values defined in the style and the selection highlighting would not work, so remove them from DataGrid.
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<!-- Changes the row style template to have more advanced "borders" (dotted, dashed, etc.) based on value converters -->
<Setter Property="Template">
<!-- ... -->
</Setter>
<!-- Part for odd rows. -->
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="False"/>
<Condition Property="AlternationIndex"
Value="1"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background"
Value="#EEE"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="True"/>
<Condition Property="AlternationIndex"
Value="1"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background"
Value="Yellow"/>
</MultiTrigger.Setters>
</MultiTrigger>
<!-- Part for even rows. -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="False"/>
<Condition Property="AlternationIndex"
Value="0"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background"
Value="#DDD"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="True"/>
<Condition Property="AlternationIndex"
Value="0"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background"
Value="LightYellow"/>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Since a DataGridCell is a child of a DataGridRow, the selection colors defined there will occlude the row colors. To avoid creating the same style for DataGridCell, too, we set its background transparent when it is selected.
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
Value="True">
<Setter Property="Background"
Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>

DataTrigger inside a MultiDataTrigger?

I want to change 2 Properties on 2 different conditions using Multi-/DataTrigger.
I have 1 Button which changes its IsEnabled Property when ValidationRule returns an error.
I also want to change the Command Property of this Button but on other conditions.
<Button Content="Save">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource MaterialDesignFlatButton}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=FirstNameBox}" Value="False" />
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=LastNameBox}" Value="False" />
[...]
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Now I want to implement another Trigger which changes the Binding of the Command Property when anoither Propertie's Binding equals to XY.
So i need to implement this next to the Datatrigger above.
<Setter Property="Command" Value="{Binding CreateEmployeeCommand}" />
<DataTrigger Binding="{Binding CurrentManageMode}" Value="2">
<Setter Property="Command" Value="{Binding EditEmployeeCommand}" />
</DataTrigger>
So is it even possible to have 2 Triggers at the same time?
Okay, i fixed it adding another Trigger just in Style.Triggers.
I didn't tried this cuz many Controls/Properties doesn't allow multiple Content.
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource MaterialDesignFlatButton}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Command" Value="{Binding CreateEmployeeCommand}" />
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentManageMode}" Value="2">
<Setter Property="Command" Value="{Binding EditEmployeeCommand}" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=FirstNameBox}" Value="False" />
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=LastNameBox}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>

DataTrigger Error

I am working with some multi-trriggers as per this posting. The binding and error are below.
I am using a custom markup extension to display the resource image so it could be suspect but I don't think so since I have used it in styles before.
The error message says I am applying the wrong property for the type and I don't see why yet.
Cheers,
Berryl
triggers
<Style x:Key="AvatarPathImageStyle" TargetType="{x:Type Image}">
<Setter Property="Source" Value="{Binding AvatarPath}"/>
<Setter Property="Height" Value="96"/>
<Setter Property="Width" Value="96"/>
<Setter Property="Stretch" Value="UniformToFill"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AvatarPath}" Value="{x:Null}" />
<Condition Binding="{Binding Gender}" Value="{x:Static domain:Gender.Female}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="{resx:Resx ResxName=Smack.Parties.Presentation.Resources.PersonDetailView, Key=Img_Female}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AvatarPath}" Value="{x:Null}" />
<Condition Binding="{Binding Gender}" Value="{x:Static domain:Gender.Male}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="{resx:Resx ResxName=Smack.Parties.Presentation.Resources.PersonDetailView, Key=Img_Male}"/>
</MultiDataTrigger>
</MultiDataTrigger>
</Style.Triggers>
</Style>
binding
<Image Grid.Column="1" Grid.Row="4"
HorizontalAlignment="Center" Margin="10, 0" Style="{StaticResource AvatarPathImageStyle}"
/>
error
System.Windows.Markup.XamlParseException occurred
... InnerException: System.ArgumentException
Message='System.Windows.Controls.Image' is not a valid value for the 'System.Windows.Controls.Image.Source' property on a Setter.
...
update
Accessing the image statically also gets an Invalid Type error:
<Setter Property="Source" Value="{x:Static imgResources:PersonDetailView.Img_Female}"/>
Comment converted to answer:
The reason for the error is that images stored in a resx file are of type System.Windows.Drawing.Image, which are not directly compatible with WPF. You can write a converter, but unless you have a strong reason for using a resource dictionary I would just embed the images and reference them by URL relative to the XAML file

How to show text of a textbox in a tooltip if no validation error occurs. Otherwise show the validation error

I have a textbox which validates a date.
I want to show the content in a tooltip on the textbox if it is valid. Otherwise I want to show the validation error in a tooltip.
I've set the standard tooltip on Text and added a couple of trigger:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Validation.HasError"
Value="False" />
<Condition Property="Text"
Value="" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="ToolTipService.ToolTip"
TargetName="DataTextBox"
Value="{x:Null}"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Validation.HasError"
Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="ToolTipService.ToolTip"
TargetName="DataTextBox"
Value="{Binding (Validation.Errors)[0].ErrorContent}"/>
</MultiTrigger.Setters>
</MultiTrigger>
I need something like a negated Condition to check if text is added in the textbox.
Thank you for your help.
Just bind your ToolTip to the TextBox's Text by default, and use a Trigger to set the Validation error if the item has an error.
<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding
Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={RelativeSource Self}}" />
</Trigger>
</Style.Triggers>
</Style>
Also, you shouldn't use a MultiDataTrigger unless you're evaluating more than one condition

WPF: Condition Binding versus Property, XamlParseException using either

I'm having trouble with a Condition for a MultiTrigger. If I do the following:
<Condition Binding="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListView}}}" Property="IsEnabled" Value="True"/>
Then I get this exception:
Condition cannot use both Property and Binding. Error at object 'System.Windows.Condition' in markup file
However, when I do the following:
<Condition Binding="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListView}}, Path=IsEnabled}" Value="True"/>
Then I get this exception:
Must specify both Property and Value for Trigger. Error at object 'System.Windows.Condition' in markup file
What gives? If it matters, here's the entire trigger:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Binding="{Binding Path=IsSelected}" Value="True"/>
<Condition Binding="{Binding Path=ItemsControl.AlternationIndex}"
Value="0"/>
<Condition Binding="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListView}}, Path=IsEnabled}"
Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background"
Value="{StaticResource evenSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource evenSelectedBorder}" />
</MultiTrigger>
The API in this case is confusing. Condition is used for two different types of multi-triggers, and the properties used are different. When using MultiTrigger, you will use the Property and Value properties. When using MultiDataTrigger (which is what you need), you specify a Binding and a Value. So, if you just switch your code to use a MultiDataTrigger, you'll be good to go:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsSelected}" Value="True"/>
<Condition Binding="{Binding Path=ItemsControl.AlternationIndex}"
Value="0"/>
<Condition Binding="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListView}}, Path=IsEnabled}"
Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background"
Value="{StaticResource evenSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource evenSelectedBorder}" />
</MultiDataTrigger>

Resources