WPF ControlTemplate AND DataTemplate - c#

I have ListView where I would like to apply a custom ControlTemplate to it's items. It is defined like this:
<ListView ItemsSource="{Binding MyAwesomeItems}" ...
MyAwesomeItems holds different classes. So I thought to myself: "Well, hello DataTemplates."
To make the contained items look the way I want them to, I have defined a ControlTemplate like this:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border><ContentControl Content="{TemplateBinding Content}"/></Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
I have used ContentControl with Binding to TemplateBinding Content. I expected that WPF would then insert my items inside that ContentControl, using whatever DataTemplate I have defined for it.
But instead, it looks like WPF just uses the items .ToString() and does not apply any DataTemplates. Is this intended behaviour?
What I want to achieve is: Have a list of items, where the container of each item looks exactly the way I want and the content of that container comes from the DataTemplate.

In a ControlTemplate for a ContentControl you usually use an empty ContentPresenter tag. In your case:
<ControlTemplate TargetType="ListViewItem">
<Border>
<ContentPresenter/>
</Border>
</ControlTemplate>
The ContentPresenter has a ContentSource property which defaults to "Content" and sets all the necessary properties (Content, ContentTemplate, etc.).
See here for details.

Related

Edit wpf control template but use original styles

Sometimes when I'm editing a copy of the controls original template I don't need to change the original styles and colors, and would like to reference the original ones directly.
For example, I wanted to change the ComboBox template to add some filtering buttons in the drop down, its toggle button refers to a Style that is also copied into the file. I would like to refer to the original style so my XAML isn't overly cluttered.
Edit:
So here is part of the XAML code that is created when you choose to edit a copy.
The ControlTemplate is what I want to change, but I don't need the ComboBoxToggleButton Style, and so for the toggleButton I'd like to set its style to the one the ComboBoxToggleButton Style was copied from. Is there some namespace that they are all stored in, or are they inaccessible?
<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
...
</Style>
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
...
<ToggleButton x:Name="toggleButton" ... Style="{StaticResource ResourceKey=ComboBoxToggleButton}"/>
</Grid>
</ControlTemplate>
And approximately what I'd like it to be like
<Window xmlns:baseStyles="{namespace/url to the default wpf styles}">
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
...
<ToggleButton x:Name="toggleButton" ... Style="{StaticResource ResourceKey=baseStyles:ComboBoxToggleButton}"/>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
Right, so Combobox isn't your basic bare templated control. Within it's ControlTemplate is a unique ToggleButton (hence the additional instance-specific Style template for it) which it requires. Once you introduce a new ControlTemplate than that's now all it knows. It CAN NOT reference a Style template inside of the original ControlTemplate since it's not a resource available outside of it. Style and ControlTemplate are different beasts.
You have two options. Either you take that unique ToggleButton Style Template and put it somewhere it can be reached as a StaticResource and ref it on the ToggleButton instance inside your ControlTemplate via the normal <ToggleButton Style="{StaticResource ComboBoxUniqueToggleButtonStyleKeyNameYouGiveIt}" ..../>
(Like if it were in a resource dictionary, except then it's loaded all the time which generally isn't necessary).
Or, you can embed it directly in your ControlTemplate just like they do in the default style/controltemplate for ComboBox.
You can inherit parts of a Style template via BasedOn but you can only have one ControlTemplate at a time.
Hope this helps, and I'll retract my duplicate vote.
To reuse the default WPF style to ComboBox, use:
<Style TargetType="ComboBox">
<!-- Setters in need of change -->
</ Style>
If you want to inherit from a Style you created yourself, you can use:
<Style TargetType="ComboBox" BasedOn="{StaticResource YourExistentStyle}">
<!-- Setters that need to change -->
</ Style>

Creating style resource for custom control removes template

