Stretching not applying in a WPF templated Button - c#

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)

Related

Make use of WindowStyle from resource dictionary and add Buttons to a ControlPresenter in an application

I make use of a resource dictionary with all my styles, icons and more.
Now I'd like to add my own titlebar implementing the title of the solution, my image and a ContentPresenter.
When using the WindowStyle I'd like to add application specific items inside this ContentPresenter, but I don't know how to proceed.
This is my WindowStyle. Inside the Grid you'll find the ContentPresenter I'd like to fill.
<Style TargetType="{x:Type Window}" x:Key="tkDarkWindowStyle">
<Setter Property="AllowsTransparency" Value="True"></Setter>
<Setter Property="Foreground" Value="{StaticResource tkBrandBlueBrush}"></Setter>
<Setter Property="Background" Value="{StaticResource exQuiteDarkBrush}"></Setter>
<Setter Property="WindowStyle" Value="None"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="BorderBrush" Value="{StaticResource exQuiteDarkBrush}"></Setter>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="80" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<DockPanel LastChildFill="True">
<Border Background="{TemplateBinding Background}" DockPanel.Dock="Top"
Height="80" x:Name="titlebar">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<Path DockPanel.Dock="Left" Margin="10" Stretch="Uniform" Fill="{TemplateBinding Foreground}" Data="{Binding Source={StaticResource tkPrimaryLogo}}" VerticalAlignment="Center">
</Path>
<Label Content="{TemplateBinding Title}" Foreground="{TemplateBinding Foreground}" Margin="10" DockPanel.Dock="Left" FontSize="26" VerticalAlignment="Center"/>
</DockPanel>
<ContentPresenter Grid.Column="1"/>
</Grid>
</Border>
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" Padding="4">
<ContentPresenter/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I used the style like this and tried to edit the template and add for example some buttons to the titlebar.
<Window.Resources>
<Style TargetType="{x:Type Window}" x:Key="newWindow" BasedOn="{StaticResource tkDarkWindowStyle}">
<!--here add application specific items? -->
</Style>
</Window.Resources>
How can I use the WindowStyle in my application and add specific items to the space i left at the titlebar?
Since the Window only has a single Content property, it makes no sense to include more than one <ContentPresenter /> element in the ControlTemplate.
You may want to create a custom class that inherits from Window and adds a dependency property called "TitleBarContent" or something. You can then add a ContentControl to the template that binds to this property:
<ContentControl Content="{TemplateBinding TitleBarContent}" />
You can set the value of the dependency property in a style setter as usual:
<Style TargetType="{x:Type local:YourWindowClass}" x:Key="newWindow" BasedOn="{StaticResource tkDarkWindowStyle}">
<Setter Property="TitleBarContent">
<Setter.Value>
<TextBlock>title...</TextBlock>
</Setter.Value>
</Setter>
</Style>

Auto scrolling on TextBox ControlTemplate

I'm using the TextBox below, which in order to apply a DropShadowEffect on its text, uses a ControlTemplate. I managed to get the TextWrapping to work, but once the TextBox fills up, it's content goes out of view. How do I replicate the Auto scrolling to the bottom feature of a native TextBox?
<TextBox TextWrapping="Wrap"
Foreground="LimeGreen"
Background="Black"
Margin="10,40,10,40"
FontSize="40"
HorizontalAlignment="Stretch"
x:Name="Inp"
FontFamily="Courier New"
CaretBrush='LimeGreen'>
<TextBox.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid x:Name="RootElement">
<ScrollViewer>
<ContentPresenter Content="{TemplateBinding Text}">
<ContentPresenter.Effect>
<DropShadowEffect ShadowDepth="4"
Direction="330"
Color="LimeGreen"
Opacity="1"
BlurRadius="5" />
</ContentPresenter.Effect>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property='TextWrapping'
Value='Wrap' />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Resources>
</TextBox>
This solution is a bit different that what you might expect. I think using the ContentPresenter is the wrong way because in the end you still want the functionality of the TextBox. So my solution focuses on getting rid of the border and focus indicator that mess up the drop shadow effect:
<TextBox x:Name="Inp"
Height="100"
HorizontalAlignment="Stretch"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0"
CaretBrush="LimeGreen"
FontFamily="Courier New"
FontSize="40"
Foreground="LimeGreen"
TextWrapping="Wrap">
<TextBox.Effect>
<DropShadowEffect BlurRadius="5"
Direction="330"
Opacity="1"
ShadowDepth="4"
Color="LimeGreen" />
</TextBox.Effect>
<TextBox.FocusVisualStyle>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate/>
</Setter.Value>
</Setter>
</Style>
</TextBox.FocusVisualStyle>
</TextBox>
I set the Background, BorderBrush to be transparent (=> no shadow). I removed the ContentPresenter; it's a 'regular textbox now. And to remove the focus border I set the FocusVisualStyle to an empty Template.

WPF how to attach different style to last child

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

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

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