I'm using a ControlTemplate for defining the appearance of my buttons in a WPF application. Additionally I would like to use styles to set certain aspects of my buttons. These styles should set properties on elements defined in the ControlTemplate, like (simplified):
<Window.Resources>
<ControlTemplate x:Key="Template1" TargetType="Button">
<Grid>
<Rectangle Name="rect" Fill="White" Stroke="Blue" StrokeThickness="2"/>
<TextBlock Name="text" Text="Hallo" Foreground="Red" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
<Style x:Key="Style1" TargetType="Button" >
<Setter TargetName="rect" Property="Fill" Value="Red"/>
</Style>
</Window.Resources>
Now the compiler complains that the TargetName "rect" isn't a valid target which I can understand since an untemplatized Button doesn't contain an element named "rect".
I know that I could change the style to setting the complete template, but I would like to avoid that (because the template is much more complex than shown here and I do not want to duplicate it for each style...)
Is it possible to achieve this behaviour? Perhaps by setting the TargetType right? Any other ideas?
You can NOT override only part of the control template. You can either change everything or nothing.
You can have setters for properties on the style though.
The standard pattern is to use TemplateBinding in the control template to bind to properties of the control itself, and then set the properties on the control in the style. For example:
<Window.Resources>
<ControlTemplate x:Key="Template1" TargetType="Button">
<Grid>
<Rectangle Name="rect" Fill="{TemplateBinding Background}" Stroke="Blue" StrokeThickness="2"/>
<TextBlock Name="text" Text="Hallo" Foreground="Red" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
<Style x:Key="Style1" TargetType="Button" >
<Setter Property="Background" Value="Red"/>
</Style>
</Window.Resources>
This will bind the Fill property on rect to the Background property on the Button. The style will set the Background property to Red, which will cause the Fill to be set to Red.
In order to set defaults, you would normally create a style that set the template as well as the other properties:
<Window.Resources>
<Style x:Key="BaseStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Rectangle Name="rect" Fill="{TemplateBinding Background}" Stroke="Blue" StrokeThickness="2"/>
<TextBlock Name="text" Text="Hallo" Foreground="Red" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="White"/>
</Style>
<Style x:Key="Style1" TargetType="Button" BasedOn="{StaticResource BaseStyle}">
<Setter Property="Background" Value="Red"/>
</Style>
</Window.Resources>
The first style will apply the template and set Background to White, so the rectangle will be white. The second style inherits from the first one but sets the Background to Red, so the rectangle will be red.
Related
I have a ContentControl like this:
<ContentControl>
<userControls:TestControl/>
</ContentControl>
OR like this [when i have PRISM system]:
<ContentControl prism:RegionManager.RegionName="TestView"/>
I see the final UserControl well until this step when i start the program.
In the above samples the Content type is UserControl. Now i want give a ControlTemplate to this ContentControl. Then i created a style named StyleTest and used it in my Xaml:
<ContentControl Style="{StaticResource StyleTest}"> .....
My style:
<Style x:Key="StyleTest" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="10">
<Border CornerRadius="10" BorderBrush="#ffffffff" BorderThickness="5">
<StackPanel>
<ContentPresenter Content="{Binding}"/>
<TextBlock>Some additional text to test template</TextBlock>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But when i start the program the UserControl can not be seen and i just see this text and a border around it: Some additional text to test template
What i must write instead of above line of code to see my UserControl again with a white border around it?
Why the UserControl not showing with above code (above style)?
There are 3 ways to do this.
- by setting the ContentTemplate
- by setting the Template
- or using the Border directly and apply a style.
In this case I would use the Border and apply a style because it looks like the ContentControl is only used to do add a styled Border.
<StackPanel>
<StackPanel.Resources>
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="5" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="Margin" Value="10" />
</Style>
<Style x:Key="ContentTemplateStyle" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border Style="{StaticResource BorderStyle}">
<!-- Bind to the DataContext of the DataTemplate which is the Content of the ContentControl -->
<!-- <ContentPresenter Content="{Binding}" />-->
<!-- TemplateBinding improves performance -->
<ContentPresenter Content="{TemplateBinding Content}" />
<!-- Using the TemplatedParent -->
<!--<ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"/>-->
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ControlTemplateStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border Style="{StaticResource BorderStyle}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ContentControl Style="{StaticResource ContentTemplateStyle}">
<Button>ContentTemplateStyle</Button>
</ContentControl>
<ContentControl Style="{StaticResource ControlTemplateStyle}">
<Button>ControlTemplateStyle</Button>
</ContentControl>
<Border Style="{StaticResource BorderStyle}">
<Button>BorderStyle</Button>
</Border>
</StackPanel>
datacontext is a type?. Normally it will be inherited in the visual tree so I think it should be left blank.
Content is the content of the parent. Normally you can use a template binding or set the content source property.
but using this template will only show you a white border around content. The original template is lost. So you should provide the entire template for the control.
Now maybe the control contains margin and border properties and you can set those from your style and leave the original template in place.
I found my mistake place and changed this part of my codes:
<Style x:Key="StyleTest" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
To this one:
<Style x:Key="StyleTest" TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
When i play with Template its effects is on inner content of the ContentControl (in this question: template of the UserControl) But when i play with ContentTemplate, its effects is on ContentControl layout.
ContentPresenter tag is same and it works now...
<ContentPresenter Content="{Binding}"/>
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.
For example, i have the following Style:
<Style x:Key="MyStyle" TargetType="MyType">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MyType">
<Grid>
<Button x:Name="MyButton"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Is it possible to inherit from him a different style and change the properties of the button "MyButton"? For example change the property Visibility?
Thank You!
There are more than one way of achieving this.
Use BasedOn
<Style TargetType="MyChildType" BasedOn="{StaticResource MyStyle}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MyType">
<Grid>
<Button x:Name="MyButton" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Apply style to MyButton and put Trigger in Style.Triggers to change the properties of button depending on the properties of parent
I've overriden the Template of TextBox to give it a rounded border:
<Style TargetType="TextBox">
<Setter Property="b:TextBoxBehaviours.SelectAllTextOnFocus" Value="True" />
<Setter Property="Width" Value="75" />
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="brdTxtBx" BorderThickness="2" CornerRadius="3" BorderBrush="{x:Static SystemColors.ActiveBorderBrush}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What I'm trying to achieve is that when the TextBox gets a validation error, the Border should change colour to a different one (e.g. Red). I've tried putting a DataTrigger in the Border style, but that doesn't seem to work.
<Border x:Name="brdTxtBx" BorderThickness="2" CornerRadius="3" BorderBrush="{x:Static SystemColors.ActiveBorderBrush}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBox}, Path=Validaton.HasError}" Value="True">
<Setter Property="BorderBrush" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
I've tried changing the Mode on the binding to see if that works, but doesn't seem to have any affect. I've also changed the DataBinding path to bind to another property (i.e. the Text value), to see if that triggers it, so I think it's more my understanding of DataTriggers in ControlTemplates isn't quite right?
Am I approaching this incorrectly, or just slightly off?.
Use the ControlTemplate.Triggers section to change properties of elements inside the template dynamically. Use Setter.TargetName to specifiy the named element to target:
<ControlTemplate>
<Border x:Name="brdTxtBx" BorderThickness="2" CornerRadius="3"
BorderBrush="{x:Static SystemColors.ActiveBorderBrush}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="brdTxtBx" Property="BorderBrush" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
The usual way to achieve your requirements is to use the Validation.ErrorTemplate Attached Property. From the linked page on MSDN:
If the user enters an invalid value, you may want to provide some feedback about the error on the application user interface (UI). One way to provide such feedback is to set the Validation.ErrorTemplate attached property to a custom ControlTemplate.
<TextBox Name="textBox1" Width="50" FontSize="15"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textBoxInError}"
Grid.Row="1" Grid.Column="1" Margin="2">
...
Again, from the linked page:
The following example shows the custom ControlTemplate validationTemplate that creates a red exclamation mark to notify the user of a validation error. Control templates are used to redefine the appearance of a control:
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
Please see the linked page for more information.
I created a button. My basic requirements are rounded thicker border, with more than one color (i.e. for Buy/Sell buttons)
I was hoping that i could create the template once, and than just override the border brush like this:
<Style x:Key="BorderButton">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderThickness="2"
BorderBrush="Red"
CornerRadius="3"
Background="{x:Null}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="GreenBorderButton" BasedOn="{StaticResource BorderButton}" TargetType="{x:Type Button}">
<Setter Property="BorderBrush" Value="Green" />
</Style>
but they both produce the same style.
Do i need to write out the whole template every time? seems like unnecessary code repetition (especially if 3-4 colors are desired). Hoping there is some way to inherit a template.
Your code is very close to working; the issue is that GreenBorderButton is applying the BorderBrush to the button itself, not the Border in the overridden Template.
To fix this, simply change the Border's BorderBrush to use the parent Button's BorderBrush. You can do this using a TemplateBinding like so:
<Style x:Key="BorderButton">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border"
BorderThickness="2"
BorderBrush="{TemplateBinding Property=BorderBrush}"
CornerRadius="3"
Background="{x:Null}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then, you can either use the same overridden styles like you have, or you could simply do:
<Button Style="{StaticResource BorderButton}" BorderBrush="Blue" Content="Blue" />