如何在 UserControl 中使用 ContentPresenter
How to use a ContentPresenter inside a UserControl
我想创建一个 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>
现在,如您所料,如果我在我的 MainView 中使用此控件,一切都会正常工作,直到我定义一些内容。
使用:
<control:ModernButton Size="200" BackgroundColor="Light">
TEST
</control:ModernButton>
在这种情况下 "TEST" 将覆盖 UserControl 的整个内容(整个按钮模板)。我想这是因为 UserControl 中的按钮被定义为 "Content" 本身,并且在定义新内容时它会被覆盖。
所以最后一个问题是:是否有可能实现我正在寻找的东西?如果是:如何?我如何才能将我在 MainView 中定义的内容 "redirect" 放入按钮模板中自定义的 ContentPresenter 而不是 UserControls 的 ContentPresenter?
如果可能,我不想创建一个新的 dp-属性 来托管我的内容,例如:
<controls:MordernButton Size="200" BackgroundColor="Light">
<controls:ModernButton.Content>
I don't want this, if possible
</controls:ModernButton.Content>
</controls:ModernButton>
假设您的 UserControl 是:
<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>
注意模板上的 TargetType 属性:没有它,项目将顺利编译,但 ContentPresenter 将无法工作。
然后:
<Window ... >
<StackPanel Orientation="Vertical">
<local:Decorator>
<Label Background="Wheat">User supplied content here</Label>
</local:Decorator>
</StackPanel>
</Window>
我强烈建议您在执行任何操作之前read this
我们开始吧。
<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>
简单;只需绕过并替换 UserControl 的模板即可。
<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>
所有用户控件(至少它是 XAML 及其模板的术语)是一个带有 ContentPresenter 的边框。 ContentPresenter 是唯一重要的部分,真的。
所以您所做的就是取出它的模板并将内容 属性 用户控件提供给一些不同的东西;在这种情况下,您的按钮。
这是从 其他控件中创建用户控件 与将某些控件 推入 用户控件之间的区别。
将 usercontrol 从其他控件中分离出来给你更多的权力。
使用 ContentPropertyAttribute
指示 xaml 设置此 属性 而不是实际内容 属性。
[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); }
}
}
然后在您的 xaml 中,绑定 Content Presenter 以使用 InnerContent 属性。
<ContentPresenter Content="{Binding InnerContent, ElementName=_modernButton}"/>
这样您就可以在不替换实际内容的情况下进行以下操作。
<control:ModernButton Size="200" BackgroundColor="Light">
TEST
</control:ModernButton>
我的对话框示例
<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>
我想创建一个 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>
现在,如您所料,如果我在我的 MainView 中使用此控件,一切都会正常工作,直到我定义一些内容。
使用:
<control:ModernButton Size="200" BackgroundColor="Light">
TEST
</control:ModernButton>
在这种情况下 "TEST" 将覆盖 UserControl 的整个内容(整个按钮模板)。我想这是因为 UserControl 中的按钮被定义为 "Content" 本身,并且在定义新内容时它会被覆盖。
所以最后一个问题是:是否有可能实现我正在寻找的东西?如果是:如何?我如何才能将我在 MainView 中定义的内容 "redirect" 放入按钮模板中自定义的 ContentPresenter 而不是 UserControls 的 ContentPresenter?
如果可能,我不想创建一个新的 dp-属性 来托管我的内容,例如:
<controls:MordernButton Size="200" BackgroundColor="Light">
<controls:ModernButton.Content>
I don't want this, if possible
</controls:ModernButton.Content>
</controls:ModernButton>
假设您的 UserControl 是:
<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>
注意模板上的 TargetType 属性:没有它,项目将顺利编译,但 ContentPresenter 将无法工作。 然后:
<Window ... >
<StackPanel Orientation="Vertical">
<local:Decorator>
<Label Background="Wheat">User supplied content here</Label>
</local:Decorator>
</StackPanel>
</Window>
我强烈建议您在执行任何操作之前read this
我们开始吧。
<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>
简单;只需绕过并替换 UserControl 的模板即可。
<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>
所有用户控件(至少它是 XAML 及其模板的术语)是一个带有 ContentPresenter 的边框。 ContentPresenter 是唯一重要的部分,真的。
所以您所做的就是取出它的模板并将内容 属性 用户控件提供给一些不同的东西;在这种情况下,您的按钮。
这是从 其他控件中创建用户控件 与将某些控件 推入 用户控件之间的区别。 将 usercontrol 从其他控件中分离出来给你更多的权力。
使用 ContentPropertyAttribute
指示 xaml 设置此 属性 而不是实际内容 属性。
[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); }
}
}
然后在您的 xaml 中,绑定 Content Presenter 以使用 InnerContent 属性。
<ContentPresenter Content="{Binding InnerContent, ElementName=_modernButton}"/>
这样您就可以在不替换实际内容的情况下进行以下操作。
<control:ModernButton Size="200" BackgroundColor="Light">
TEST
</control:ModernButton>
我的对话框示例
<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>