I wanted to update a stringformat of my binding via a datatrigger. So I thought of just doing this:
<TextBlock Text="{Binding Foo.Name}" Margin="3">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Path=., StringFormat='Start {0}'}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Foo.IsEnabled}" Value="True">
<Setter Property="Text" Value="{Binding Path=., StringFormat='Stop {0}'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
now this just shows the value of Foo.Name and not with the string format applied('start'/'stop' string).
I've modified the code, to get it working. This is the working code:
<TextBlock Margin="3">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Foo.Name, StringFormat='Start {0}'}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Foo.IsEnabled}" Value="True">
<Setter Property="Text" Value="{Binding Foo.Name, StringFormat='Stop {0}'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Now i'm looking for the reason why the first piece of code doesn't work and why the second piece of code works.
I've seen similar behavior in DataTemplates where I could not set or alter a property value via a Setter in a DataTrigger because the property was already set directly via the property on the object itself. Only when I removed the property from the object itself and set it as a style it worked.
Or is this just a limitation in WPF?
I wouldn't call it a limitation, it's documented. Dependency properties have a value precedence. https://msdn.microsoft.com/en-us/library/ms743230(v=vs.110).aspx
Related
In my user control I want to have label displaying Text from resource depending on viewmodel property Location value and with different format.
I implemented it by defining DataTrigger for each Location value
<Style x:Key="LocationValueHelper" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Location}" Value="HomeViewModel">
<Setter Property="Content" Value="" />
<Setter Property="ContentStringFormat" Value="{}{0}???"/>
</DataTrigger>
<DataTrigger Binding="{Binding Location}" Value="FirstViewModel">
<Setter Property="ContentStringFormat" Value="{}{0}+++"/>
<Setter Property="Content" Value="{localization:LanguageResource ResourceKey=First}" />
</DataTrigger>
<DataTrigger Binding="{Binding Location}" Value="SecondViewModel">
<Setter Property="ContentStringFormat" Value="{}{0}---"/>
<Setter Property="Content" Value="{localization:LanguageResource ResourceKey=Second}" />
</DataTrigger>
<DataTrigger Binding="{Binding Location}" Value="ThirdViewModel">
<Setter Property="ContentStringFormat" Value="{}{0}***"/>
<Setter Property="Content" Value="{localization:LanguageResource ResourceKey=Third}" />
</DataTrigger>
</Style.Triggers>
</Style>
<Label Foreground="White" HorizontalAlignment="Center" FontSize="40" Style="{StaticResource LocationValueHelper}" />
The problem is that format never changes from the first one (???) - HomeViewModel selected first - although text changing works.
I have
Home???
First???
Second???
Third???
instead of
Home???
First+++
Second---
Third***
There are a lot of discussions, using Labels and ContentStringFormat does not work like expected or desired:
WPF StringFormat on Label Content
StringFormat VS ContentStringFormat
...
The reason seems to be the template based implementation under the hood, since the content also could contain other data than strings. To solve your problem, my proposal is to use a TextBlock, which works quite smooth like this:
<StackPanel>
<CheckBox IsChecked="{Binding MyFlag}"/>
<TextBlock HorizontalAlignment="Center" FontSize="40">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding MyFlag}" Value="False">
<Setter Property="Text" Value="{Binding Path=MyText, StringFormat='{}{0}+++'}" />
</DataTrigger>
<DataTrigger Binding="{Binding MyFlag}" Value="True">
<Setter Property="Text" Value="{Binding Path=MyText, StringFormat='{}{0}???'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
I have done some research on this but failed to find out on how to reuse the style trigger.
I have style trigger set on Label and I am using Data trigger to set content. I have multiple labels in same view and also in different view. The Data bound is of same type except its different property of same data context.
Consider following 2 different labels where I need to display performance of 2 person - PersonA and PersonB. The value displayed for both the labels will be based on Performance format selected.
<Label Grid.Row="5" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource SomeGlobalStaticStyle}" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Fractional">
<Setter Property="Content" Value="{Binding DataModel.PersonA.Performance.Value.Fractional}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Decimal">
<Setter Property="Content" Value="{Binding DataModel.PersonA.Performance.Value.Decimal}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="US">
<Setter Property="Content" Value="{DataModel.PersonA.Performance.Value.US}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
<Label Grid.Row="6" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource SomeGlobalStaticStyle}" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Fractional">
<Setter Property="Content" Value="{Binding DataModel.PersonB.Performance.Value.Fractional}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Decimal">
<Setter Property="Content" Value="{Binding DataModel.PersonB.Performance.Value.Decimal}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="US">
<Setter Property="Content" Value="{DataModel.PersonB.Performance.Value.US}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
If you see, the only difference is the first trigger takes PersonA and the second trigger takes PersonB. I have 10 instances of such label spread in same and multiple views. Is there is way I can define this trigger once in Resources and bind whatever data I want to from each Label.
Looking forward for the solution.
Regards,
Abdyax
configurate setters to obtain values not from Label DataContext, but another Label property - Tag
<Style BasedOn="{StaticResource SomeGlobalStaticStyle}" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Fractional">
<Setter Property="Content" Value="{Binding Tag.Performance.Value.Fractional, RelativeSource={RelativeSource Self}}}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Decimal">
<Setter Property="Content" Value="{Binding Tag.Performance.Value.Decimal, RelativeSource={RelativeSource Self}}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="US">
<Setter Property="Content" Value="{Binding Tag.Performance.Value.US, RelativeSource={RelativeSource Self}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
and bind labels Tag to different properties:
<Label Grid.Row="5" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Tag="{Binding DataModel.PersonA}"/>
<Label Grid.Row="6" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Tag="{Binding DataModel.PersonB}"/>
both labels should pick our default style for TargetType="Label" with triggers
Is there is way I can define this trigger once in Resources and bind whatever data I want to from each Label.
Short answer: No, I am afraid not. You cannot replace the Value of a Setter but reuse the rest of the Style in XAML.
You could create the styles programmatically using the XamlReader.Parse method. Then you can simply replace the "PersonA" with "PersonB" in the string that you pass to the method.
But there is no way to do this generally in pure XAML.
I have a WPF application and I use Telerik.
I'm trying to set the Icon Template so that it has a default value and only on a certain condition will it bind the image source:
<telerik:RadWindow.Resources>
<Style x:Key="CustomIconStyle" TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsConditionMet, ElementName=MyWindow, UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="Source" Value="{Binding Path=IconImageSource, ElementName=MyWindow, UpdateSourceTrigger=PropertyChanged}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadWindow.Resources>
<telerik:RadWindow.IconTemplate>
<DataTemplate>
<Image Style="{StaticResource CustomIconStyle}" Source="/MyAssembly;Component/Resources/myIcon.ico" Height="16" Margin="0,0,5,0"/>
</DataTemplate>
</telerik:RadWindow.IconTemplate>
For some reason it always show the default icon.
I would also like to mention that I did implement the property changed - and I copied the same style just to a control inside the window and not in the template and it worked - so the problem isn't with the property changed
Any ideas?
You can use Triggers like that :
<telerik:RadWindow.Resources>
<Style TargetType="Image" x:Key="Style1">
<Setter Property="Source" Value="default.ico"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyCondition}" Value="true">
<Setter Property="Source" Value="custom.ico"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadWindow.Resources>
<telerik:RadWindow.IconTemplate>
<DataTemplate>
<Image Style="{StaticResource Style1}" Height="16" Margin="0,0,5,0"/>
</DataTemplate>
</telerik:RadWindow.IconTemplate>
So the problem was that once the RadWindow was loaded it didn't change the Icon.
The solution:
<telerik:RadWindow.IconTemplate>
<DataTemplate>
<Image Height="16" Margin="0,0,5,0">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="/MyAssembly;Component/Resources/myIcon.ico" />
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding Path=IsConditionMet, ElementName=MyWindow}">
<Setter Property="Source" Value="{Binding Path=IconImageSource, ElementName=MyWindow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</DataTemplate>
</telerik:RadWindow.IconTemplate>
But the trick is to give the correct value of IsConditionMet in the windows constructor before the load.
Thanks for the help everyone.
i've a little Problem with the Databinding in WPF an hope you can help me.
I want to bind the SelectedDate Parameter of a DatePicker on a TextBlock, but only when a CheckBox is checked. The CheckBox and the TextBlock are in a DataView, the DatePicker is outside.
In the Moment i try to use it with a Trigger an set the Binding in the Value-Property in the Setter Part.
<TextBlock Text="">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="True">
<Setter Property="Text" Value="{Binding ElementName=StandartPitBis, Path=SelectedDate, StringFormat='dd.MM.yyyy'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
But this will not works. Has anywone a Tipp what i can do?
Here is the Code-Parts there i'm using:
Inside the GridView
The CheckBox
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Content="" x:Name="check_Anlage" IsChecked="{Binding Path=IsChecked}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
The TextBlock
<TextBlock Text="">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="True">
<Setter Property="Text" Value="{Binding ElementName=StandartPitBis, Path=SelectedDate, StringFormat='dd.MM.yyyy'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Outside the GridView
The DatePicker
<DatePicker Grid.Column="1" Margin="0,5,0,5" SelectedDate="{x:Static sys:DateTime.Now}" x:Name="StandartPitVon" />
What i want to make is, that the SelectedDate from the DatePicker is shown in the TextBlock, but only when the CheckBox is Checked.
Thanks a lot
So. I've found the Problem. The Problem is, when a Property is set in the Object self, then it can't be overridet. When you need a Default-Value and a Trigger, then you must define the Default-Value in the Style too.
Example:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{x:Null}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="True">
<Setter Property="Text" Value="{Binding ElementName=StandartPitVon, Path=SelectedDate, StringFormat='dd.MM.yyyy'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I am new to xaml, i have following code below, my question is how can i call InvalidForeground from c# code to change the color of checkbox text?
<ControlTemplate x:Key="ItemTemplate"
TargetType="ListViewItem">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="CkBoxVisual">
<CheckBox.IsChecked>
<Binding Path="IsSelected"
Mode="TwoWay">
<Binding.RelativeSource>
<RelativeSource Mode="TemplatedParent" />
</Binding.RelativeSource>
</Binding>
</CheckBox.IsChecked>
<DataTrigger Binding="{Binding InvalidForeground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Value="true">
<Setter TargetName="CkBoxVisual" Property="Foreground" Value="Red"/>
</DataTrigger>
</CheckBox>
<ContentPresenter />
</StackPanel>
</ControlTemplate>
In your code, you are not calling anything. In it, you're hoping a change of dependency property..
But a control of type Window does not have a dependency property with the name: "InvalidForeground".
This trigger will never be triggered.
What is your goal, change a property or be notified of a change (trigger)?
Edit: There are a number of rules you must follow:
1) The control referenced in Binding property from DataTrigger (RelativeSource):
Binding="{Binding Path=InvalidForeground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
Must have a dependency property called "InvalidForeground", which works correctly, based on this, will not work:
{x:Type Window}
its type must be declared, will work, for example:
{x:Type my:ControlName}
2) A property changed by a Trigger or DataTrigger can never be explicitly stated, for example. Will not work:
<TextBlock Text="{Binding Any}" Foreground="#FFCCCCCC">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TabItem, Mode=FindAncestor}, Path=IsSelected}" Value="True">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Will work:
<TextBlock Text="{Binding Any}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#FFCCCCCC"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TabItem, Mode=FindAncestor}, Path=IsSelected}" Value="True">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
3) Check the operation of Bindings using RelativeSource they have some peculiarities (you should know a little about the visual tree of WPF).
http://msdn.microsoft.com/en-us/library/ms743599.aspx
4) You should check that the binding is working properly and can follow these steps:
http://www.codeproject.com/Articles/244107/Debugging-WPF-data-bindings