DataTrigger for Textblock - c#

I have a Textblock that I'm trying to change the value of the Text property if a property is True or False. The issue I'm having is that the flag could be changed on different events on the screen (onchange events from other combo boxes).
I'm not sure how to get this datatrigger to work as I don't think it know when the value has been changed.
<TextBlock Grid.Row="9" HorizontalAlignment="Right" Text="Some Old Value:" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="False" >
<Setter Property="Text" Value="Different Text:"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I see in some comboboxes there is the UpdateSourceTrigger=PropertyChanged, but I don't see a way to implement that in the TextBlock.

First of all set default Text in style setter, otherwise no matter whether your triggers fires successfully or not, Text won't take value from Style setter because of Dependency property value precedence order. Local value has higher precedence than style setter values.
<TextBlock Grid.Row="9" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="Some Old Value:"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="False" >
<Setter Property="Text" Value="Different Text:"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Second, if IsChecked property resides in your ViewModel (implementing INotifyPropertyChanged) and TextBlock DataContext is rightly pointing to ViewModel instance, you don't have to worry about it.
Just make sure whenever property IsChecked changes in ViewModel a PropertyChanged event gets raised so that UI can listen to that and updates itself.

Related

DataTrigger to a control's Validation.HasError

I have a user control which uses the INotifyDataErrorInfo interface and it turns red when it has errors, inside of this user control I put a TextBlock and the following DataTrigger doesn't seem to work:
<TextBlock Text="{Binding DurationText}"
Grid.Row="1">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ActivityUserControl, Path=(Validation.HasError)}"
Value="True">
<Setter Property="Foreground"
Value="White">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I made sure that the x:Name of my user control is correct (ActivityUserControl), the user control itself turns red when it has errors but the TextBlock's DataTrigger doesn't work (text stays black).
Your should set the Value of the DataTrigger to begin with:
<DataTrigger Binding="{Binding ElementName=ActivityUserControl, Path=(Validation.HasError)}"
Value="True">
...
You should also make sure that the ActivityUserControl is in the same namescope as the TextBlock and that it acually contains some validation errors.
You can solve the namescope issue by binding to the parent UserControl using the RelativeSource property:
Binding="{Binding Path=(Validation.HasError),
RelativeSource={RelativeSource AncestorType=UserControl}}"

How to bind ToolTip content in a custom textblock styles property setter

I am trying to create a textblock that shows a tooltip of the textblock's text when its being trimmed. I have the actual visibility part of this taken care of in a converter. My problem is binding the ToolTip's content to its partent textblock's text. I have been fiddling with different relative paths for awhile now and can never get anything but a blank tooltip. The text shows up fine when I want if I hardcode something in the tooltips content.
<Style x:Key="InfoToolTipBaseTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockBase}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget, Converter={StaticResource TrimmedVisibilityConverter}}" Content="{Binding Path=Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBlock}}}"/>
</Setter.Value>
</Setter>
</Style>
Instead of creating a tooltip that's always there, setting its content, and then switching its visibility, just have a style trigger on the TextBlock that sets the ToolTip property to Text when needed.
I've used your existing converter, but you might want to rewrite it to return bool. Then you'd have Value="True" in the DataTrigger.
<Style
x:Key="InfoToolTipBaseTextBlockStyle"
TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource TextBlockBase}"
>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource TrimmedVisibilityConverter}}"
Value="Visible"
>
<Setter Property="ToolTip" Value="{Binding Text, RelativeSource={RelativeSource Self}}" />
</DataTrigger>
</Style.Triggers>
</Style>
I don't know if this is an issue for you, but if Text changes at runtime, the the tooltip won't be updated, because that Binding doesn't know you care about the Text property. The fix for that would be to rewrite the converter as IMultiValueConverter so you can use it with a MultiBinding that would have bindings to Text as well as Self. It wouldn't have to use Text, but it would update the target when Text changes.
A better solution, simpler and WPFish, would be to write a behavior for TextBlock that recycles the guts of your converter, handles change notifications on Text, and updates an attached TextBlockExt.IsTextElided bool property on the TextBlock.
Solved it simply by this.
<Style x:Key="InfoToolTipBaseTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockBase}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget, Converter={StaticResource TrimmedVisibilityConverter}}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Text}"/>
</Setter.Value>
</Setter>
</Style>

