WPF how to attach different style to last child - c#

I've got a WrapPanel containing multiple Buttons.
All buttons have the same base style to define their basic appearance.
Every button has a visible border at the right side. But I want to use styles to remove the border of the last button.
I think its quite simple. WPF is new to me, so I want to understand it.
The solutions I find on the internet are for different cases. That solutions are all concerning ListBox or ItemContainer to style some list items by index.
I don't want to just add another style key to the last Button, because that's not dynamic, and the WrapPanel is dynamically populated with Buttons depending on the state of the application.
This is my code:
<Window.Resources>
<Style x:Key="lastTabStyle" TargetType="Button">
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style x:Key="tabButton" TargetType="Button">
<Setter Property="Background" Value="#FF21588B" />
<Setter Property="BorderThickness" Value="0 0 1 0" />
<Setter Property="Foreground" Value="#fff" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" Padding="20 10 20 10">
<ContentPresenter HorizontalAlignment="left" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF266095" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="tabButtonSelected" TargetType="Button">
<Setter Property="Background" Value="#FF3474B0" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" Padding="10 8">
<ContentPresenter HorizontalAlignment="left" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And the WrapPanel:
<WrapPanel VerticalAlignment="Bottom" x:Name="topTabs">
<Button Style="{StaticResource tabButton}">Button 1</Button>
<Button Style="{StaticResource tabButton}">Button 2</Button>
<Button Style="{StaticResource tabButton}">Button 3</Button>
</WrapPanel>

...But I want to use styles to remove the border of the last button.
Then the define another style and apply this to the last Button or simply set ("override") the BorderThickness property of the last Button:
<WrapPanel VerticalAlignment="Bottom" x:Name="topTabs">
<Button Style="{StaticResource tabButton}">Button 1</Button>
<Button Style="{StaticResource tabButton}">Button 2</Button>
<Button Style="{StaticResource tabButton}" BorderThickness="0">Button 3</Button>
</WrapPanel>
Local values take precedence over values set by styles: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-value-precedence

Related

WPF Override control background property

I'm kind of new to WPF and I'm not really sure how the whole thing works. I have three buttons below, which I would like to be individually coloured. I also want them to be transparent and have grey text when they are disabled.
I want them to retain the Background colour property assigned in MainWindow.xaml when they are enabled and be transparent when disabled.
I'm not sure how I would go about this. Does it involve templates, styles, binding converters? Any help would be greatly appreciated.
MainWindow.xaml
<Button Grid.Column="0" Content="Suspend" Style="{StaticResource BubbleButton}" IsEnabled="True" Background="Blue"/>
<Button Grid.Column="1" Content="Training Mode" Style="{StaticResource BubbleButton}" IsEnabled="True" Background="Orange"/>
<Button Grid.Column="2" Content="Exit Program" Style="{StaticResource BubbleButton}" Background="Red"/>
App.xaml
<Style x:Key="BubbleButton" TargetType="{x:Type Button}">
<!-- Triggers -->
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Trigger.Setters>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Gray"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
<!-- Style -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="10" Background="{TemplateBinding Background}" Name="Button" Margin="10,5,10,5">
<Grid>
<Border BorderThickness="1,0,1,1" BorderBrush="Black" CornerRadius="{Binding ElementName=Button, Path=CornerRadius}">
<Border.Effect>
<BlurEffect Radius="2" KernelType="Gaussian"/>
</Border.Effect>
</Border>
<Border BorderThickness="0,1,0,0" BorderBrush="White" Margin="2" Opacity="0.7" CornerRadius="{Binding ElementName=Button, Path=CornerRadius}">
<Border.Effect>
<BlurEffect Radius="2" KernelType="Gaussian"/>
</Border.Effect>
</Border>
<TextBlock TextWrapping="WrapWithOverflow" Text="{TemplateBinding Content}" FontWeight="Medium" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="20" />
</Style>
Enabled:
Disabled:
using ControlTemplate.Triggers and adding TargetName should make it work. do the same for TextBlock and Foreground (from what I see, the Setter in Style, not in Trigger should be White).
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Button" Property="Background" Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>

how to change button background in UWP

