WPF Style Triggers for DataTemplates - c#

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}" />

Related

How to clear TextBox text property in Binding inside a HierarchicalDataTemplate

I can not clean the text property of a TextBox when the user clicks inside it. The TextBox is inside a HierarchicalDataTemplate but the trigger doesn't work.
My code is as follows:
<HierarchicalDataTemplate x:Key="FoldersTemplate"
DataType="{x:Type vm:FolderViewModel}"
ItemsSource="{Binding Children}">
<Border>
<Grid>
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding Name}" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</Border>
</HierarchicalDataTemplate>
The action I would like to happen is that when the user clicks on the text, it disappears. How can I solve this?
Thanks a lot.

Reuse ToggleButton-Style in WPF

I have the following ToggleButton-Style:
<Style x:Key="GraphToggleButtonStyle" TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource Graph}" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource Graph_Off}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
I'm trying to reuse this for my ToggleButtons (it's a list) - so every item in my list has one ToggleButton. The problem is, that when I click on my ToggleButton, the icon of the buttons, which aren't clicked disappears. Only my clicked button shows the desired image... Am I doing something wrong with this style?
My ToggleButton-Implementation:
<ToggleButton Style="{StaticResource GraphToggleButtonStyle}"
ToolTip="{x:Static res:Resources.UseGraphToggle}"
Visibility="{Binding Selected,
Converter={StaticResource BoolToVisibilityConverter}}" />
You don't need to add child (Image) into Visual Tree whenever button IsChecked property changes.
Issue is Style's are shared by default unless you set x:Shared="False" and second any Visual can be added only in one Visual tree. If you add Visual in another Visual tree, it will be removed form previous Visual tree. Visual in your case is Image control.
In your case setting x:Shared="False" will work but that will break the re-usability feature of resources. So, I would suggest instead of creating new instance of Image control everytime, create Image control only once and in triggers change the Image Source.
<Style x:Key="GraphToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image x:Name="image"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsChecked,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ToggleButton}}" Value="True">
<Setter TargetName="image" Property="Source"
Value="{StaticResource Graph}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ToggleButton}}" Value="False">
<Setter TargetName="image" Property="Source"
Value="{StaticResource Graph_Off}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The Image in your style is a visual. One visual can only be used once in the visual tree. Instead of reusing the style let WPF create a new one each time its referenced:
<Style x:Key="GraphToggleButtonStyle" TargetType="ToggleButton" x:Shared="False">
....

Wpf listbox itemstlye triggers not working

I am using a listbox itemstyle and triggers to change the mouseover color but the style does not seem to work:
<ListBox ItemsSource="{Binding LocationItems}" SelectionChanged="RadListBox_SelectionChanged" Name="RLBLocations">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="BorderBrush" Value="#FF709A70"/>
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="200"/>
<Setter Property="Foreground" Value="#FF5C5C5C"/>
<Setter Property="FontFamily" Value="Franklin Gothic Book"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel Orientation="Horizontal" Height="50">
<Image Source="{Binding Icon}" Margin="0" Stretch="UniformToFill" Width="32" Height="32"/>
<TextBlock Margin="10,10,0,0" Text="{Binding Text.Text}" Foreground="Black" TextAlignment="Left"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding ToolTip}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="#FFF0F0F0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsMouseOver}" Value="True">
<Setter Property="Background" Value="#FF709A70"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I get this error in the output window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ListBoxItem', AncestorLevel='1''. BindingExpression:Path=IsMouseOver; DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'NoTarget' (type 'Object')
I understand that the error but need help trying figure out the style - I have tried doing it with a datatemplate and that did not work out either.
Your DataTrigger:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsMouseOver}"
Value="True">
<!-- ... -->
</DataTrigger>
Is trying to find the Ancestor (of Type ListBoxItem) of the ListBoxItem. Of course that doesn't exist unless you nest ListBoxItems inside of each other.
Change it to this:
<Trigger Property="IsMouseOver" Value="True">
<!-- ... -->
</Trigger>
Also, you're overriding the ListBoxItem's Template, and in your custom template there's nothing pointing to the ListBoxItem.Background property, that brush will never be visible on screen.
I suggest you use it for the StackPanel's Background:
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel Orientation="Horizontal" Height="50" Background="{TemplateBinding Background}">
<!-- ... -->
</StackPanel>
</ControlTemplate>
Also, I think you might be confusing the concept of ListBoxItem.Template with ListBox.ItemTemplate, which is a DataTemplate used to render your Data inside regular ListBoxItems. I suggest you take a look at this tutorial for more info.

ToggleButton Style only works on last ToggleButton

