[英]In a WPF control that acts as a container, how do I place the content?
我正在编写一个WPF控件,它的意思是一个容器,就像Border
和ScrollViewer
是容器一样。 它被称为EllipsisButtonControl
,它应该在其内容的右侧放置一个省略号按钮。 这是我打算如何使用它的一个例子:
<local:EllipsisButtonControl>
<TextBlock Text="Testing" />
</local:EllipsisButtonControl>
这是EllipsisButtonControl
的XAML:
<ContentControl
x:Class="WpfApplication1.EllipsisButtonControl"
x:Name="ContentControl"
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="30" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />
<Button Grid.Column="1" Command="{Binding ElementName=ContentControl, Path=Command}" Margin="3,0" Width="30" Height="24" MaxHeight="24" VerticalAlignment="Stretch" Content="..." />
</Grid>
</ContentControl>
以下是代码背后的代码:
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class EllipsisButtonControl
{
public EllipsisButtonControl()
{
InitializeComponent();
}
public static string GetCommand(DependencyObject obj)
{
return (string)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, string value)
{
obj.SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
name: "Command",
propertyType: typeof(ICommand),
ownerType: typeof(EllipsisButtonControl),
defaultMetadata: new UIPropertyMetadata());
}
}
这不起作用。 它使用System.Runtime.Remoting.RemotingException
崩溃Designer。
我相信对EllipsisButtonControl
XAML的ContentPresenter
的绑定是错误的,但我不知道如何使它正确。 使该行引用控件内容的适当语法是什么? (例如,用法示例中定义的TextBlock
)
编辑:
poke在下面提供了一个全面的答案(包括工作代码),但为了其他可能分享我最初误解的人的利益,让我总结一下这里的关键概念:容器控件本身不能“放置内容”。 它通过定义修改调用XAML呈现内容的方式的模板来实现预期效果。 解决方案的其余部分来自该前提。
尝试替换此行:
<ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />
有了这个
<ContentPresenter Grid.Column="0" Content={Binding Content} />
在现有的代码,你使这种ContentPresenter
显示的生成内容EllipsesButtonControl
,其中包括ContentPresenter
必须渲染生成的内容ElipsesButtonControl
其中包括ContentPresenter
.....无限递归。
您的EllipsisButtonControl的XAML已将其内容设置为顶级网格。 您可能想要的是创建一个ControlTemplate
,例如:
<ContentControl x:Class="WpfApplication1.EllipsisButtonControl"
x:Name="ContentControl"
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="30" d:DesignWidth="300">
<ContentControl.Template>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
Content="{Binding ElementName=ContentControl, Path=Content}"/>
<Button Grid.Column="1"
Command="{Binding ElementName=ContentControl, Path=Command}"
Margin="3,0" Width="30" Height="24" MaxHeight="24"
VerticalAlignment="Stretch" Content="..." />
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
<local:EllipsisButtonControl>
<TextBlock Text="Testing" />
</local:EllipsisButtonControl>
这确实设置了用户控件的Content
。 但是用户控件的XAML中的以下内容也是如此:
<ContentControl …>
<Grid>
…
</Grid>
</ContentControl>
调用XAML在这里具有优先权,因此无论你在用户控件的XAML中做什么,实际上都会被忽略。
这里的解决方案是设置用户控件的模板 。 模板(在本例中为控件的控件模板)确定控件本身的呈现方式。 用户控件的最简单模板(以及它的默认模板)就是在那里使用ContentPresenter
,但是当然,你想要添加一些东西,所以我们必须覆盖模板。 这通常看起来像这样:
<ContentControl …>
<!-- We are setting the `Template` property -->
<ContentControl.Template>
<!-- The template value is of type `ControlTemplate` and we should
also set the target type properly so binding paths can be resolved -->
<ControlTemplate>
<!-- This is where your control code actually goes -->
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
现在,这是您完成此工作所需的框架。 但是,一旦进入控件模板,就需要使用正确的绑定类型。 由于我们正在编写模板并希望绑定到父控件的属性,因此我们需要将父控件指定为绑定中的相对源。 但最简单的方法是使用TemplateBinding
标记扩展。 使用它, ContentPresenter
可以放在上面的ControlTemplate
:
<ContentPresenter Content="{TemplateBinding Content}" />
这应该是您需要的所有内容,以使内容演示者工作。
但是,现在您使用控件模板,当然您也需要调整其他绑定。 特别是绑定到自定义依赖项属性Command
。 这通常看起来与绑定到Content
的模板相同,但由于我们的控件模板的目标是ContentControl
类型,而ContentControl
没有自定义属性,因此我们需要在此处显式引用您的自定义依赖项属性:
<Button Command="{TemplateBinding local:EllipsisButtonControl.Command}" … />
一旦我们拥有了,所有绑定都应该正常工作。 (如果你现在想知道:是的,绑定总是以类型的静态依赖属性为目标)
因此,总而言之,您的自定义内容控件应如下所示:
<ContentControl
x:Class="WpfApplication1.EllipsisButtonControl"
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"
xmlns:local="clr-namespace:WpfApplication1"
d:DesignHeight="30" d:DesignWidth="300" mc:Ignorable="d">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Grid>
<ContentPresenter Grid.Column="0"
Content="{TemplateBinding Content}" />
<Button Grid.Column="1" Content="…"
Command="{TemplateBinding local:EllipsisButtonControl.Command}" />
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.