WPF Style Triggers for DataTemplates

I would like to set Triggers for the controls in a DataTemplate. Whenever I set a property of the control within the DataTemplate, it seems not working. However, If do not set the property within the TextBlock inside the DataTemplate, then I can see the effect of Trigger in the style (it works). I am not sure whether using Style Triggers with DataTemplate is good or not! The XAML is below;
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock" x:Key="BlockOf">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontWeight" Value="ExtraBold" />
<Setter Property="FontSize" Value="22" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
...........
DataTemplate for the button,
<Button.ContentTemplate>
<DataTemplate DataType="Button">
<TextBlock Style="{DynamicResource BlockOf}" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"
FontStyle="Italic" FontSize="9"/>
</DataTemplate>
</Button.ContentTemplate>
I can see two problems here. First one is that current trigger will work only for TextBlock inside Button, not over whole Button. You can change that by using DataTrigger with RelativeSource binding. Second problem is that even when mouse is over TextBlock Style.Trigger cannot overwrite local value that you set against TextBlock so you need to bring default values as Setter into your Style. Check Dependency Property Setting Precedence List
<Style TargetType="TextBlock" x:Key="BlockOf">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="FontSize" Value="9"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=IsMouseOver}" Value="True">
<Setter Property="FontWeight" Value="ExtraBold" />
<Setter Property="FontSize" Value="22" />
</DataTrigger>
</Style.Triggers>
</Style>
and then TextBlock simply
<TextBlock Style="{DynamicResource BlockOf}" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />

Trouble setting a DataTrigger in WPF

I have a ComboBox and a Button on my main view, and I want to apply a style to the button such that when the combobox index is set to 1, the button becomes visible (initially it's hidden). This is my XAML code:
<Grid>
<StackPanel Orientation="Vertical" Margin="10">
<ComboBox Name="comboBox"/>
<Button Name="myBtn" Content="Hello" Visibility="Hidden">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBox, Path=SelectedIndex}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
Someone already asked a question about this here, and I'm doing pretty much the same thing, but it doesn't work, the button remains hidden even when the index is changed to 1. The comobox is initially being populated in the code behind with 2 items. Any help is appreciated.
The problem is that dependency property values set locally (like you've done with visibility) have a higher precedence then those set from a style trigger. As such, even when the trigger is hit, it won't override the value you've already set.
The simple solution is to instead set the default value in a style Setter:
<Button Name="myBtn" Content="Hello">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBox, Path=SelectedIndex}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
And now your trigger will override the property value when it is hit.
While you're at it, you should have a look at this link that lists the precedence order for setting DP values.

Change property depending on property in other control - WPF

I want to use some kind of trigger to change a button in my window to IsEnabled = False when the textbox property Validation.HasErrors of my textbox is True.
Can I do this with Triggers of some kind on the Button?
If yes, some example would be nice!
You can do this by using a Style. If you want to tie it directly to the text box, you could do something like this:
<Style x:Key="DisabledOnErrors" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding (Validation.HasErrors)}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBox x:Name="myTextBox" />
<Button Style="{StaticResource DisabledOnErrors}"
DataContext="{Binding ElementName=myTextBox}" />
This works if the button is not already binding to the DataContext for other properties. In this case, the Style is reusable for other button and text box pairings.
Yes, you can do this with triggers, but because you are referring to another element, you must use a DataTrigger rather than a normal Trigger:
<Button>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding (Validation.HasError), ElementName=tb}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Note you must use a Style rather than the Button.Triggers collection, because Button.Triggers can contain only EventTriggers.

Categories