I'm trying to customize my ToggleButtons so that when checked they say 'Yes' in green and when not checked, say 'No' in red.
I've created the following style which is sitting in my Styles resource dictionary.
<!-- ToggleButtons -->
<Style x:Key="YesNoToggleStyle" TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="SpringGreen" />
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="Yes"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="Crimson" />
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="No"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
This works ... sort of. If the ToggleButton is the last one of either value, then it displays correctly. All previous buttons with the same value are blank. The height was also shrinking, but I fixed that with the 'Height' Setter above the triggers. To illustrate, when a new record is being created it looks like:
and after I've clicked buttons 1, 2, and 3 and 1 again:
I originally had the style referenced from the surrounding grid:
<Grid>
...
<Grid.Resources>
<Style BasedOn="{StaticResource YesNoToggleStyle}" TargetType="{x:Type ToggleButton}" />
</Grid.Resources>
But changing that so each ToggleButton references the style individually (<ToggleButton Style="{StaticResource YesNoToggleStyle}" ... />) hasn't made a difference.
I looked at Customizing the toggle state of a toggle button in wpf, and Override ToggleButton Style where the effect is the same, but they talk about external images, and my issues is all within wpf.
I also looked at the second answer to: i want to change backcolor of toggle button when toggle button ischecked and viceversa in WPF but a) I only have the blend + sketchflow preview that comes with VS2012, and b) i'm a total noob with blend and can't get from Select the "Checked State" to Reset the Background Color instruction in the answer (plus i'd be surprised if this task requires the blend tool).
Can anyone show me what to do to get multiple ToggleButtons to use the same style properly?
This works for me. Somewhere in Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="YesNoToggleStyle" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="Crimson" />
<Setter Property="Content" Value="No"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="SpringGreen" />
<Setter Property="Content" Value="Yes"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Note, that style is based on ToolBar.ToggleButtonStyle.
<Grid>
<Grid.Resources>
<ResourceDictionary Source="pack://application:,,,/Dictionary1.xaml"/>
</Grid.Resources>
<ItemsControl ItemContainerStyle="{StaticResource YesNoToggleStyle}">
<ToggleButton />
<ToggleButton />
<ToggleButton />
</ItemsControl>
</Grid>
try to replace Content property to ContentTemplate:
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="Yes"/>
</DataTemplate>
</Setter.Value>
</Setter>
In my case I wanted to have a "Locked" ToggleButton in a common dll defined and reused across my Apps.
Here's my result, which worked for me. Maybe someone find it useful (put this in a Resourcedictionary.xaml):
<BitmapImage x:Key="LockedLock"
UriSource="/...;component/Resources/Lock_closed_16p.png" />
<BitmapImage x:Key="OpenLock"
UriSource="/...;component/Resources/Lock_open_16p.png" />
<Style x:Key="LockButton"
TargetType="ToggleButton">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{DynamicResource OpenLock }"
Width="12"
Height="12"
Name="contentimage" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton , AncestorLevel=1, Mode=FindAncestor }, Path=IsChecked}"
Value="True">
<Setter Property="Image.Source"
TargetName="contentimage"
Value="{DynamicResource LockedLock }" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Credits to:
Setting Button's Content to <Image> via Styles
Setter Target Name not recognized

Validating data templates for items in an itemscontrol

I have an ItemsControl (let's say a ListBox) and I have a DataTemplate for the contents. When I press a button, I want all ListBoxItems to be validated. This works.
Yet, although all items are properly validated and I can retrieve error messages for them, WPF does only show the ValidationError.Template for the SelectedItem of the ListBox in question. It does not display the ValidationError.Template for the other items which failed validation. I do update the source of the binding for every item and the Validation.HasError Property is set to true for them! Just the visuals are missing, the style is not being applied!
Has anyone got a solution for the problem?
Sample
A TextBox Style:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<AdornedElementPlaceholder Name="MyAdorner" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{DynamicResource TextBoxBackgroundBrush}" />
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="{DynamicResource TextBoxFocusBackgroundBrush}" />
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="{DynamicResource ErrorBrush}" />
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
A DataTemplate for a Person Entity:
<DataTemplate DataType="{x:Type entities:Person}" x:Key="PersonItemStyle">
<Grid>
<TextBox x:Name="SomeTextBox">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validators:RequiredFieldValidationRule ErrorMessage="Please enter a name!" />
</Binding.ValidationRules/>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
Somewhere in some control:
<ListBox Grid.Row="1" x:Name="ListBoxPersons" Style="{DynamicResource ListBoxStyle}" ItemsSource="{Binding Path=Persons}"
ItemContainerStyle="{StaticResource PersonItemStyle}">
</ListBox>
Then try editing a few persons, for example set their names to null or use any erroneous binding. When you validate, the triggers for Validation.HasError will only be set for the selected item.
How can one work around that problem?
You are setting the ItemContainerStyle to a DataTemplate, why? The style is applied to all textboxes so you shouldn't have to set the ItemContainerStyle separately.

Categories