I want to change button background by clicking the button. But, I don't want to change background using click event.
I have tried in WPF and achieved it using below code:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background"
Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border CornerRadius="3"
BorderBrush="DarkGray"
BorderThickness="1"
Background="{TemplateBinding Background}" />
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers >
<Trigger Property="IsFocused"
Value="True">
<Setter Property="BorderThickness"
Value="2"/>
<Setter Property="FontWeight"
Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Content="All"
Height="20"
Width="50"
Margin="2"
Style="{StaticResource ButtonStyle}" />
Can anyone please suggest how to achieve the same functionality in UWP?
I want to change button background by clicking the button. But, I don't want to change background using click event.
For your requirement, You could realize it via use XamlBehaviors. For example:
<Button>
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Click">
<Core:ChangePropertyAction PropertyName="Background">
<Core:ChangePropertyAction.Value>
<SolidColorBrush Color="Red"/>
</Core:ChangePropertyAction.Value>
</Core:ChangePropertyAction>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
It is open source model and host all behaviors code on GitHub will allow new features and fixes to be addressed more quickly.

ControlTemplate use control property

Is it possible for the ControlTemplate to use a property from the control, that uses the template?
For example, I've got a button, that changes color to Red on MouseOver. But, I've also got a button, that looks exactly the same, except it changes to White, instead of Red. Would it be possible, that whatever Background value the Button has, that value is then used in the control template?
Currently, this is what my ControlTemplate looks like:
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="{x:Type Button}">
<Border>
<Border.Style>
<Style>
<Setter Property="Border.Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="True">
<Setter Property="Border.Background" Value="#FFE53935" />
<Setter Property="Window.Cursor" Value="Hand" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Foreground="#FFEEEEEE" HorizontalAlignment="Center" VerticalAlignment="Center" Text="X" FontFamily="Calibri" />
</Border>
</ControlTemplate>
What I'm trying to do, is have Border.Background set to whatever the Button Background value is. So if I have a <Button Background="Red" />, then the value of Border.Background is Red.
Yes It is possible for the ControlTemplate to use a property from the control, that uses the template.See this link for binding
<Window.Resources>
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="{x:Type Button}">
<Border BorderBrush="Black" BorderThickness="1" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
<Border.Style>
<Style>
<Setter Property="Border.Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="True">
<Setter Property="Border.Background" Value="{Binding Path=Background,RelativeSource={RelativeSource TemplatedParent}}" />
<Setter Property="Window.Cursor" Value="Hand" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Foreground="#FFEEEEEE" HorizontalAlignment="Center" VerticalAlignment="Center" Text="X" FontFamily="Calibri" />
</Border>
</ControlTemplate>
</Window.Resources>
<UniformGrid>
<Button Background="Red" Template="{StaticResource CloseButtonTemplate}" Height="30" Width="200"/>
<Button Background="Green" Template="{StaticResource CloseButtonTemplate}" Height="30" Width="200"/>
<Button Background="Blue" Template="{StaticResource CloseButtonTemplate}" Height="30" Width="200"/>
</UniformGrid>
You should probably do this:
<Border x:Name="border" Background="{TemplateBinding Background}">
See TemplateBinding on MSDN

Stretching not applying in a WPF templated Button

I created a templated button in WPF :
<ControlTemplate TargetType="{x:Type Button}"
x:Key="BoutonGris">
<Button Foreground="White"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<Button.Content>
<Border CornerRadius="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ContentPresenter />
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background"
Value="#58585a" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="{DynamicResource DegradeCouleurTheme}" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Button.Content>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</ControlTemplate>
I use this button in my Window :
<Button x:Name="BtnRestoreDefault" Template="{DynamicResource BoutonGris}" Content="Rest. défaut" Height="27" Width="88" Click="Button_Click_RestoreDefault"/>
My problem is that the button don't appear to have the Height="27" and Width="88".
Here is my result in VS2012 :
The button has the good size, but the gray area don't. I have use the Stretch keywords on the Template but i don't find the mistake.
If i use Stretch for HorizontalAlignment, HorizontalContentAlignment, VerticalAlignment, and VerticalContentAlignment, the gray has the good size, but, the text content is in left/up ! I would like a center text.
Anyone have an idea please ?
Thanks a lot,
Best regards,
Nixeus
This should do the trick.
<ControlTemplate TargetType="{x:Type Button}" x:Key="BoutonGris">
<Button Foreground="White" BorderBrush="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<Button.Content>
<!-- A lot of your code -->
</Button.Content>
<!-- A lot of your code -->
</Button>
</ControlTemplate>
One question, why do you have a Button in your ControlTemplate for a Button? Usually, I would expect simple controls like Border etc here, because you want to reimplement the visual appearance.
Edit
One of the ContentPresenter should be declared like this.
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
I'm not sure which one. But you can try both. ;o)

