I'd like to create a UserControl (in this case a square-Button with defined Backgroundcolors) which can host it's own content.
UserControl:
<UserControl x:Class="SGDB.UI.Controls.ModernButton"
xmlns:local="clr-namespace:SGDB.UI.Controls"
xmlns:converter="clr-namespace:SGDB.UI.Converter"
x:Name="_modernButton">
<Button>
<Button.Resources>
<converter:EnumToColorConverter x:Key="ColorConverter"/>
</Button.Resources>
<Button.Template>
<ControlTemplate>
<Border Width="{Binding Size, ElementName=_modernButton}" Height="{Binding Size, ElementName=_modernButton}" BorderBrush="Black" BorderThickness="0.8,0.8,3,3">
<Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}">
<ContentPresenter/>
</Grid>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
Now, as you may expect it, if I use this Control inside my MainView everthing works just fine until I define some Content.
Using:
<control:ModernButton Size="200" BackgroundColor="Light">
TEST
</control:ModernButton>
In this case "TEST" will override the whole Content of the UserControl (the whole Button Template). I guess this happens because The Button inside the UserControl is defined as "Content" itself and it will get overridden when defining new Content.
So the final question is: Is it possible to achieve what I'm looking for? if yes: How? How could I "redirect" the Content I'm defining in my MainView into the self-defined ContentPresenter inside my Button Template instead of the UserControls's ContentPresenter?
If possible I don't want to create a new dp-propery which hosts my Content, e.g.:
<controls:MordernButton Size="200" BackgroundColor="Light">
<controls:ModernButton.Content>
I don't want this, if possible
</controls:ModernButton.Content>
</controls:ModernButton>
Use the ContentPropertyAttribute to instruct the xaml to set this property instead of the actual Content property.
[ContentProperty("InnerContent")]
public partial class ModernButton : UserControl
{
public ModernButton()
{
InitializeComponent();
}
public static readonly DependencyProperty InnerContentProperty =
DependencyProperty.Register("InnerContent", typeof(object), typeof(ModernButton));
public object InnerContent
{
get { return (object)GetValue(InnerContentProperty); }
set { SetValue(InnerContentProperty, value); }
}
}
Then in your xaml, Bind the Content Presenter to use InnerContent property instead.
<ContentPresenter Content="{Binding InnerContent, ElementName=_modernButton}"/>
This way you can do the following without replacing the actual content.
<control:ModernButton Size="200" BackgroundColor="Light">
TEST
</control:ModernButton>
Here we go.
<UserControl x:Class="SGDB.UI.Controls.ModernButton"
xmlns:local="clr-namespace:SGDB.UI.Controls"
xmlns:converter="clr-namespace:SGDB.UI.Converter"
x:Name="_modernButton">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Button Content="{TemplateBinding Content}">
<Button.Resources>
<converter:EnumToColorConverter x:Key="ColorConverter"/>
</Button.Resources>
<Button.Template >
<ControlTemplate TargetType="Button">
<Border Width="{Binding Size,
ElementName=_modernButton}"
Height="{Binding Size,
ElementName=_modernButton}"
BorderBrush="Black"
BorderThickness="0.8,0.8,3,3">
<Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}">
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Let's assume that youre UserControl is:
<UserControl x:Class="QuickAndDirtyAttempt.Decorator" ....
<UserControl.Template>
<ControlTemplate TargetType="{x:Type local:Decorator}">
<StackPanel Orientation="Vertical">
<Label>Foo</Label>
<ContentPresenter/>
<Label>Bar</Label>
</StackPanel>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Note the TargetType property on the template: without it the project will happily compile, but the ContentPresenter will not work.
And then:
<Window ... >
<StackPanel Orientation="Vertical">
<local:Decorator>
<Label Background="Wheat">User supplied content here</Label>
</local:Decorator>
</StackPanel>
</Window>
I strongly recommend you to read this before implementing anything
Simple; Just circumvent and replace the UserControl's Template.
<UserControl.Template>
<ControlTemplate TargetType="{x:Type UserControl}">
<Button Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}">
<Button.Resources>
<converter:EnumToColorConverter x:Key="ColorConverter"/>
</Button.Resources>
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border Width="{Binding Size,
ElementName=_modernButton}"
Height="{Binding Size,
ElementName=_modernButton}"
BorderBrush="Black"
BorderThickness="0.8,0.8,3,3">
<Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}">
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</ControlTemplate>
</UserControl.Template>
All a user control is (at least it terms of XAML and its template), is a Border with a ContentPresenter inside it. The ContentPresenter being the only important part, really.
So all you do is gut out its Template and feed the Content property the UserControl has into something a little different; in this case, your button.
This is the difference between making a usercontrol out of other controls, and shoving some controls into a user control.
Making the usercontrol out of other controls gives you much more power.
My example for dialog box
<UserControl
x:Class="CyberpunkModManager.Controls.DialogBox"
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:local="clr-namespace:CyberpunkModManager.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
Foreground="{StaticResource ThemeForeground}"
mc:Ignorable="d">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid Background="{StaticResource ThemeTransparentColor}">
<Border
MinWidth="400"
Padding="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{StaticResource ThemeElement}"
CornerRadius="4">
<ContentPresenter />
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
Related
It is stated in MSDN that
Starting with Windows 10, version 1809, you can use the x:Bind markup extension anywhere you use TemplateBinding in a ControlTemplate.
However, when I try to replace TemplateBinding with {x:Bind} whilst defining the style of a custom control, as so,
<Style TargetType="local:PomodoroTimer" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:PomodoroTimer">
<Grid Width="300" Height="300" Background="{x:Bind Background}">
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I get the following error: Unable to resolve symbol 'Background'. What am I missing?
x:Bind needs code-behind. (see here)
So, thanks to MainWindow.xaml.cs, this works:
<Window
x:Class="Bindings.MainWindow"
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:local="using:Bindings"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style
x:Key="CustomButtonStyle"
TargetType="Button">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
BorderBrush="{x:Bind BorderBrush, Mode=OneWay}"
BorderThickness="{x:Bind BorderThickness, Mode=OneWay}">
<ContentControl Content="{x:Bind Content, Mode=OneWay}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</Grid.Resources>
<Button
BorderBrush="SkyBlue"
BorderThickness="1"
Content="Custom Button"
Style="{StaticResource CustomButtonStyle}" />
</Grid>
</Window>
For custom (templated) controls, I'd go with:
Text="{TemplateBinding Text}"
or
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
for TwoWay bindings.
If you want to do x:Bind inside the ControlTemplate, this answer might help.
I have a ContentControl is defined in XAML like this:
<ContentControl Width="100"
Height="100"
Canvas.Top="100"
Canvas.Left="100"
Template="{StaticResource DesignerItemTemplate}">
<Ellipse IsHitTestVisible="False" Fill="Blue"/>
</ContentControl>
And I want to create another one by code but I don't know how to set the Template property. Anyone can help me, please?
Write the Template in some ressources (App, Window, Grid, whatever, ...)
<Window>
<Window.Resources>
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="{x:Type ContentControl}">
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Window.Resources>
</Window>
In code behind, find the resource and assign the Template :
public MainWindow()
{
InitializeComponent();
contentControl1.Template = Resources["DesignerItemTemplate"] as ControlTemplate;
}
Regards
Try using
Test.Template=Application.Current.FindResource("DesignerItemTemplate")
I got the following setup:
<Button x:Name="DeleteFilter" Margin="5" Grid.Column="1" Padding="2">
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Stretch="Fill" Margin="5 3">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource appbar_delete}"/>
</Rectangle.Fill>
</Rectangle>
<TextBlock Text="{Resx DeleteFilter}"/>
</StackPanel>
</Button>
However, when launching my application I get the following result which is not what I want. All font properties seem to be ignored when I set the Content property of my button manually.
Live example:
I'd like to have the same fontstyle and fontsize as the right button. I tried to specify the style manually by using StaticResource and DynamicResource as follows:
<Button Style="{StaticResource MetroButton}"....
but nothing changed.
I guess that I need to implement a style which overrides the existing one and transfers the formatting to the TextBlock element but I have no clue how to do that.
The working "LOAD FILTERS" button in the right:
<Button x:Name="LoadFilter" Content="{Resx LoadFilters}" Margin="5" Grid.Column="2"/>
MahApps.Metro's standard button (MetroButton) is included in this file.
The style I applied to my icon button:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MetroIconButton" BasedOn="{StaticResource MetroButton}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Margin="5 3" Stretch="Fill">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In your setup the StackPanel is used as a content which is not so ideal, you may create a style containing the template and the required property setters for font so it remain consistent for the desired buttons across the application.
So if I try to create a button style for you that would be
<Style x:Key="MetroButton" TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="13"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Margin="5 3" Stretch="Fill">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
then I would use this style on button as
<Button Content="Delete" Style="{StaticResource MetroButton}" Tag="{StaticResource appbar_delete}"/>
Update
leveraging the ContentTemplate to achieve the same while utilizing the existing template.
<Style x:Key="MetroIconButton" BasedOn="{StaticResource MetroButton}" TargetType="{x:Type Button}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Rectangle Height="9" Width="9" Margin="5 3" Stretch="Fill">
<Rectangle.Fill>
<VisualBrush Visual="{Binding Tag, RelativeSource={RelativeSource FindAncestor, AncestorType=Button}}"/>
</Rectangle.Fill>
</Rectangle>
<ContentControl Content="{Binding}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
usage remain quite same except the style MetroIconButton
<Button Content="Delete" Style="{StaticResource MetroIconButton}" Tag="{StaticResource appbar_delete}"/>
I am using Tag property to hold the icon so it is plug and play for you, but better is to use Attached properties for the same. l:ExtraProperties.Icon={StaticResource appbar_delete}", I can provide a sample if you need that too.
Actually in previous style we override the Template defined in the MetroButton style so it failed. After looking at the original implementation of MetroButton style I come up with the ContentTemplate way to actieve the same. So instead of setting Template we will set the content template which will be picked up by MetroButton style and applied to the content.
Using Attached Properties
declare a class inheriting DependencyObject or any of its derived class along with the desired property as mentioned below
class ExtraProperties: DependencyObject
{
public static Visual GetIcon(DependencyObject obj)
{
return (Visual)obj.GetValue(IconProperty);
}
public static void SetIcon(DependencyObject obj, Visual value)
{
obj.SetValue(IconProperty, value);
}
// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconProperty = DependencyProperty.RegisterAttached("Icon", typeof(Visual), typeof(ExtraProperties), new PropertyMetadata(null));
}
add namespace to your xaml
<Window x:Class="Example.MainWindow"
...
xmlns:l="clr-namespace:Example">
then change the style as
<VisualBrush Visual="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}, Path=(l:ExtraProperties.Icon)}"/>
and usage as
<Button Content="Delete" Style="{StaticResource MetroIconButton}" l:ExtraProperties.Icon="{StaticResource appbar_delete}"/>
using Attached properties is more WPF approach, instead of hacking other properties which may not be guaranteed to behave as expected.
I have a custom ContentControl which has a fixed XAML layout like a UserControl (Rather than the usual applied generic template).
Previously this layout had no extra markup, so it was literally:
<ContentControl x:Class="MyControls.CustomViewControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</ContentControl>
This worked fine.
I now want to put a border around the content, so I have changed the XAML to:
<ContentControl x:Class="MyControls.CustomViewControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ContentControl.Template>
<ControlTemplate>
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter />
</Border>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
This shows the border, but no content.
I tried supplying an explicit binding for ContentPresenter:
<ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
But this made no difference.
Setting an explicit Content does work:
<ContentPresenter Content="TEST" />
Anyone know why the Content binding doesn't work? I guess I could fall back to the usual generic template but it would be easier if I could just do it directly like a UserControl.
Add TargetType for Control Template
<ContentControl.Template>
<ControlTemplate TargetType="Button">
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter />
</Border>
</ControlTemplate>
</ContentControl.Template>
Use TemplateBinding instead of Binding inside a ControlTemplate :
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</ContentControl.Template>
EDIT:
The important part of the shown code snippet is the TargetType in the definition of the ControlTemplate. With the target type
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter/>
</Border>
</ControlTemplate>
</ContentControl.Template>
already works without any TemplateBinding
I'm sure this is dead simple, but I can't seem to figure it out.
I have a ListBox to display items, and these are displayed with a DataTemplate. I now want to group these items, so have added a group based on the manufacturer property. This is done in code behind.
ICollectionView view = CollectionViewSource.GetDefaultView(Items);
PropertyGroupDescription groups = new PropertyGroupDescription("Manufacturer");
view.GroupDescriptions.Add(groups);
I wanted to have each group in an expander, so they can be hidden. I have got this working by looking at GroupTemplates at MSDN This involves, having an expander, textblock and then a seperator to rule off the extra space like in Windows Vista/7 Groups. As Below.
The problem I am having is I cannot get the separator to fill up the remaining space correctly. If I use a MinWidth value, all my expanders have the same width. If I use the {binding ActualWidth, ElementName=MyListBox}, then the separator is too wide, as its as wide as the control that contains it. So it sets the scroll bars to be visible, (see screenshot below). If i leave width blank, then the seperator is not drawn at all.
My gut feeling is the stackpanel should have expanded the seperator to use the remaining space but it didn't. So i tried a DockPanel as in the XamlCode below, yet this also fails. I have a few other problems with getting controls to fill up the remaining space, by using a suitable width so if you can help me resolve this, it would be great.
My current WPF Xaml Markup. You will need to add elements to get this to display something.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="myStackPanel">
<ListBox x:Name="MyListBox">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<DockPanel HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="{Binding ActualWidth, ElementName=MyListBox}"
Margin="10">
<TextBlock DockPanel.Dock="Left" Margin="0" FontSize="14" FontWeight="Bold" Foreground="Black" Text="{Binding Path=Name}"/>
<Separator DockPanel.Dock="Right" Margin="4,0,4,0"></Separator>
</DockPanel>
</Expander.Header>
<ItemsPresenter Margin="5,0,0,0" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- Data Template Here -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
It is in fact not trivial, the control template of the Expander consists of a ToggleButton as the Header and a ContentPresenter for the content. The problem is that the ToggleButton itself has a special style & template which contains the arrow that has the alignment hard-coded into it, the default one looks something like this:
<Style x:Key="ExpanderDownHeaderStyle"
TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent"
SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- ... -->
<!-- The HorizontalAlignment needs to be set to stretch here -->
<ContentPresenter Grid.Column="1"
Margin="4,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
SnapsToDevicePixels="True"
RecognizesAccessKey="True"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- ... -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
To get your style to work you need to modify the default Expander template (get default templates on MSDN - Default WPF Themes link). Not nice but you don't really have much of a choice.