WPF - RelativeSource in Style - wpf

My target is to bind the Content-Property of the Label to the Tag-Property of the Elements the Style is applied to. But my solution doesn't seem to work:
My style:
<Style
TargetType="TextBox"
x:Key="HintedTextBox">
<Style.Resources>
<VisualBrush
AlignmentX="Left"
AlignmentY="Center"
Stretch="None"
x:Key="HintedTextBox_Hint">
<VisualBrush.Visual>
<Label
Content="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"
Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<!-- Triggers -->
</Style>
My textbox:
<TextBox
Style="{StaticResource ResourceKey=HintedTextBox}"
x:Name="tbTest" />

If I understand correctly, you want to set the text for VisualBrush, that will be displayed in the TextBox.
You can do it like this:
<TextBox Name="MyTextBox" Tag="MyNewValue" Width="100" Height="25">
<TextBox.Background>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=TextBox}, Path=Tag}" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
</TextBox>
In order to explain why your example not earned:
1. As you probably understand, looking at my example, RelativeSource must be not self, in which case it will point to itself (VisualBrush), and the element with the type must be of TextBox, located higher in the visual tree.
2. Binding with RelativeSource does not work in resources, because the Resource is not part of the visual tree, or part of the template.
3. In styles this construction will not work, because the Style is just the collection of setters, he does not know about control, are there. For this purpose, usually using DataTemplate or ControlTemplate.
As an alternative, in this case, I suggest using a template for the TextBox, which will be registered VisualBrush.
Below is my example:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="MinWidth" Value="120" />
<Setter Property="MinHeight" Value="20" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border" CornerRadius="0" Padding="2" BorderThickness="1" BorderBrush="Black">
<Border.Background>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}"
Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
<ScrollViewer Margin="0" x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TextBox Name="MyTextBox" Tag="MyNewValue" Width="100" Height="25" />
</Grid>
Output

Related

Text box in a popup effecting a style for the controlling button

I am trying to build a text filter for a list in a popup, and notify the user when the filter is active by changing the color of the button that opens the popup.
I am using an MVVM lite setup and a XAML style sheet to reference the desired styles. Up until this point I have created the popup and the controlling button, and I have been able to put a mouse over trigger on the button and have it work. However when I tried to have a datatrigger set to the value of the textbox in the popup, it does not respond at all.
Here is the XAML code in the View for the button and the popup:
<StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Center" >
<TextBlock HorizontalAlignment="Center" Text="Filter Popup" />
<Button x:Name="PopUpControl" Tag="popup" Style="{StaticResource FilterButtonStyle}" Command="{Binding OpenPopupCommand}" IsEnabled="{Binding IsFilterEnabled, UpdateSourceTrigger=PropertyChanged}" Margin="2,1,2,1" VerticalAlignment="Top"/>
</StackPanel>
<Popup x:Name="textPopup" IsOpen="{Binding IsFilterPopupOpen, Mode=TwoWay}" PlacementTarget="{Binding ElementName=PopUpControl}" Placement="Bottom" Width="Auto" StaysOpen="False" Margin="2 2 2 2">
<TextBox x:Name="TextValue" Grid.Column="0" BorderThickness="1" Style="{StaticResource WatermarkedTextBox}" Margin="2,4" VerticalAlignment="Center" Tag="Text Filter" Text="{Binding FilterSearch, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
<Button Grid.Column="4" Style="{StaticResource SearchIconStyle}" IsEnabled="{Binding FilterEnabled, Mode=TwoWay}" Command="{Binding ApplyFilterCommand}" Margin="19,6" Grid.ColumnSpan="2" />
</Grid>
</Popup>
Here is the style for the button:
<Style x:Key="SortButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Width" Value="15"/>
<Setter Property="Height" Value="15"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Margin" Value="4,0,4,0" />
<Setter Property="Tag" Value="Default" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
Padding="4,2"
CornerRadius="3"
Background="{DynamicResource PaleBlue}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
<Border.OpacityMask>
<VisualBrush Visual="{StaticResource appbar_filter}" />
</Border.OpacityMask>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource ColumnHeaderFilterColor}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What I would like to happen, is when the element "TextValue" has a value in the Text field, the primary background color for the "border" changes color. Any help would be greatly appreciated.
You could use a DataTrigger that binds to the FilterSearch source property and sets a property if returns an empty string or null:
<ControlTemplate TargetType="Button">
<Border Name="border"
Padding="4,2"
CornerRadius="3"
Background="Green">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
<Border.OpacityMask>
<VisualBrush Visual="{StaticResource appbar_filter}" />
</Border.OpacityMask>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding FilterSearch}" Value="">
<Setter TargetName="border" Property="Background" Value="{DynamicResource PaleBlue}" />
</DataTrigger>
<DataTrigger Binding="{Binding FilterSearch}" Value="{x:null}">
<Setter TargetName="border" Property="Background" Value="{DynamicResource PaleBlue}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>

