TemplateBinding not working with .NET framework objects - wpf

I am new to using the ControlTemplate. I am writing my first control but I am having (what seems to me) a very strange issue.
Any dependency properties that I make TemplateBinding to work, but any properties from the .NET framework objects i.e. the Content property of a ContentControl or the Items property of an ItemsControl does not get populated at runtime.
I am sure I am missing something... Just what it is I dont know...
An example of the code is below:
The class is very simple at the moment:
public class Title : ContentControl
{
}
And the Template is:
<Style TargetType="{x:Type UI:Title}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UI:Title}">
<TextBlock Text="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The base ContentControl class is the .NET class located in the System.Windows.Controls.Control namespace.
Thanks,
Adam

I believe if you'd like to override where the Content is placed you can do that using a ContentPresenter.
<Style TargetType="{x:Type UI:Title}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UI:Title}">
<Label>
<ContentPresenter />
</Label>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Note I've also changed from a TextBlock to a Label as I believe the TextBlock.Text property will not accept everything from ContentControl.Content. Here is an example I put together that works as intended:
<Window x:Class="ContentControlTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ContentControlTest"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type local:Title}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Title}">
<Button>
<ContentPresenter />
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<local:Title>
<TextBlock Text="Happy Days!" />
</local:Title>
</Window>

You may need to implement the INotifyPropertyChanged interface on your objects and INotifyCollectionChanged on your collections.

Related

Using a Button control in a ButtonBase derived template

We have a specific look and feel to buttons in our application and these are defined in a named style for ButtonBase which also happens to be the default style for Button, ToggleButton and RepeatButton.
We also have ToolbarButtons which are derived from ButtonBase and include extra properties such as Text and Icon. These are used to place a specific text and an icon on a ToolbarButton.
The theme for ToolbarButtons is defined as follows in Themes/generic.xaml:
<Style TargetType="{x:Type c:ToolbarButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:ToolbarButton}">
<Button Command="{TemplateBinding Property=Command}">
.. controls to place text and icon etc ..
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see I'm using a Button control within a control based on ButtonBase and binding the Command to the Command on the contained Button. This hack makes sure that I can override the style of the 'Button' used in the Toolbar by defining the Button default style.
It all seems to work quite well, but it does not feel right using a Button inside a Button. I'm still wondering if I'm doing the right thing. Any ideas?
Your approach is good but you don't have to use a second button in your template, but a ContentPresenter. Use it every time your redefine the template of a ContentControl (a button for example). The BasedOn attribute is useful to override the specific button style.
It looks like this :
<Style TargetType="{x:Type c:ToolbarButton}" BasedOn="{StaticResource {x:Type ButtonBase}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ToolbarButton}">
<StackPanel>
<!-- Image -->
<Image Source="{TemplateBinding Image}"/>
<!-- Content -->
<ContentPresenter Content="{TemplateBinding Content}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You will find the same mechanism with the templates of the ItemsControl (ListBox, ListView, etc) with the ItemsPresenter.
UPDATE:
To take your comment in account, maybe you should override a ContentControl to apply your specific chrome to it:
public class ButtonContentControl : ContentControl { }
<Style TargetType="{x:Type local:ButtonContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ButtonContentControl}">
<!-- Specific chrome -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then you can use it inside the templates of your ButtonBase and your ToolbarButton :
<
Style TargetType="{x:Type ButtonBase}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:ToolbarButton}">
<ButtonContentControl>
<!-- ContentPresenter or something else -->
</ButtonContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type c:ToolbarButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:ToolbarButton}">
<ButtonContentControl>
.. controls to place text and icon etc ..
</ButtonContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Possible to create an implicit style that is used, by default, as the base for other styles?

