Bind to controls property in corresponding style - c#

I want to define a style for the text box and within the style I want to bind to a property which the corresponding style applies to. Something like this:
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Text="{Binding ????Bind to the textbox TEXT property????}">
</TextBlock>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Is this possible at all?
Here the full Window:
<Window x:Class="StyleBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StyleBinding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Text="{Binding Text, RelativeSource={RelativeSource Self}}"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBox HorizontalAlignment="Left" Height="34" Margin="235,140,0,0" TextWrapping="Wrap" IsEnabled="True"
Text="Just a simple text" VerticalAlignment="Top" Width="284">
</TextBox>
</Grid>
</Window>

You just have to use a RelativeSource binding to access the Text property of the TextBox.
<Setter Property="ToolTip" Value="{Binding Text, RelativeSource={RelativeSource Self}}"/>
In case you build a custom tool tip template in your style, you can do it like this.
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<TextBlock Text="{Binding PlacementTarget.Text, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
The PlacementTarget of the ToolTip is the TextBox here.

Here's a working example:
<Style BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Text}" />
</Style>
The key is to use a RelativeSource binding of Self.
There's no need to set a trigger on IsEnabled because a ToolTip will only display on an enabled control by default.

Related

Material Design and IntegerUpDown Tooltip to Display Error

I'm using MaterialDesign and the Xceed IntegerUpDown control in a WPF project. I'm trying to display errors associated with the updown control as a tooltip when the mouse hovers over the control.
I've gotten this to work with TextBoxes and TextBlocks by using the following global style:
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource CustomizedMaterialDesignTextBox}" >
<Setter Property="Margin" Value="5"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}"
Background="{DynamicResource ValidationErrorBrush}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}"
DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
But when I tried to adapt this to the updown control as follows, no tooltip is displayed:
<Style TargetType="{x:Type xctk:IntegerUpDown}">
<Setter Property="Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}"
Background="{DynamicResource ValidationErrorBrush}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}"
DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
No error message about a failed binding or whatnot is displayed in the output window when the app is run in the VS 2017 debugger and an error condition is triggered.
Interestingly, a red border appears around the control when an error condition exists, even without the custom updown style.
You didn't show how exactly you're binding it but remember to 1) specify UpdateSourceTrigger and 2) actually hover over it ! Here's a MCVE showing that it actually works with no problem.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xctk="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
xmlns:local="clr-namespace:WpfApp5"
x:Class="WpfApp5.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type xctk:IntegerUpDown}">
<Setter Property="Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}"
Background="{DynamicResource ValidationErrorBrush}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}"
DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Window.DataContext>
<local:SampleViewModel/>
</Window.DataContext>
<Grid>
<xctk:IntegerUpDown Text="{Binding MyInteger, UpdateSourceTrigger=PropertyChanged}"
FontSize="24"
Width="125"
Height="50"/>
</Grid>

How do I access an element of a parent template style in XAML?

I am having trouble accessing a named element of a parent template style in WPF.
I have a custom control for a Button (written per this StackOverflow question):
CustomButton.xaml:
<ResourceDictionary ...>
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid>
<Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CurrentImage" Property="Source" Value="{Binding ImageHover, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<Window ...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CustomButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>
ImageButton inherits Button, and Image and ImageHover are defined as dependency properties. This code works great and does what expected.
Now, I want to extend this template style for a single particular button, adding an additional Trigger for changing the image of the button. I am trying to do the following:
<Window...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CustomButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
<Style TargetType="{x:Type local:ImageButton}"
BasedOn="{StaticResource ButtonCustomStyle}"
x:Key="Disableable">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="CurrentImage" Property="Source" Value="\Images\disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
<local:ImageButton Style="{StaticResource Disableable}" Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>
But Visual Studio underscores the new setter line and says 'The name "CurrentImage" is not recognized'.
Is there a way to alter the CurrentImage element?
I am having trouble accessing a named element of a parent template style in WPF.
This is not possible. You can't base a ControlTemplate on another ControlTemplate.
I am afraid you will have to re-define the entire ControlTemplate from scratch in the derived Style if you want the setter to be able find the CurrentImage element that is defined in the template of the base Style.
WPF: Is there a way to override part of a ControlTemplate without redefining the whole style?
But instead of using a ControlTemplate trigger, you should be able to use a Style trigger that sets the Image property of the control itself rather than setting the Source property of the Image element in the template:
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid>
<Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Image" Value="{Binding ImageHover, RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type local:ImageButton}"
BasedOn="{StaticResource ButtonCustomStyle}"
x:Key="Disableable">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
Unlike ControlTemplete trigger, Style trigger doesn't have access to template elements. since you exposed Image property, use it in Style trigger setter:
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>

ControlTemplate DataTrigger is not fired in ItemsControl ControlTemplate

I have a NavigationMenuControl with an ObservableCollection<HtNavigationMenuQuickLinkItem>. Everything is working fine, but the Style on my HtMenuIcon Control is not triggered. Where the Visibility is changed correctly. Can someone please give me a hint where I have a mistake? QuickLinkSymbol is a DependencyProperty of an Enum.
I also want to put the Visibility Behavior into the DataTrigger section.
Navigation Menu
<Style TargetType="Navigation:HtNavigationMenu">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Navigation:HtNavigationMenu">
<Grid>
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=QuickLinkItems}"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
QuickLinkItem
<Style TargetType="Navigation:HtNavigationMenuQuickLinkItem">
<Style.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Navigation:HtNavigationMenuQuickLinkItem">
<Controls:MyButton Width="40" Height="40" Margin="10,10,10,0">
<Viewbox Margin="3">
<Controls:HtMenuIcon x:Name="icon" Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsQuicklink, Converter={StaticResource BoolToVis}}"/>
</Viewbox>
</Controls:MyButton >
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding QuickLinkSymbol, RelativeSource={RelativeSource TemplatedParent}}" Value="Home">
<Setter TargetName="icon" Property="Style" Value="{StaticResource Home}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to reference Home as {x:Static EnumNAmeSpace:EnumType.Home}.
Oh, and if QuickLinkSymbol is a DepProp of HtNavigationMenuQuickLinkItem,
just use Trigger instead of DataTrigger.

