Style Border in Buttons ControlTemplate with DataTrigger - c#

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>

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>

How to trigger parent element using child?

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>
...

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

MouseOver effect for button in DataTemplate

I use a DataTemplate to show some buttons with a customized view (with an image, text, etc). Here is a simplified example:
<DataTemplate DataType="{x:Type viewModel:ActionItem}">
<Button Background="SlateGray" Command="{Binding Command}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="DarkGoldenrod"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<TextBlock Text="{Binding Name}" />
</Button>
</DataTemplate>
Why is the mouse over effect not working at all?
Because Background="SlateGray" overrides anything you can trigger in a Style. Remove that bit and it should work.
Please set the trigger at at the template level like
<DataTemplate DataType="{x:Type viewModel:ActionItem}">
<Button Background="SlateGray" Command="{Binding Command}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="DarkGoldenrod"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
<TextBlock Text="{Binding Name}" />
</Button>
</DataTemplate>

How do I access an element inside a style programmatically?

I have a WPF button style below:
<Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{Binding Width}" Height="{Binding Height}">
<Border Name="container" Background="{Binding Background}" CornerRadius="{Binding CornerRadius}">
<TextBlock Margin="10" FontFamily="Arial" FontWeight="Bold" TextAlignment="Left" HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto" FontSize="{Binding TextSize}" Foreground="White" Text="{Binding Text}" TextWrapping="Wrap"/>
</Border>
<Border Name="overlay" Background="Transparent" CornerRadius="{Binding CornerRadius}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="overlay" Property="Opacity" Value="0.6" />
<Setter TargetName="overlay" Property="Background" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that, although all of the buttons of my application have the same style, some of them need to have a border around it. Therefore I would like to know if it is possible to access the container border of my style to set its thickness and color? If so how can I do this?
Edit:
I've mixed the suggestions of madd0 and Josh and created a DataTrigger inside my style with a binding to a property that tells me if the button should or should not have a border.
The final code is below:
<Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{Binding Width}" Height="{Binding Height}">
<Border Name="container" Background="{Binding Background}" CornerRadius="{Binding CornerRadius}">
<TextBlock Margin="10" FontFamily="Arial" FontWeight="Bold" TextAlignment="Left" HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto" FontSize="{Binding TextSize}" Foreground="White" Text="{Binding Text}" TextWrapping="Wrap"/>
</Border>
<Border Name="overlay" Background="Transparent" CornerRadius="{Binding CornerRadius}" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=HasBorder}" Value="true">
<Setter TargetName="container" Property="BorderThickness" Value="{Binding BorderThickness}" />
<Setter TargetName="container" Property="BorderBrush" Value="{Binding BorderBrush}" />
</DataTrigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="overlay" Property="Opacity" Value="0.6" />
<Setter TargetName="overlay" Property="Background" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thank you all for your help,
Komyg
I think if you extend the Button class and give it a new boolean DependencyProperty, essentially all you need to do is give your Border a name, then in ControlTemplate.Triggers, Trigger off that boolean property to make the border as you need it in your special cases.
It would be similar to what you already have with the IsPressed ControlTemplate trigger.
I don't think you'll really need to access your control template properties. Since Button already has border properties, you ought to just set those properties on the button directly. Then, add the border to your ControlTemplate, binding its properties to that of the button.

Categories

Resources