WPF: How to use BasedOn Style - wpf

I am new to WPF and I created the following simple style example. But it doesn't work properly and button's content doesn't show although I can still click on it. Can anyone tell me why it is broken?
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Grid" x:Name="GridWithMarginStyle">
<Setter Property="Margin" Value="12"></Setter>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<EventSetter Event="Button.Click" Handler="ButtonHandler" />
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</StackPanel.Resources>
<Button Name="OkBtn">OK</Button>
<Button Name="CancelBtn" Click="CancelBtn_Click">Cancel</Button>
</StackPanel>

You are using the BasedOn property in the correct way. The problem is that your ContentPresenter is not binded to the control it renders (i.e. the button).
Just try to replace your ControlTemplate XAML with this one:
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
By using TemplateBinding you can bind the ContentPresenter to the Content property of your templated control.

Related

IsMouseOver Property on Button not working

I have this button and wanted to change the design if I hover over it with the mouse. It's not working and I'm not getting an error.
What am I doing wrong?
(I'm really new to WPF)
<Button MaxWidth="180"
Margin="5"
DockPanel.Dock="Top"
Padding="5"
FontSize="12"
Foreground="#1261AC"
FontWeight="SemiBold"
BorderBrush="Transparent">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="5" Background="LightGray" BorderThickness="1" Padding="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="#157ec4"/>
<Setter Property="Background" Value="#000000"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
The button itself is working but it is not chaning the color of the background or font.
(The colors in my example are just for testing)
The problem with your code is that you define the border-background to be gray.
Now you change the control background using a trigger.
However, the background that is set by the trigger is not yet related to the border background in your example.
I added a template binding that fixes this issue to you. Now the border in your template will always have the Background defined in your style, set by triggers or directly set in XAML.
PLEASE NOTE:
If you set the color in XAML by using <Button Background="Pink"/> this will overwrite the style and trigger attributes.
if you still want to overwrite the background property for a single button for some reason without overwritting the triggers you'll have to create a style based on the original style using the BasedOn Property:
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Propert="Background" Value="Yellow"/>
</Style>
try this piece of art:
ButtonStyle:
<Button Content="Hello there!"
MaxWidth="180"
Margin="5"
DockPanel.Dock="Top"
Padding="5"
FontSize="12"
Foreground="#1261AC"
FontWeight="SemiBold"
BorderBrush="Transparent">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="HotPink"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="5" Background="{TemplateBinding Background}" BorderThickness="1" Padding="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
<Setter Property="Background" Value="Lime" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>

ToolTip style Template Text not working

I'm trying to change the text color of ToolTips through a style template. But changing the ToolTip Foreground property doesn't change the color no matter what I do. I'm almost certain this is because I also have a TextBlock style that's overriding it.
When attempting to retemplate with a new textblock, it has no effect at all. I've spent all day yesterday fiddling with this issue and searching through threads and have found nothing.. Any contribution would be appreciated.
This is the style in my resource dictionary:
<!-- TextBlock -->
<Style TargetType="{x:Type TextBlock}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Foreground" Value="#B2FFFFFF"/> <!-- ToolTip text color overridden by this -->
</Style>
<!-- ToolTip -->
<Style TargetType="{x:Type ToolTip}" BasedOn="{StaticResource {x:Type ToolTip}}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Foreground" Value="DarkBlue"/> <!-- has no effect -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Grid>
<Border Name="Border"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
MinWidth="100"
MinHeight="30"
Margin="0,0,0,50"
Background="Beige"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="10"/>
<TextBlock Foreground="DarkBlue"/> <!-- has not effect -->
<ContentPresenter Margin="4"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I figured out one solution to this issue.
Removing the TargetType from this line:
<ControlTemplate TargetType="ToolTip">
to read as so:
<ControlTemplate >
and further down in the ControlTemplate, the textblock should be set like this:
<TextBlock
Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"
Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}">
</TextBlock>
This allows the text color of a tooltip to be set with the ToolTip's "Foreground" property and is not overridden by other TextBlock styles.

WPF style present different while apply for multiply control