What is the correct XAML for a dynamic sized ToolTip?

I need to create a ToolTip for a label which is always twice the size of the Label. I have not been able to get the size of the ToolTip to be correct.
My code is here; I've included a complex template but the problem is the same if I use a string value:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="200" Height="200" Title="MainWindow">
<Window.Resources>
<ControlTemplate x:Key="Density">
<StackPanel>
<TextBlock Text="𝑘𝑔" />
<Separator Margin="2,0" BorderBrush="Black" />
<TextBlock Text="𝑚³" />
</StackPanel>
</ControlTemplate>
<TransformGroup x:Key="DoubleSize">
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2" ScaleY="2" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
<Style x:Key="TTipStyle" TargetType="ToolTip">
<Setter Property="RenderTransform" Value="{StaticResource DoubleSize}"/>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate >
<Grid Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Background="AliceBlue">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Template="{StaticResource Density}">
<Label.ToolTip>
<ToolTip RenderTransform="{StaticResource DoubleSize}" Background="AliceBlue" Style="{StaticResource TTipStyle}" Template="{StaticResource Density}" />
</Label.ToolTip>
</Label>
<ContentControl HorizontalAlignment="Left" VerticalAlignment="Top" Template="{StaticResource Density}" RenderTransform="{StaticResource DoubleSize}"/>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Top" Text="::---- THIS SIZE TOOLTIP"/>
</Grid>
</Window>
How should I define a Style for the ToolTip which has size extents that are large enough to contain the full, double-sized label content?
Take your RenderTransform out of the ToolTip tag itself...
<ToolTip Background="AliceBlue" Style="{StaticResource TTipStyle}" Template="{StaticResource Density}" />
...and then in your style remove the RenderTransform and Template and set the LayoutTransform instead:
<Style x:Key="TTipStyle" TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Setter.Value>
</Setter>
</Style>

WPF TabItem - Image Binding - using Triggers

I want to show Image inside of the Title of header in TabControl
This question(WPF TabItem - Image Binding) seems is right, but I have using Triggers and it doesn't help on my code.
Here is what I have Providing:
<TabControl Name="MainTabControl" Margin="0,28,0,0" TabStripPlacement="Top" >
<TabControl.Resources>
<Image x:Key="imgProfile" Source="/Icons/red icons/profile.png" />
<Image x:Key="imgComment" Source="Icons/red icons/notification.png" />
<Style TargetType="TabItem" >
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding}"
MouseDown="UIElement_OnMouseDown"
FontSize="18"
/>
<Image Height="25" Margin="4,0,4,0"
Source="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag.Source}" />
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" BorderThickness="1,1,1,0" BorderBrush="Gainsboro" CornerRadius="0" Margin="2,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="5,2"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="BorderBrush" Value="Transparent" />
<Setter TargetName="Border" Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource TabItemNormal}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Name="tiProfile" Header="a" Tag="{StaticResource imgProfile}"></TabItem>
<TabItem Name="tiComment" Header="b" Tag="{StaticResource imgComment}"></TabItem>
</TabControl>
the binding from TabItem.HeaderTemplate to the TabItem.Tag must be via FindAncestor of TabItem type.
instead create Images in Resources, create BitmapImage, and bind it the Image.Source.
so, Resources:
<BitmapImage x:Key="imgProfile" Source="/Icons/red icons/profile.png" />
<BitmapImage x:Key="imgComment" Source="Icons/red icons/notification.png" />
Header DataTemplate:
<Image Height="25" Margin="4,0,4,0"
Source="{Binding Tag, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}}" />

