[英]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.