I'm trying to figure out a way to create an implicit style for a custom control, that will be inherited by other styles applied to the control.
For example, let's say I have a control FancyButton. Then I can define an implicit style by putting this in my app resources:
<Style TargetType="my:FancyButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:FancyButton">
<TextBlock Text="hello world" Foreground="{TemplateBinding Foreground}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then users of the control will see "hello world" in green just by writing:
<my:FancyButton Foreground="Green" />
The problem is, if someone wants to define a green-colored style like this ...
<Style TargetType="my:FancyButton" x:Key="GreenButton">
<Setter Property="Foreground" Value="Green" />
</Style>
<my:FancyButton Style="{StaticResource GreenButton}" />
... then they will have lost the implicit style's control template, and will not see the "hello world".
Of course, you can define a base style, which the implicit style inherits ...
<Style TargetType="my:FancyButton" x:Key="FancyButtonDefaultStyle">
<!-- ... -->
</Style>
<Style TargetType="my:FancyButton" BasedOn="{StaticResource FancyButtonDefaultStyle}">
</Style>
... and that way users of the control can inherit the default style:
<Style TargetType="my:FancyButton" x:Key="GreenButton" BasedOn="{StaticResource FancyButtonDefaultStyle}">
<Setter Property="Foreground" Value="Green" />
</Style>
But it seems awkward to force users of the control to remember to inherit a default style based on a key. Isn't there some way to make a custom control's styles inherit the implicit style, in the same way that the framework controls do?
Ie, I want my FancyButton to behave in the same way a Button does, where I can define a style that doesn't wipe out the other default style properties (esp. ControlTemplate):
<Style TargetType="Button" x:Key="GreenButton">
<Setter Property="Foreground" Value="Green" />
</Style>
You need to put the implicit style definition
<Style TargetType="my:FancyButton">
<!-- etc -->
</Style>
in the special "Themes/Generic.xaml" resource dictionary (in the same assembly as where the control class is defined). This makes all styles inherit from the implicit style, as you wanted.
Sorry for quoting. :)
Hope this helps.
Here's my code:
MainPage.xaml:
<UserControl x:Class="SilverlightApplication4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightApplication4"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.Resources>
<ResourceDictionary>
<Style TargetType="local:TemplatedControl1"
x:Key="myCustomTemplate">
<Setter Property="Foreground"
Value="Purple" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot"
Background="Yellow"
Width="500"
Height="500">
<local:TemplatedControl1 Style="{StaticResource myCustomTemplate}" />
</Grid>
</UserControl>
Now in TemplatedControl.cs don't forget to put this:
this.DefaultStyleKey = typeof(TemplatedControl1);
And finally put your implicit style definition in Themes\Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SilverlightApplication4">
<Style TargetType="local:TemplatedControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TemplatedControl1">
<TextBlock Text="hello world"
Foreground="{TemplateBinding Foreground}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
With this method, the result should be purple text in a terrible yellow background. :)
Thanks to #Tanis83 for pointing out the (very simple) answer. I just needed to put the implicit style ...
<Style TargetType="my:FancyButton">
<!-- etc -->
</Style>
in the special "Themes/Generic.xaml" resource dictionary (in the same assembly as where the control class is defined). This makes all styles inherit from the implicit style, as I wanted.

How to pass properties to WPF Style

