WPF - When TextBox inside DateTemplate is focused change datatemplate child property - wpf

I have a listview with a Border wrapping a textbox (and other elements not shown in sample code). I want when the textbox is keyboard focused to change a property of the border that wraps it.
<ListView ItemsSource="{Binding activeLists}">
<ListView.ItemTemplate>
<DataTemplate>
<Border x:Name="border">
<TextBox Text="something">
<TextBox.Style>
<Style>
<Style.Triggers>
<Trigger Property="TextBox.IsFocused" Value="True">
<Setter TargetName="border" Property="TextBox.Background" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If using inside the trigger TargetName="border" the item is not found.
If possible to do this from XAML only.

When I tried running this, I also got the error "TargetName property cannot be set on a Style Setter". Which indicates that you can't set a property of the Border control inside a style setter for the TextBox control (which doesn't honestly surprise me.)
What you can do instead is set it in the style of the border control itself, using a DataTrigger to bind to the IsFocused property of the textbox:
<Border>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsFocused, ElementName=textBox}" Value="true">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBox Name="textBox" Text="something"/>
</Border>

Related

WPF: How to bind to another control's child's child properties

Problem:
I have two separate objects in a grid: A TextBox, and a TextBlock
I want the TextBlock to be visible when the vertical scrollbar of the TextBox is visible.
Update : The vertical scrollbar visibility should be set to Auto, so it is the actual visibility (see below) we want to trigger on.
Approach:
The template of the TextBox seems to contain a Grid named PART_InnerGrid which in turn contains a scrollviewer named PART_ContentHost. This scrollviewer contains a property called ComputedVerticalScrollBarVisibility which is of type Visibility.
I have tried creating a trigger on the TextBlock, but I have been unable to construct a proper binding path. How would I do this:
<TextBox x:Name="TB1" TextWrapping="Wrap" AcceptsReturn="True" MinHeight="40" VerticalScrollBarVisibility="Auto"/>
<TextBlock Text="VISIBLE" Grid.Row="1" VerticalAlignment="Bottom">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TB1, Path=(PART_InnerGrid).(PART_ContentHost).ComputedVerticalScrollBarVisibility}">
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
You could bind both of their visibility to a boolean property and use a Converter to convert the boolean to a visibility value.
Their visibility would look like this:
Visibility="{Binding YourBoolProperty, Converter={StaticResource Converters_BoolToVisConverter}}"
<StackPanel>
<TextBox Text="text" Width="50" VerticalScrollBarVisibility="Hidden" x:Name="TextBox"/>
<TextBlock Text="ScrollViewer visible">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TextBox, Path=VerticalScrollBarVisibility}" Value="Visible">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>

Binding to Style Target using RelativeSource in Setter.Value

I want to create a Trigger to be applied to all TextBox on Validation.HasError to show the Validation.Error in a custom ToolTip.
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource XXX}, Path=(Validation.Error)[0].ErrorContent}"/>
</StackPanel>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
What should I put in the XXX?
My problem is that I don't really understand how RelativeSource works in this context and I can't get the correct code for binding to the TextBox.
I am guessing Self would refer to the TextBlock and FindAncestor x:Type TextBox will fail becuase it will traverse from TextBlock > StackPanel > Setter.Value > Setter > etc.. instead.
How can I refer to the Style Target instead?
Since ToolTip is not part of the visual tree, it's a bit cumbersome to get the behavior you want.
You can use its PlacementTarget property to find the element it is attached to, and set its DataContext to that element. In your case that will be a TextBox.
Now you can bind directly to the Validation.Errors property, and it will find the validation errors on a given TextBox.
You can use the following code to get it working:
<Window.Resources>
<ToolTip x:Key="errorTooltip"
DataContext="{Binding PlacementTarget,
RelativeSource={RelativeSource Self}}">
<StackPanel>
<TextBlock Text="{Binding (Validation.Errors)[0].ErrorContent}" />
</StackPanel>
</ToolTip>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{StaticResource errorTooltip}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>

TextBox trigger to clear Text using a style

First, let me say I've been working with WPF for about a week. I want to style a TextBox so that when it is disable, it is cleared. This article explained how to do it, however I'm confused on how to set the generic style as a resource so that every TextBox can bind to a different property without repeating the style for each TextBox.
<Window.Resources>
<Style TargetType="{x:Type TextBox}" x:Key="style1">
<Setter Property="Text" Value="{What do I really put here?}" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
....
<TextBox Style="{StaticResource style1}" Text="{Binding SomeProperty}"/>
Thanks!
You won't be able to use the Text property like that. Setting the Text property explicitly on any TextBox that has that style will override the Text setter in the trigger (like you noticed).
If you only need the TextBox to be cleared and not the property it is binding to, then a workaround is to use an attached property (or Tag) for the text which you bind Text to in the Style.
Example..
<Style TargetType="{x:Type TextBox}" x:Key="style1">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self},
Path=Tag}"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Then a TextBox can use this Style like
<TextBox Style="{StaticResource style1}" Tag="{Binding SomeProperty}" />