I have two button with the code in xaml is:
<Button Grid.Row="0" Grid.Column="2" Style="{StaticResource styleInfomationButton}" />
<Button Grid.Row="1" Grid.Column="2" Style="{StaticResource styleInfomationButton}" />
They're same style, but in runtime they were presented differently:
Here the image how the form showed:
Style applied:
<Style x:Key="styleInfomationButton" TargetType="Button">
<Setter Property="Width" Value="22" />
<Setter Property="Height" Value="22" />
<Setter Property="Content">
<Setter.Value>
<TextBlock FontWeight="ExtraBold" Foreground="White">?</TextBlock>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="20" Height="20">
<Ellipse Fill="#81A9F0" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I tried the above code, it failed at runtime with the error:
Specified element is already the logical child of another element. Disconnect it first
Then, I looked at the code again and see you are setting Content property in the style. When you tried to apply the Style to two buttons, WPF tries to set the same TextBlock element as Content for the two buttons - this can't work. In WPF, for any element, you can only have one logical parent.
Update your style to the following, to get the desired behavior:
<Style x:Key="styleInfomationButton" TargetType="{x:Type Button}">
<Setter Property="Width" Value="22" />
<Setter Property="Height" Value="22" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="20" Height="20">
<Ellipse Fill="#81A9F0" />
<<TextBlock FontWeight="ExtraBold" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center">?</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Very good article on Understanding the Visual Tree and Logical Tree in WPF

How can I style certain WPF listboxes to be radio buttons?

I want to style some of my listboxes' items to be radiobuttons. Here is the code I have but this style gets applied to every listboxitem.
<Window.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
How can I do something so I can designate a listbox to have radio buttons. I imagine the designation would go something like this:
<ListBox Name="ListBox1" Width="120" Visibility="Visible" Background="{x:Null}" BorderThickness="0" Style="{StaticResource radioListBox}">
I know part of the problem is that this only styles listboxitems but I am not sure how to style the listbox itself. I would of course prefer to add in the background and border properties.
Any help would be appreciated.
You need to give your Style a key:
<Window.Resources>
<Style x:Key="radioListBoxItem" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And then you would apply it to a ListBox like:
<ListBox ItemContainerStyle="{StaticResource radioListBoxItem}" />
If you want to create a style for a ListBox that contains the radio list box items, and some other properties, you could do that too:
<Style x:Key="radioListBox" TargetType="{x:Type ListBox}">
<Setter Property="ItemContainerStyle" Setter="{StaticResource radioListBoxItem}" />
<Setter Property="Background" Value="Navy" />
</Style>
And you'd apply it:
<ListBox Style="{StaticResource radioListBox}" />

WPF templating/styling issue

Given this piece of XAML
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<DockPanel>
<Border DockPanel.Dock="Top">
<Border.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground"
Value="Red" />
</Style>
</Border.Resources>
<ContentPresenter ContentSource="Header" />
</Border>
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top"
Header="GroupBox header"
DockPanel.Dock="Top">
...
...
I would like to know why the group box header is not displayed in red letters.
I've already tried styling the Label type with no success either.
(sorry about the overly generic post title... I wasn't able to think of something more meaninful)
This code solved the problem:
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="Label">
<Style.Setters>
<Setter Property="Foreground" Value="Red" />
</Style.Setters>
</Style>
</DataTemplate.Resources>
<Label Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top" Header="GroupBox header" DockPanel.Dock="Top">
...
...
However, I still don't know why the proposed code didn't worked.
It seems that the ContentPresenter doesn't use TextBlock to show the string you provide as header or explicitly sets its style, so the style you defined cannot be applied.
If you are certain that you will only use text as group box header, you can remove the ContentPresenter and use a TextBlock on your own.
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<DockPanel>
<Border DockPanel.Dock="Top">
<Border.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Border.Resources>
<TextBlock Text="{TemplateBinding Header}"></TextBlock>
</Border>
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top"
Header="GroupBox header"
DockPanel.Dock="Top"/>
</DockPanel>
try this:
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}" >
<Setter Property="Foreground" Value="Red" />
</Style>
</DockPanel.Resources>
You don't need a templet for this. But if you demand on using a Templete, you probably have to set the Groupbox.HeaderTemplet not the GroupBox.Templet.
Edit:
This is what i got so far, but i keep getting an XamlPraseException.
<Style TargetType="{x:Type GroupBox}" >
<Setter Property="HeaderTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="{TemplateBinding GroupBox.Header}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources