我正在编写一个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。
我认为对ContentPresenter
XAML的EllipsisButtonControl
的绑定是错误的,但我不知道如何使它正确。使该行引用控件内容的适当语法是什么? (例如,使用示例中定义的TextBlock
)
修改
poke在下面提供了一个全面的答案(包括工作代码),但为了其他可能分享我最初误解的人的利益,让我总结一下这里的关键概念:容器控件本身不能“放置内容”。它通过定义修改调用XAML呈现内容的方式的模板来实现预期效果。其余的解决方案都是从这个前提出发的。
答案 0 :(得分:1)
尝试替换此行:
<ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />
有了这个
<ContentPresenter Grid.Column="0" Content={Binding Content} />
在现有代码中,您ContentPresenter
显示生成的EllipsesButtonControl
内容,其中包含ContentPresenter
,ElipsesButtonControl
必须呈现生成的ContentPresenter
内容,其中包含{{1}} .....无限递归。
答案 1 :(得分:1)
您的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>
答案 2 :(得分:1)
<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>
一旦我们拥有了,所有绑定都应该正常工作。 (如果你现在想知道:是的,绑定总是以类型上的静态依赖属性为目标)
因此,总而言之,您的自定义内容控件应如下所示:
SSLHandshakeException