WPF - Access the parent Control from inside a Style Setter ControlTemplate

When making controls non-amendable we display them as a TextBox to keep a consistent style. The problem is that a ComboBox can have any type of data so binding the Text property of the ControlTemplate TextBox is not as simple as using SelectedItem.
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}, Path=????, Converter={StaticResource ResourceKey=ComboToTextConverter}, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
The idea I have is to use a Converter and send the whole ComboBox so it can be handled by the Converter code. Is there anyway to do this?
Any other suggestions are welcome!
you need to use the SelectedValue and SelectedValuePath properties:
<Style TargetType="ComboBox" x:Key="cStyle">
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<TextBox Text="{Binding RelativeSource=
{RelativeSource TemplatedParent},
Path=SelectedValue}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
and heres your ComboBox
<ComboBox Name="cbox" ItemsSource="{Binding}"
Style="{StaticResource cStyle}"
SelectedValuePath="SomeText"
DisplayMemberPath="SomeText" />
now when you set the IsReadOnly property to true on the ComboBox, it turns into a TextBox with the selected value as its text.

Use a property trigger to change a property that already has a binding

How can I use a property trigger in a style (or another method) to change a property (ToolTip for example) that already has its value defined by a binding?
I have a simple button like so:
<Button Name="Button1" Style="{StaticResource ButtonStyle}"
ToolTip="{Binding Name}" >My Button</Button>
It has a binding on the tooltip to show the Name property of the class set as DataContext.
My problem is I want to show the Name when the button is enabled, but something else when it is disabled. I thought I could get around my problem with a style and a trigger like so:
<Style TargetType="Button" x:Key="ButtonStyle">
<Setter Property="ToolTipService.ShowOnDisabled" Value="True" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="ToolTip" Value="Disabled" />
</Trigger>
</Style.Triggers>
</Style>
But this doesn't work. If I removed the tooltip binding from the button then I do get the correct tooltip when the button is disabled. But it seems I can't have both a binding and a trigger on the same property.
I could get around it by adding another trigger like so:
<Trigger Property="IsEnabled" Value="true">
<Setter Property="ToolTip" Value="{Binding Name}" />
</Trigger>
But I would like to use the style for 4 or 5 buttons that will all have different bindings for the enabled ToolTip, but the same (fixed) tooltip when they are disabled.
Any suggestions?
The easiest solution to your problem is to create a button style that all of your buttons can use (_DisabledButtonToolTipStyle in the example below) and then define a unique style for each button that applies the enabled tool tip value. If the individual buttons always have a different tool tip value then I'd recommended simply embedding the style like below; however, if you wanted to reuse the enabled tool tip style you could easily define it in your resources and give it a key.
<Window.Resources>
<Style x:Key="_DisabledButtonToolTipStyle" TargetType="Button">
<Setter Property="ToolTipService.ShowOnDisabled" Value="True" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="ToolTip" Value="Disabled" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Name="Button1" Content="My Button">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource _DisabledButtonToolTipStyle}">
<Setter Property="ToolTip" Value="{Binding Name}" />
</Style>
</Button.Style>
</Button>
</Grid>
Your problem has nothing to do with binding, the problem is that properties you set directly on the element override the properties set in a style, for example:
Given the style
<Style TargetType="Button">
<Setter Property="Background" Value="Blue"/>
</Style>
And those two buttons:
<Button Content="1st"/>
<Button Content="2nd" Background="Red"/>
The first button will get its background from the style but the second overrides the style background.
You can get what you want with triggers but not using styles, you can use a DataTemplate like this (the TextBox and CheckBox are there for testing):
<ContentPresenter Content="{Binding}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBox Name="edt" Text="Tooltip text"/>
<Button Name="btn" Content="x"
ToolTip="{Binding ElementName=edt, Path=Text}"
ToolTipService.ShowOnDisabled="True"/>
<CheckBox Content="Enabled"
IsChecked="{Binding ElementName=btn, Path=IsEnabled}"/>
</StackPanel>
<DataTemplate.Triggers>
<Trigger SourceName="btn" Property="IsEnabled" Value="False">
<Setter TargetName="btn" Property="ToolTip" Value="Disabled"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>

Resources