I've got a custom control in WPF, which has a variety of dependency properties that allow visual customization. For the sake of brevity I won't post the entire control, but it basically is setup like this:
<UserControl.Resources>
<Style TargetType="{x:Type MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyControl}">
<Border BorderBrush="{TemplateBinding BorderColor}">
// more stuff here
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter>
</Setter>
</Style>
</UserControl.Resources>
The BorderColor property works fine if I set it directly, like this:
<ctl:MyControl BorderColor="Brushes.Red">....</ctl:MyControl>
But I want to set it application-wide. The problem I have is if I simply set the style with no key, it does not apply. Like this:
<Window.Resources>
<Style TargetType="{x:Type ctl:MyControl}">
<Setter Property="BorderColor" Value="Brushes.Red"/>
</Style>
</Window.Resources>
This does not do anything to the control. So I thought I'd just set a key and apply that style, like this:
<Style TargetType="{x:type ctl:MyControl}" x:Key="myStyle">....</Style>
<ctl:MyControl Style="{StaticResource myStyle}">.....</ctl:MyControl>
But this causes the control to vanish, I'm assuming because it's removing the Template. What am I doing wrong? With other framework controls you can just set the properties you want without losing the control template.
You need to inherit from the default style you have created.
inherit style from default style

Apply a WPF style to a Border within a UserControl

I have a very simple UserControl, which just consists of a TextBlock contained within a Border element.
Is there a way of applying a style to the TextBlock within the UserControl from the containing window.
I know that I can create a style with..
<Style TargetType='TextBlock'>
But this applies to all TextBlocks within my window, not just the ones inside my UserControl
So I want to be able to say something like...
<Style TargetType='MyUserControl.TextBlock'>
Thanks,
Rich.
PS. this is a simplified example of what I'm trying to do!
ADDITIONAL NOTE
As I was driving home this evening, this was rattling around inside my head, and I thought of a possible solution.. and that's to create a basic sub-class of the TextBlock control, and call it MyTextBlock.. so just have a definition like
public class MyTextBlock : TextBlock { }
Then, within the usercontrol, use 'MyTextBlock' rather than 'TextBlock'. This will allow me to apply a style to a type of 'MyTextBlock'. Bingo !!!
Maybe this isn't the tidiest way of doing this, but it's very little code, and it works.
However, as I'm fairly new to WPF, I'm quite interested in a more standard way of acheiving this.
One option if you want to apply a Style to all TextBlocks inside MyUserControl is this
<Style TargetType="{x:Type my:MyUserControl}">
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Style.Resources>
</Style>
And if you want to add another Style for MyUserControl you just have to base it on the default Style
<Style x:Key="myStyle" TargetType="{x:Type my:MyUserControl}"
BasedOn="{StaticResource {x:Type my:MyUserControl}}">
<!-- ... -->
</Style>
Otherwise, if you want to be able to set a Style for some controls inside MyUserControl you can use DependencyProperties. For a TextBlock you can have a style called TextBlockStyle for example. The TextBlock will bind to this Style and you can set the Style from your window (or wherever you use it). This can also be seen in some controls in the framework, AutoCompleteBox in the toolkit for example
<toolkit:AutoCompleteBox>
<toolkit:AutoCompleteBox.TextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="MaxLength" Value="10"/>
</Style>
</toolkit:AutoCompleteBox.TextBoxStyle>
</toolkit:AutoCompleteBox>
MyUserControl.xaml
<Border BorderThickness="1">
<TextBlock Style="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Path=TextBlockStyle}"
Text="Test"/>
</Border>
MyUserControl.xaml.cs
public partial class MyUserControl : UserControl
{
public static DependencyProperty TextBlockStyleProperty =
DependencyProperty.Register("TextBlockStyle",
typeof(Style),
typeof(MyUserControl));
public MyUserControl()
{
InitializeComponent();
}
public Style TextBlockStyle
{
get { return (Style)GetValue(TextBlockStyleProperty); }
set { SetValue(TextBlockStyleProperty, value); }
}
}
And then you set the Style when you declare your instance in markup for example
<my:MyUserControl>
<my:MyUserControl.TextBlockStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Green"/>
</Style>
</my:MyUserControl.TextBlockStyle>
</my:MyUserControl>
Update
To set this on resource level you can add a default style for MyUserControl, in the window resources or App.xaml for example
<Window.Resources>
<Style TargetType="{x:Type my:MyUserControl}">
<Setter Property="TextBlockStyle">
<Setter.Value>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Green"/>
</Style>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
There are a few things you can change.
First, you can use Styles with a resource key. So you would write:
<Style x:Key="myTextStyle" TargetType="{x:Type TextBlock">
Then in order for a TextBox to have this style applied to it, it would need to specify:
<TextBlock Style="{StaticResource myTextStyle" />
If you do not want to modify the UserControl and you just want to apply the style in an element that is nesting the UserControl inside it, keep in mind that you can declare styles in the resource dictionary of a nested element. When you place your UserControl, consider doing this:
<local:UserControl>
<local:UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
...
</Style>
</local:UserControl.Resources>
</local:UserControl>
There are 2 ways I can think of doing this:
One is in code in the containing window by explicitly setting the style
MyUserControl.TextBlockName.Style = FindResource("TextBlockStyle") as Style;
The other way would be to create a DependencyProperty to hold the Textblock style and apply the style on the TextBlock element when the DependencyProperty is changed.
if you have something like this in your xaml:
<xmlns:local = "myCustonUserControl">
you can use this as a style definition:
<Style TargetType="{x:Type local:MyUserControl}">
hope that helps!

