我创建了一个代表图块的CustomControl。它包含一个带标题,工具栏和主要内容的标题,并且工作正常。但是,我想将<StackPanel Orientation="Horizontal">
移动到控件的模板,因为我总是希望工具栏像这样呈现。
由于我想将孩子添加到DependencyProperty
而不是直接添加到控件,我无法从ItemsControl
派生。我可以在不向控件中添加大量样板代码的情况下解决这个问题吗?
我认为我必须用ContentPresenter
替换第二个ItemsPresenter
,但是:
DependencyProperty
需要什么类型? <{1}}和UIElementCollection
都不能实现。ItemsCollection
显示其内容?当前用法:
StackPanel
请求用法:
<controls:Tile Title="Projects" Margin="6,6,0,6">
<controls:Tile.ToolbarContent>
<StackPanel Orientation="Horizontal">
<controls:ToolbarButton Text="Add" ImageSource="pack://application:,,,/MyAssembly;Component/Resources/plus-16.png" />
<controls:ToolbarButton Text="Remove" ImageSource="pack://application:,,,/MyAssembly;Component/Resources/delete-16.png" />
<controls:ToolbarButton ... />
<controls:ToolbarButton ... />
</StackPanel>
</controls:Tile.ToolbarContent>
<ListView x:Name="MainContent" BorderThickness="0" ItemsSource="{Binding Projects}" />
</controls:Tile>
样式:
<controls:Tile Title="Projects" Margin="6,6,0,6">
<controls:Tile.ToolbarContent>
<controls:ToolbarButton Text="Add" ImageSource="pack://application:,,,/MyAssembly;Component/Resources/plus-16.png" />
<controls:ToolbarButton Text="Remove" ImageSource="pack://application:,,,/MyAssembly;Component/Resources/delete-16.png" />
<controls:ToolbarButton ... />
<controls:ToolbarButton ... />
</controls:Tile.ToolbarContent>
<ListView x:Name="MainContent" BorderThickness="0" ItemsSource="{Binding Projects}" />
</controls:Tile>
代码:
<Style TargetType="controls:Tile">
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryColor_100}" />
<Setter Property="Background" Value="White" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:Tile">
<Border BorderBrush="{DynamicResource PrimaryColor_40}" BorderThickness="1" Background="{TemplateBinding Background}" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Border x:Name="Header" Background="{TemplateBinding BorderBrush}">
<TextBlock VerticalAlignment="Center" FontSize="{TemplateBinding FontSize}" Margin="3" Text="{TemplateBinding Title}" Foreground="White" />
</Border>
<Border x:Name="Toolbar" Grid.Row="1" Background="{DynamicResource PrimaryColor_40}" Padding="1">
<ContentPresenter x:Name="ToolbarContent" Content="{TemplateBinding ToolbarContent}" />
</Border>
<ContentPresenter x:Name="MainContent" Grid.Row="2" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
答案 0 :(得分:1)
我不知道一个好的方法来按字面意思做你要求的东西。您以一种恕我直言,笨拙并且不符合特定控件的预期设计的方式重载ContentControl
类型。也就是说,有可能沿着这些方向发挥作用。
要理解的重要一点是,您无法创建随后分配给StackPanel
子项的集合。这不是Panel
对象的工作方式。相反,您需要一些其他控件,您可以为其分配控件集合并使其工作。用于此目的的通常控制是ItemsControl
对象。
您可以将ToolbarContent
属性设为ObservableCollection<UIElement>
,然后将其值绑定到ItemsControl.ItemsSource
属性。在ItemsControl
中,您可以设置ItemsPanel
属性,让ItemsControl
使用具有所需布局的StackPanel
(例如Orientation="Horizontal"
)。
例如:
<ControlTemplate TargetType="controls:Tile">
<Border BorderBrush="{DynamicResource PrimaryColor_40}" BorderThickness="1" Background="{TemplateBinding Background}" UseLayoutRounding="{TemplateBinding UseLayoutRounding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Border x:Name="Header" Background="{TemplateBinding BorderBrush}">
<TextBlock VerticalAlignment="Center" FontSize="{TemplateBinding FontSize}" Margin="3" Text="{TemplateBinding Title}" Foreground="White" />
</Border>
<Border x:Name="Toolbar" Grid.Row="1" Background="{DynamicResource PrimaryColor_40}" Padding="1">
<ItemsControl ItemsSource="{TemplateBinding ToolbarContent}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
<ContentPresenter x:Name="MainContent" Grid.Row="2" />
</Grid>
</Border>
</ControlTemplate>
和
class Tile : ContentControl
{
private static readonly DependencyPropertyKey ToolbarContentKey =
DependencyProperty.RegisterReadOnly("ToolbarContent", typeof(ObservableCollection<UIElement>), typeof(Tile), new PropertyMetadata());
public static readonly DependencyProperty ToolbarContentProperty = ToolbarContentKey.DependencyProperty;
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(Tile), new PropertyMetadata());
public ObservableCollection<UIElement> ToolbarContent
{
get { return (ObservableCollection<UIElement>)GetValue(ToolbarContentProperty); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public Tile()
{
SetValue(ToolbarContentKey, new ObservableCollection<UIElement>());
}
}
然后,当您使用所需的语法时,元素将添加到集合中,并且该集合绑定到ItemsSource
属性,以便可以使用StackPanel
作为主机来显示它们。 / p>
现在,虽然这会起作用,但对我来说,你定义的模板确实有更多的复合控制&#34;感受到它。这通常意味着实施UserControl
。使用UserControl
,您只是声明一个控件,它本身由一组其他控件组成,其中XAML中定义的布局和其他特征类似于Window
的方式。< / p>
在这种方法中,您可以将内部元素集合作为UserControl
的公共接口的一部分公开,而不是创建外部集合并绑定到内部元素的属性,并添加内容直接。这也允许您以更直接的方式定义自定义控件的布局/组合(例如,具有实际的StackPanel
元素,而不必将其包装在ItemsControl
中。)
这可能看起来像这样:
<UserControl x:Class="TestSO37929673CollectionContent.Tile2"
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"
x:Name="root"
d:DesignHeight="300" d:DesignWidth="300">
<Border BorderBrush="Black" BorderThickness="1"
Background="{Binding Background, ElementName=root}"
UseLayoutRounding="{Binding UseLayoutRounding, ElementName=root}"
SnapsToDevicePixels="{Binding SnapsToDevicePixels, ElementName=root}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Border x:Name="Header" Background="{Binding BorderBrush, ElementName=root}">
<TextBlock VerticalAlignment="Center"
FontSize="{Binding FontSize, ElementName=root}"
Margin="3"
Text="{Binding Title, ElementName=root}" Foreground="White" />
</Border>
<Border x:Name="Toolbar" Grid.Row="1" Background="White" Padding="1">
<StackPanel x:Name="stackPanel1" Orientation="Horizontal"/>
</Border>
<ContentPresenter Content="{Binding MainContent, ElementName=root}" Grid.Row="2" />
</Grid>
</Border>
</UserControl>
和
[ContentPropertyAttribute("MainContent")]
public partial class Tile2 : UserControl
{
private static readonly DependencyPropertyKey ToolbarContentKey =
DependencyProperty.RegisterReadOnly("ToolbarContent", typeof(UIElementCollection), typeof(Tile2), new PropertyMetadata());
public static readonly DependencyProperty ToolbarContentProperty = ToolbarContentKey.DependencyProperty;
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(Tile2), new PropertyMetadata());
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(object), typeof(Tile2));
public UIElementCollection ToolbarContent
{
get { return (UIElementCollection)GetValue(ToolbarContentProperty); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public object MainContent
{
get { return GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
public Tile2()
{
InitializeComponent();
SetValue(ToolbarContentKey, stackPanel1.Children);
}
}
使用语法是相同的(即您在帖子中要求的完全相同),您也可以以相同的方式设置控件的样式。但是它在UserControl
定义本身中封装了控件的布局,而不是依赖于样式中指定的ControlTemplate
来实现这一点。
请注意,UserControl
本身是ContentControl
子类。因此,如果您觉得使用ContentControl
的功能很重要,您仍然可以利用它们。只是UserControl
范例恕我直言使客户端和自定义控件的关系更清晰,更易于维护。