How to use multibiding with a Style Setter - wpf

The alternate row style is defined as:
<Style TargetType="telerik:GridViewRow">
<Setter Property="Background" Value="{Binding Color,Converter={StaticResource dataToColorConverter}}">
</Style>
But I want to update the rowstyle depedninng on multiple values. I want to achieve something like this.
<Style>
<Setter Property="Background" >
<MultiBinding Converter={StaticResource dataToColorConverter}>
<Binding Path="Color"/>
<Binding ElementName="myListBox" Path="SelectedItem"/>
</MultiBinding>
</Setter>
</Style>
But getting the error "The type 'Setter' does not support direct content."

Because the Setter element doesn't support direct content, you must specify that you are setting the Value property (include "<Setter.Value>" in your XAML):
<Setter Property="Background" >
<Setter.Value>
<MultiBinding Converter="{StaticResource dataToColorConverter}" >
<Binding Path="Color" />
<Binding ElementName="myListBox" Path="SelectedItem" />
</MultiBinding>
</Setter.Value>
</Setter>

Related

The attached property is not send to the style

I wanted to create a cell style to show errors and a text with the tooltip.
My style in the xaml dictionary file is this:
<Style TargetType="{x:Type DataGridCell}" x:Key="DataGridCellConErrores">
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
<Setter Property="ToolTipService.InitialShowDelay" Value="5000"/>
<Setter Property="ToolTipService.ShowDuration" Value="60000"/>
<Setter Property="ToolTip">
<Setter.Value>
<MultiBinding Converter="{StaticResource ResourceKey=dlgEnviarAlbaranCantidadParaDescontarTooltipMultivalueConverter}">
<MultiBinding.Bindings>
<Binding Path="(ap:CeldasDatagridConErroresAttachedProperty.TextoTooltip01)"/>
<Binding Path="(ap:CeldasDatagridConErroresAttachedProperty.TextoTooltip02)"/>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(ap:CeldasDatagridConErroresAttachedProperty.EsDatoCorrecto)}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Orange"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
This is how I am trying to use in my column of the datagrid:
<DataGridTextColumn Header="Cantidad Para Descontar" Binding="{Binding CantidadParaDescontar, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnDataErrors=True}" Width="AUTO" IsReadOnly="false"
ap:CeldasDatagridConErroresAttachedProperty.TextoTooltip01="{Binding Path=DescripcionCantidadParaDescontar}"
ap:CeldasDatagridConErroresAttachedProperty.TextoTooltip02="{Binding Path=MotivoCantidadParaDescontarIncorrecta}"
ap:CeldasDatagridConErroresAttachedProperty.EsDatoCorrecto="{Binding Path=EsCantidadParaDescontarCorrecta}"
CellStyle="{StaticResource DataGridCellConErrores}"/>
I can compile and run, but the text of tooltip is always empty and also it doesn't change the color of the background if the data is not correct.
How should I bind the attached properties?
Thanks.
You should bind to the attached property of the column. Try this:
<Binding Path="Column.(ap:CeldasDatagridConErroresAttachedProperty.TextoTooltip01)"/>

WPF: DataTrigger is not triggering inside DataTemplate