Issue with applying style on WPF UserControl

I have a user-control and I want to use it in some other project. There is no problem when I set some value to its properties directly:
<local:MyUserControl prop1="val1" prop2="val2">
...
</local:MyUserControl>
But I can't apply a style to it. I tried:
<Window ...>
<Window.Resources>
<Style x:Key="MyUserControlStyle" TargetType="{x:Type local:MyUserControl}">
<Setter Property="prop1" Value="val1"/>
<Setter Property="prop2" Value="val2"/>
</Style>
</Window.Resources>
<Grid>
<local:MyUserControl Style="{StaticResource ResourceKey=MyUserControlStyle}">
...
</local:MyUserControl>
</Grid>
</Window>
Where did I wrong? -Thanks
Using dear #Mario Vernari's instructions, I found it out that the problem was due to a bad strategy which I'd used to create my UserControl. I wanted to create a UserControl that be able to hold some other ones. So I had tried this:
<UserControl x:Class="MyNamespace.MyUserControl"
...
Style="{DynamicResource ResourceKey=MyUserControlStyle}">
<UserControl.Resources>
...
<Style x:Key="MyUserControlStyle" TargetType="{x:Type UserControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserControl}">
<Border BorderBrush="{Binding Path=DP1}">
...
<ContentPresenter ... Content="{TemplateBinding Content}"/>
...
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
</UserControl>
Where DP1 is a dependency property of type Brush. The UserControl which has been created through this way works if you set its properties (like DP1) directly. Absolutely this is not the true way as #Mario told me:
...When you use an UserControl, it means that you already know its layout, and there is no need to style the control further. You are defining its style twice at the same time thus results a collision...
And he added:
Instead, you should use a CustomControl; Define the default style in the Themes folder (if you own regular Visual Studio, it makes automatically). Afterward, you may override the default style in your own app. In the same way you would do for a normal base class and its derived.
Follow this:
http://www.codeproject.com/KB/WPF/WPFCustomControl.aspx ...
Obviously, in this case we need to derive our lookless control from ContentControl class (instead of Control class). You may take a look at this & this to master the details.
Here, I give thanks to #Mario again. ;)
You are giving Style="{StaticResource ResourceKey=MyUserControlStyle}".
It's just - Style="{StaticResource MyUserControlStyle}".

How can I swap controls based on a dependency property?

I am creating a custom control.
I want the template for this control to use different controls for the root control based on the value of a dependency property called CanExpand. CanExpand is defined in the custom control class.
If CanExpand is true, I want to display using an Expander:
<ControlTemplate ...>
<Expander ...>
<!--...-->
<ContentPresenter/>
</Expander>
</ControlTemplate>
If CanExpand is false, I want to display using a HeaderedContentControl instead:
<ControlTemplate ...>
<HeaderedContentControl ...>
<!--...-->
<ContentPresenter/>
</HeaderedContentControl>
</ControlTemplate>
I thought of using a DataTemplateSelector, but this is a ControlTemplate not a DataTemplate and there is no selector property for the Template of a control.
I can't set the different controls to visible/hidden with a Trigger because the child content can only live under one control. Also, I don't think you can change the Content property using a Trigger.
Any suggestions?
Thanks.
Inside your Style, set the ControlTemplate property for the default state and then have a Trigger which sets the ControlTemplate property to a different template. For example:
<Style ...>
<Setter Property="ControlTemplate">
<ControlTemplate ...>
</ControlTemplate>
</Setter>
<Style.Triggers>
<Trigger Property="YourProperty" Value="WhateverValue">
<Setter Property="ControlTemplate">
<ControlTemplate ...>
</ControlTemplate>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Keep in mind you can have a trigger on the same property for multiple values, each value getting a completely different template.

Categories

Resources