I'm trying to write a reusable Template for a WPF ItemContainerStyle.
This Template changes the way the TabControl's Item looks.
This template is meant to be used in several places in the application.
In each place it is used I want to be able to pass different parameters to it.
For example: to change the Margin of the Border of the Item:
<Style x:Key="TabItemStyle1" TargetType="{x:Type TabItem}">
<Setter Property="Margin" Value="10,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="Bd" Width="80"
Background="Gray"
Margin="{TemplateBinding Margin}">
<ContentPresenter x:Name="Content"
ContentSource="Header" />
</Border>
</Grid>
<ControlTemplate.Triggers>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<TabControl ItemContainerStyle="{DynamicResource TabItemStyle1}">
In the place where the style is used I Would like to Write something like:
ItemContainerStyle="{DynamicResource TabItemStyle1 Margin='5,0'}"
or
<TabControl Margin="78,51,167,90" ItemContainerStyle="{DynamicResource TabItemStyle1}"
ItemContainerStyle.Margin="5,0">
The motivation is to use this template in different places with different Margins.
Is there a way to do this ?
Thank you
You can do it with attached properties. I wrote a blog post explaining how to do it:
http://www.thomaslevesque.com/2011/10/01/wpf-creating-parameterized-styles-with-attached-properties/
Another option is to use DynamicResource, and redefine the resource in derived styles
OK, I've found a way to do this with dave's help.
The Solution is to create a derived template and set the properties in it.
This way the original template can be reused.
<Style x:Key="TabItemStyle2" TargetType="{x:Type TabItem}"
BasedOn="{StaticResource TabItemStyle1}">
<Style.Setters>
<Setter Property="Margin" Value="40,0"></Setter>
</Style.Setters>
</Style>
And set the TabControl's ItemContainerStyle to the derived style:
<TabControl ItemContainerStyle="{DynamicResource TabItemStyle2}">
In my case I had to change some parameters deep in the applied template (so I couldn't use just a setter).
And I didn't want to code some classes that traverse the visual tree or register an attached property to do the changes.
However, it is possible to define resources within the base style and override these values in the derived definitions. So, with the original example this would look like this:
<Style x:Key="AbsTabItemStyle" TargetType="{x:Type TabItem}">
<!-- Override these default values in derived style definitions -->
<Style.Resources>
<s:Double x:Key="GridBorderMargin">10</s:Double>
<Color x:Key="GridBorderColor">Grey</Color>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="Bd"
Width="80"
Background="{DynamicResouces GridBorderColor}"
Margin="{DynamicResouces GridBorderMargin}"
>
<ContentPresenter x:Name="Content"
ContentSource="Header" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BigMarginTabItemStyle" TargetType="{x:Type TabItem}" BasedOn="{StaticResource AbsTabItemStyle}">
<!-- Set different values in this derived style definition -->
<Style.Resources>
<s:Double x:Key="GridBorderMargin">20</s:Double>
</Style.Resources>
</Style>
<Style x:Key="RedTabItemStyle" TargetType="{x:Type TabItem}" BasedOn="{StaticResource AbsTabItemStyle}">
<!-- Set different values in this derived style definition -->
<Style.Resources>
<c:Color x:Key="GridBorderColor">Red</Color>
</Style.Resources>
</Style>
A way of solving it is by adding a Margin property to the objects/ViewModels you want to display and (data)bind to that value in the template.
As far as I know there is no support of parameterized styles/templates.

Global button cursor

Is there a way to set the default cursor for a control type at the application level? I'd like to say that all Button controls, regardless of whether or not they have a specific style, have a default cursor of the hand cursor unless it's overridden in that button's individual style specification.
Here's an example of such a button with its own style that I'd like to override the default
<UserControl>
<UserControl.Resources>
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<!-- My button's custom content here -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button x:Name="btnClose" Style="{DynamicResource CloseButtonStyle}"/>
</UserControl>
Put the style below in Application.Resources in your App.xaml file.
<Style TargetType="Button">
<Setter Property="Cursor" Value="Hand"/>
</Style>
UPDATE
In regards to the 3rd comment:
To achieve that, you need to leave just your control template in UserControl.Resources:
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<!-- My button's custom content here -->
</Grid>
</ControlTemplate>
Then set Template property for Button:
<Button Template="{DynamicResource CloseButtonTemplate}"/>

Define new ControlTemplate keeping the original events

I want to use the ExtendedWPFToolkits's ColorPicker but with a custom ButtonStyle.
I can create a new look overriding the Template property of the item but the original templates click event is missing.
I want to keep it, but how?
<Controls:ColorPicker >
<Controls:ColorPicker.ButtonStyle>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button Content="ColorPicker"></Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Controls:ColorPicker.ButtonStyle>
</Controls:ColorPicker>
What you have is not valid. You are putting a Button in the ControlTemplate of a ToggleButton, so basically a button in a button.
You'd need to do something like:
<Controls:ColorPicker >
<Controls:ColorPicker.ButtonStyle>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent">
<TextBlock Text="ColorPicker" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Controls:ColorPicker.ButtonStyle>
</Controls:ColorPicker>
I added a transparent Border so the button will be able to receive mouse events for areas not covered by the text.

Resources