How To Style ListBoxItem

<Border Grid.Row="1" Padding="15" Margin="15" BorderBrush="LightBlue" Background="AliceBlue" BorderThickness="1">
<ListBox ItemsSource="{Binding Configs}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Focusable" Value="False" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<view:Config />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
This is the code I've written in XAML. However, it doesn't appear to affect anything.
There are so many posts on SO with similar questions but all of their response are "use ListBox.ItemContainerStyle" which I've done :S Such as There is no ListBox.SelectionMode="None", is there another way to disable selection in a listbox? and WPF, XAML: How to style a ListBoxItem using binding on property of ListBox ItemsSource object?
What I would expect is that there is no background, no border and the item is not focusable.
What have I done wrong?
Redefine the ControlTemplate of your ListBoxItem. The sample code below uses a redish color just to make the point, but you can just replace it with Transparent:
<Window x:Class="WpfApplication235.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication235"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="SampleData" Type="{x:Type sys:String}">
<sys:String>Item 1</sys:String>
<sys:String>Item 2</sys:String>
<sys:String>Item 3</sys:String>
<sys:String>Item 4</sys:String>
<sys:String>Item 5</sys:String>
</x:Array>
<Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<TextBlock Text="{Binding}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1FFF0000"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox x:Name="listBox" ItemsSource="{StaticResource SampleData}"
ItemContainerStyle="{StaticResource ListBoxItemStyle1}"
Height="150" Margin="0" Width="250"/>
</Grid>
and using a Transparent background, no focus, no highlighting, as required:
You can use style in resource like:
<Window.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#D8D8D8"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="FontWeight" Value="400"/>
<Setter Property="Height" Value="30"/>
<!--and so on whatever you want...-->
</Style>
</Window.Resources>
And remove code like:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Focusable" Value="False" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
And don't use any property in listBox which is used in style, other wise it will not behave which is described in style.

WPF Listbox with separators, missing highlighted colors

I have a listbox (C# WPF) which gets filled on startup. I would like a separator between every item in the list and I have found this code from another old post and it's actually working, but when I use the code, I loose the highlighted and selected colors instead and I can't figure out where it's going wrong.
How can I get back those highlighted and selected colors?
Here is the code I'm using.
<ListBox x:Name="radioBox" HorizontalAlignment="Left" Height="494" Margin="14,14,0,0" VerticalAlignment="Top" Width="287" Background="{x:Null}" FontFamily="Calibri" FontWeight="Thin" FontSize="25" Foreground="#FFEDEDF7" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" BorderThickness="1" Padding="30,30,0,0" >
<ListBox.ItemBindingGroup>
<BindingGroup/>
</ListBox.ItemBindingGroup>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel>
<Separator x:Name="Separator" Background="White" Opacity="0.1" Height="20"/>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Separator" Value="Collapsed"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Try below code. It's a sample done by me and it should work.
<Window x:Class="ListBoxStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:ListBoxStyle"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemContainerStyle="{DynamicResource _ListBoxItemStyle}"
Width="200" Height="250"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListBoxItem>Hello</ListBoxItem>
<ListBoxItem>Hi</ListBoxItem>
</ListBox>
</Grid>

Categories