How to trigger parent element using child? - c#

What I am trying to do is when my mouse got over a button inside a border that border background color must change.
<Border Name="HistoryBorder"
Grid.Column="0"
Height="30"
VerticalAlignment="Top"
Margin="0,0,326.8,0"
Background="#FF323030"
CornerRadius="0,0,15,0"
MouseDown="draging_bar">
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="75"
Name="HistoryButton"
Height="30"
Margin="0"
FontFamily="/Fonts/#Minecraft"
Foreground="#FFB6B002" >
<Button.Content>
History
</Button.Content>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=HistoryButton, Path=IsMouseOver}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Green" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Border>
As you can see border is parent and button is a child in it. When I try to use targetname it is not working. Is something like that even possible ?

As far as I understand, setting Style for Border to be bind with it's child's IsMouseOver can be one of the ways.
<Border Name="HistoryBorder"
Grid.Column="0"
Height="30"
VerticalAlignment="Top"
Margin="0,0,326.8,0"
CornerRadius="0,0,15,0"
>
<!-- You have to put Background color to Style. Or priority makes it cannot be overrided from the Setter of Style -->
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="#FF323030"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=HistoryButton, Path=IsMouseOver}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Black"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
...

Related

WPF override custom template using trigger XAML tag on validation

So I have a style for my TextBox as shown
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="20">
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<StackPanel>
<!-- ScrollViewer to restore the default textbox template that is gone because of
us now overriding it -->
<Border CornerRadius="2" Padding="2" Background="White" BorderBrush="Black" BorderThickness="1">
<ScrollViewer Margin="0" x:Name="PART_ContentHost" Background="Gray" />
</Border>
<ItemsControl FontSize="10" ItemsSource="{TemplateBinding Validation.Errors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
When there is an error, I'd like the text box to show a red background, with the error messages appearing underneath it. I've set up a trigger to change the TextBox's background when there's an error, but it seems that since I override the TextBox's normal template so that I can show the error messages, the background of the textbox itself doesn't change (it stays gray). I realize that this is because I used a ScrollViewer to restore the textbox appearance, but now how can I somehow refer to this ScrollViewer from my trigger?
It's more for learning purposes.
You need to better use ControlTemplate.
Possible example:
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="20"/>
<Setter Property="Background" Value="Gray"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<StackPanel>
<!-- ScrollViewer to restore the default textbox template that is gone because of
us now overriding it -->
<Border x:Name="PART_Border" CornerRadius="2"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ItemsControl FontSize="10" ItemsSource="{TemplateBinding Validation.Errors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="PART_Border" Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but now how can I somehow refer to this ScrollViewer from my trigger?
Move the trigger from the Style to the ControlTemplate and use the TargetName property of the Setter to refer to the ScrollViewer or the parent Border element inside the template, e.g.:
<ControlTemplate TargetType="TextBox">
<StackPanel>
<Border ...>
<ScrollViewer Margin="0" x:Name="PART_ContentHost" Background="Gray" />
</Border>
<ItemsControl ... />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="PART_ContentHost" Property="Background" Value="Red"/>
...
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

WPF: Move focus to previous control (SHIFT + Tab) when using ControlTemplate with Placeholder Textbox

I created a WPF TextBox with Placeholder text by creating a customized Style that overlays one TextBox (as a placholder text) on another TextBox in the ControlTemplate.
Here's the Style definition:
<Style x:Key="TextBoxFormBaseStyle"
BasedOn="{StaticResource {x:Type TextBox}}"
TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<TextBox x:Name="_textSource"
Panel.ZIndex="2"
Background="Transparent"
Text="{Binding Path=Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="_textTag"
Panel.ZIndex="1"
Focusable="False"
Text="{TemplateBinding Tag}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, Source={x:Reference _textSource}}" Value="">
<Setter Property="Foreground" Value="{Binding Path=OpacityMask, Source={x:Reference _textSource}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
<!-- Move Focus to child control -->
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="_textSource" Property="FocusManager.FocusedElement" Value="{Binding ElementName=_textSource}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
A Trigger on Property IsFocused allows focusing the the child TextBox control (named _textSource) when navigating through multiple TextBox's by Tab key.
Navigating to the previous element by pressing SHIFT + Tab is not working. How can I get SHIFT + Tab to set the focus to the previous element?
There are better ways to add a watermark if you want the TextBox to actuallty behave and get focused like a built-in TextBox. Try this style:
<Style x:Key="TextBoxFormBaseStyle" BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<TextBox x:Name="_textTag"
Focusable="False"
Text="{TemplateBinding Tag}"
BorderThickness="0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, RelativeSource={RelativeSource AncestorType=TextBox}}" Value="">
<Setter Property="Foreground" Value="{Binding Path=OpacityMask, RelativeSource={RelativeSource AncestorType=TextBox}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Sample usage:
<TextBox OpacityMask="Red" Tag="Red Watermark" Text="text" Style="{StaticResource TextBoxFormBaseStyle}" />
<TextBox OpacityMask="Green" Tag="Green Watermark" Text="text" Style="{StaticResource TextBoxFormBaseStyle}" />