My requirement is to change foreground color to white if the background color of the Grid cell is Red.
I am using below template for the cell. But, its not triggering.
<DataTemplate x:Key="NumericThreeDecimalCellTemplate">
<TextBlock HorizontalAlignment="Right"
VerticalAlignment="Center">
<TextBlock.Text>
<Binding Path="Value"
StringFormat="###,###,###,###,###,###,##0.000;(###,###,###,###,###,###,##0.000)" />
</TextBlock.Text>
<TextBlock.Foreground>
<Binding Path="Value"
Converter="{StaticResource negativeToBrushConvertor}" />
</TextBlock.Foreground>
</TextBlock>
<DataTemplate.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Background.Color,RelativeSource={RelativeSource Mode=Self}}" Value="#FF0000">
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataTemplate.Resources>
</DataTemplate>`
Let me know if am doing anything wrong.
A local value, such as the returned by your negativeToBrushConverter, takes precedence over a value set by a style setter: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-value-precedence
So no matter what you set the Foreground property to in your Style, it won't be applied unless you remove this part:
<TextBlock.Foreground>
<Binding Path="Value" Converter="{StaticResource negativeToBrushConvertor}" />
</TextBlock.Foreground>
You should set the default value for the Foreground property in the Style. And you should set the AncestorType of the {RelativeSource} to whatever the property of your cell is:
<DataTemplate>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.Text>
<Binding Path="Value" StringFormat="###,###,###,###,###,###,##0.000;(###,###,###,###,###,###,##0.000)" />
</TextBlock.Text>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground">
<Setter.Value>
<Binding Path="Value" Converter="{StaticResource negativeToBrushConvertor}" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Background.Color,RelativeSource={RelativeSource AncestorType=dxg:GridCellContentPresenter}}" Value="#FF0000">
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>

Changing color of ListBoxItem based on index

I am trying to change the foreground (text) color of one of the items in a ListBox based on an index value. For example, if the index was 1, then the item at index 1 would have a different text color than all the other items. The index isn't the list selection index but my own value.
I searched around on Google and found some ideas to use AlternationIndex but I can't get it to work that. The converter is either receiving 0 (zero) or DependencyProperty#Unset for the AlternationIndex binding.
Here is my code:
<ListBox x:Name="videoList" SelectionMode="Single" AlternationCount="{Binding Mode=OneWay, Path=Items.Count}" Grid.Column="1" SelectionChanged="videoList_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Mode="OneWay" Converter="{StaticResource EqualityConverter}">
<MultiBinding.Bindings>
<Binding ElementName="videoWindow" Mode="OneWay" Path="VideoIndex" />
<Binding RelativeSource="{RelativeSource AncestorType=ListBoxItem}" Path="(ItemsControl.AlternationIndex)" />
</MultiBinding.Bindings>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Foreground" Value="Blue" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
For the RelativeSource I've tried: Self, TemplatedParent and AncestorType=ListBoxItem.
I also tried lots of different combinations with the Path value.
I can't get it to work. Any suggestions?
Solution: With the help from Clemens, turns out it was because the binding for the ListBox AlternationCount was incorrect. Oops!
<ListBox x:Name="videoList"
SelectionMode="Single"
AlternationCount="{Binding RelativeSource={RelativeSource Self}, Mode=OneWay, Path=Items.Count}"
Grid.Column="1"
SelectionChanged="videoList_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Mode="OneWay" Converter="{StaticResource EqualityConverter}">
<MultiBinding.Bindings>
<Binding ElementName="videoWindow" Mode="OneWay" Path="VideoIndex" />
<Binding RelativeSource="{RelativeSource Self}" Path="(ItemsControl.AlternationIndex)" />
</MultiBinding.Bindings>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
It works with RelativeSource Self and a large enough AlternationCount, e.g. int.MaxValue.
<ListBox AlternationCount="2147483647" ...>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource EqualityConverter}">
<MultiBinding.Bindings>
<Binding ElementName="videoWindow"
Path="VideoIndex"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="(ItemsControl.AlternationIndex)"/>
</MultiBinding.Bindings>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

How to bind to self?

I know RelativeSource Self binds to the DataContext in scope. That is not what I want. I have a TextBlock and I want to set a trigger which will multibind 2 things. A value from my DataContext/ViewModel which is easy and I have done that. The 2nd value I want is the Text property of the TextBlock. I can't seem to get the syntax at all.
I have this trigger in a TextBlock style.
<Style TargetType="TextBlock"}">
<Setter Property="Margin" Value="10"></Setter>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource CustomMultiValueConverter}">
<Binding Path="SelectedCategory"></Binding>
<Binding Path="Text" RelativeSource="{RelativeSource Self}"></Binding>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Blue"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
You should be able to use a RelativeSource.AncestorType Binding to reach the TextBlock.Text value. Try this:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10"></Setter>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource CustomMultiValueConverter}">
<Binding Path="SelectedCategory"></Binding>
<Binding Path="Text" RelativeSource="{Binding RelativeSource
AncestorType={x:Type TextBlock}}" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Blue"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>

How to fire a trigger twice on an attached property in a datagridcell?

O.K., taking a hint from my previous post, in short how do I get the trigger to fire on an attached property?
After more trial (and much error), I think I know where the problem is, but not how to fix it. When using drag and drop onto the datagrid, the Trigger responsible for changing the background of the DataGridCell only fires on the first drop or load into an unused cell. Outside of removing the data from the cell, in which case the cell returns to the color of the row, any further use of the cell returns its background to the first cell specific color it had and the trigger no longer fires.
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource watchNameConverter}" ConverterParameter="1">
<Binding RelativeSource="{RelativeSource Self}" Path="Text"></Binding>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}, AncestorLevel=1}" Path="." />
<Binding RelativeSource="{RelativeSource Self}" Path="."></Binding>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Purple"/>
<Setter Property="local:Scheduler.IsDirty" Value="True" />
</DataTrigger>
<Trigger Property="local:Scheduler.IsDirty" Value="true">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource colorTextConverter}" >
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}, AncestorLevel=1}" Path="." />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

Resources