Multicontent on a wpf button ( Image + Text)

I would like to create a template for a button. I would like to place an image (on the left) and text on this button.
I tried to make a thing like that:
<ControlTemplate TargetType="{x:Type Button}" x:Key="BoutonImageEtTexte">
<Button Grid.Column="2" BorderBrush="Transparent" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Width="90" Height="27" >
<Button.Content>
<Label>
<Label.Content>Fichiers joints</Label.Content>
<Label.Foreground>white</Label.Foreground>
<Label.VerticalAlignment>center</Label.VerticalAlignment>
<Label.HorizontalAlignment>center</Label.HorizontalAlignment>
</Label>
<Border CornerRadius="3">
<ContentPresenter/>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="#58585a" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource DegradeCouleurTheme}" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Button.Content>
<Button.Style>
<Style TargetType="{x:Type Button}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}" >
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</ControlTemplate>
Here is my code with a stackpanel added:
<ControlTemplate TargetType="{x:Type Button}" x:Key="BoutonImageEtTexte">
<Button Grid.Column="2" BorderBrush="Transparent" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Width="90" Height="27" >
<Button.Content>
<StackPanel Orientation="Horizontal">
<Border CornerRadius="3">
<ContentPresenter/>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="#58585a" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource DegradeCouleurTheme}" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<Label>
<Label.Content>Fichiers joints</Label.Content>
<Label.Foreground>white</Label.Foreground>
<Label.VerticalAlignment>center</Label.VerticalAlignment>
<Label.HorizontalAlignment>center</Label.HorizontalAlignment>
</Label>
</StackPanel>
</Button.Content>
<Button.Style>
<Style TargetType="{x:Type Button}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}" >
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Here is my call of the Template :
<Grid Margin ="10,180,10,14">
<Button Template="{StaticResource BoutonImageEtTexte}" HorizontalAlignment="Left" Margin="13,0,0,0">
<Image Source="ToolBar_FicJoints.png" />
</Button>
</Grid>
Unfortunately, i only see the image, and , not the text ?
Anyone have an idea please ?
Thanks
Maybe you can try something easier:
<Button>
<StackPanel Orientation="Horizontal">
<Image Source="yourimage.jpg" />
<TextBlock>Button content</TextBlock>
</StackPanel>
</Button>
You can just wrap the 2 elements in a single element like StackPanel
Example:
<ControlTemplate TargetType="{x:Type Button}" x:Key="BoutonImageEtTexte">
<Button Grid.Column="2" BorderBrush="Transparent" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Width="90" Height="27" >
<Button.Content>
<StackPanel Orientation="Horizontal">
<Border CornerRadius="3">
<ContentPresenter/>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="#58585a" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource DegradeCouleurTheme}" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<Label>
<Label.Content>Fichiers joints</Label.Content>
<Label.Foreground>white</Label.Foreground>
<Label.VerticalAlignment>center</Label.VerticalAlignment>
<Label.HorizontalAlignment>center</Label.HorizontalAlignment>
</Label>
</StackPanel>
</Button.Content>
<Button.Style>
<Style TargetType="{x:Type Button}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}" >
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</ControlTemplate>
You'll find that creating a button with an image inside, will give you an ugly border, that should be avoided. A workaround is to create an event for an image.
For instance, you can create an OnMouseOver event that switches the button to, either a new image, text, or an image with text, or really anything you choose.
You can also create an OnClick event that could trigger the image to do a task as if it was a button.
What I have done in a situation like this, was create an image that looks like a custom button, then create the same image, and modify it so that a border appeared around it.
I made an OnMouseOver to switch the images (so the bordered image appears), and an OnClick to do the function I required.
You can then use this template for any other situation you have with custom buttons.
Hope this helps!

Categories