Trigger command when ListBox selected item changes

I am trying to set up a sort of menu using ListBox and buttons as ListBoxItems to trigger a command when selection changes, but the result is that some times the selected ListBoxItem is changed but the command is not triggered, other times the opposite. The XAML is the following:
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:TripViewModel}">
<views:TripView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:AddTripViewModel}">
<views:AddTripView />
</DataTemplate>
<Style TargetType="{x:Type Button}">
<Setter Property="Height"
Value="50" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Grid Background="Transparent">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Padding"
Value="3" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Window.Content>
<DockPanel Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto"
LastChildFill="True">
<!-- Left Panel -->
<Border BorderBrush="Black"
BorderThickness="0,0,1,0">
<StackPanel DockPanel.Dock="Left"
Margin="0"
Background="#555D6F">
<!-- First Row: Stacco Image -->
<Image Height="183"
Width="275"
Margin="3"
Source="/Media/stacco.png" />
<!-- Second Row: Buttons -->
<ScrollViewer CanContentScroll="True"
VerticalAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
Margin="0,10,0,0">
<ListBox Name="MenuButtons"
ItemsSource="{Binding PageViewModels}"
Background="#555D6F"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</ItemContainerTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</StackPanel>
</Border>
<!-- Control Area: Different View made by controls -->
<ContentControl DockPanel.Dock="Right"
Content="{Binding CurrentPageViewModel}" />
</DockPanel>
</Window.Content>
Am I doing right trying to insert a button in the ItemContainerTemplate? Would it be better to use the code behind to trigger the selection changed?

How to Clear Triggered Control Template

I am trying to achieve the "hint text" functionality for a TextBox in WPF. I can set the default text fine, but the problem comes when I want the control to return its appearance to a normal TextBox. Here is the trigger I have so far:
Trigger A
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBox>
<TextBox.Background>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="UniformToFill">
<VisualBrush.Visual>
<Label Content="{TemplateBinding TextBox.Tag}" Background="White"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
This sets the Background to a VisualBrush when the Text property is empty. What I need to do is clear this ControlTemplate when the user selects the TextBox to input text.
Here is what I tried:
Trigger B
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBox/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
These two don't work together. I tested each by changing the Background colors. If I comment out either one, they will each work. If both are uncommented, Trigger A works and B is never seen. How can I remove/overwrite Trigger A?
I know that the functionality of these templates is supposed to clear when the trigger condition is no longer met, but for example, Trigger A's setting will not go away when I enter text into the TextBox like it should. Like the Text property is still String.Empty or something.
So what am I missing?
EDIT:
Here is the whole style (there's not much more to it):
<UserControl.Resources>
<Style TargetType="TextBox" x:Key="FormsTextBox">
<Setter Property="Width" Value="45"/>
<Setter Property="Margin" Value="3 2 3 2"/>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBox>
<TextBox.Background>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="UniformToFill">
<VisualBrush.Visual>
<Label Content="{TemplateBinding TextBox.Tag}" Background="White" Width="45"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
I cannot see whole template but this looks a bit overcomplicated. I assume you're trying to achieve watermark text. For hint use box standard ToolTip property, which by default will display your text in a popup when hover your TextBox but this behaviour can be disabled and ToolTip property reused. You can either create reusable Style - which I prefer - for TextBox, something like this:
<Window ...>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<Style TargetType="{x:Type TextBox}" x:Key="WatermarkTextBoxStyle">
<Setter Property="ToolTipService.IsEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
BorderThickness="1"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
<Grid>
<TextBlock Margin="5,0,0,0"
Text="{TemplateBinding ToolTip}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}"
Opacity="0.5"/>
<ScrollViewer Name="PART_ContentHost"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<TextBox ToolTip="watermark text" Style="{StaticResource WatermarkTextBoxStyle}"/>
</Window>
or, if it's a one-off thing, then do you can do something like this without any Style or Template:
<Grid Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<TextBlock Margin="5,0,0,0"
Text="watermark text"
Opacity="0.5"
Visibility="{Binding ElementName=myTextBox, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="myTextBox" Background="Transparent" />
</Grid>

Resources