Style Border in Buttons ControlTemplate with DataTrigger

I have the following Element:
<Button Click="btn_Click" Name="aName">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Name="tmpBorder" Padding="10">
<TextBlock Text="General" />
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding doIt}" Value="someString">
<Setter TargetName="tmpBorder" Property="Background" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
My goal is to set the background of the Border element in the ControlTemplate if doIt is set as "someString" in the code behind. doIt is a public member of the defined class in the code behind and will be set by the method btn_Click.
Executing the method and setting doIt works fine, but the Background of the border doesn't change to red. Am I missing something important?
this should work
<Button Click="btn_Click" Name="aName">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Padding="10" Background="{TemplateBinding Background}">
<TextBlock Text="General" />
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding doIt}" Value="someString">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
EDIT: since the first approach didn't work try directly with a style applied to the Border
If you don't set a property like a background in a control template your changes with a trigger won't work
<Button Click="btn_Click" Name="aName">
<Button.Template>
<ControlTemplate TargetType="Button">
<ControlTemplate.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding doIt}" Value="someString">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ControlTemplate.Resources>
<Border Padding="10" Background="{TemplateBinding Background}" Style>
<TextBlock Text="General" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>

WPF Rounded border button responsive to click color change

I have a button on my WPF application (MVVM Pattern) which is responsive to a click event. Basically if you click on this button its background becomes LightGreen (the default color is LightGray). I already achieved the desired behavior using the following code:
<Button Grid.Column="2" Grid.Row="6" Grid.ColumnSpan="3" Grid.RowSpan="2" Content="{Binding FirstSchedule.Message}" Command="{Binding FirstScheduleButtonClick}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="LightGray"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding FirstScheduleButtonSelected}" Value="True">
<Setter Property="Background" Value="LightGreen"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
In which FirstScheduleButtonSelected is a ViewModel property defined by:
private bool _firstScheduleButtonSelected;
public bool FirstScheduleButtonSelected
{
get { return _firstScheduleButtonSelected; }
set { _firstScheduleButtonSelected = value; NotifyPropertyChanged("FirstScheduleButtonSelected"); NotifyPropertyChanged("Background"); }
}
Now I need to make this button's borders rounded. I already tried this solution How to create/make rounded corner buttons in WPF?, and I ended up with:
<Button Grid.Column="2" Grid.Row="6" Grid.ColumnSpan="3" Grid.RowSpan="2" Content="{Binding FirstSchedule.Message}" Command="{Binding FirstScheduleButtonClick}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="15" Background="LightGray" BorderThickness="1" Padding="2">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="LightGray"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding FirstScheduleButtonSelected}" Value="True">
<Setter Property="Background" Value="LightGreen"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Now my borders are, in fact, rounded but when I click the button it does not becomes green.
Q: How can I modify this button in order to make its borders rounded and keep my already functioning behavior of changing its color on click?
Here's what I'd do:
<Window.Resources>
<!-- ... -->
<Style x:Key="GreenToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Background" Value="LightGray" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border
CornerRadius="15"
Background="{TemplateBinding Background}"
BorderThickness="1"
Padding="2"
>
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter
Property="Background"
Value="LightGreen"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ... -->
</Window.Resources>
...
<ToggleButton
Grid.Column="2"
Grid.Row="6"
Grid.ColumnSpan="3"
Grid.RowSpan="2"
Content="{Binding FirstSchedule.Message}"
IsChecked="{Binding FirstScheduleButtonSelected}"
Style="{StaticResource GreenToggleButtonStyle}"
/>

Button over Button has a different color

I have two buttons. Its the same grafic on both. But if i place one button on the other button, the button on top will be a little more brighter.
<Button x:Name="btnMenue1" HorizontalAlignment="Left" Margin="10,0,0,360" Width="625" Click="btnMenue1_Click" VerticalAlignment="Bottom" FontWeight="Bold" FontSize="36" Foreground="White" Height="340" RenderTransformOrigin="0.497,0.503" Background="#FFCBCAC8" BorderBrush="#FF0F0F11" IsEnabled="False">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" Value="0.5" TargetName="Border"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding ElementName=btnMenue1Text, Path=IsPressed}" Value="True">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button x:Name="btnMenue1Text" HorizontalAlignment="Left" Margin="15,0,0,650" Width="310" Click="btnMenue1_Click" VerticalAlignment="Bottom" FontSize="36" Foreground="White" Height="50" RenderTransformOrigin="0.497,0.503" Background="{x:Null}" BorderBrush="{x:Null}" IsEnabled="False">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" Value="0.5" TargetName="Border"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<TextBlock x:Name="tbMenue1" Foreground="White" FontSize="36" Text="Menue1" VerticalAlignment="Top" Height="50" HorizontalAlignment="Left" FontFamily="Segoe UI Light" FontWeight="Bold" Width="290" />
</Button>
Picture if button is over button
Picture if button is not over button
Don't use Opacity for color blending.
So if you want something like a maroon color, use a maroon color, not a half-transparent red element on a black background.
(To hack around the hack you could add a completely black element below your transparent overlaying element. Don't do this.)

Categories

Resources