有没有办法将项目/数据模板注入到自定义样式或UserControl中



读取解决方案的更新

我有一个小的WPF应用程序,里面有几个小游戏克隆,比如《扫雷》、《Connect 4》、《Tic Tac Toe》等等。

所有这些的共同点是,它们都是一个统一的正方形网格,每个正方形都由一个按钮控制。

对于这些游戏中的每一个,我都在它们的XAML中定义了一个带有UniformGrid ItemsPanelTemplate的UserControl。

它们唯一不同的地方是使用的DataTemplate:

<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Button that fit the specific need of the game -->
</DataTemplate>           
</ItemsControl.ItemTemplate>

为了避免重复,我想创建一个UserControl,它有一个DataTemplate依赖属性(并在XAML中定义了一个名为ItemsControl的ItemsControl(:

public DataTemplate DataTemplate
{
get => (DataTemplate)GetValue(DataTemplateProperty);
set => SetValue(DataTemplateProperty, value);
}
public static readonly DependencyProperty DataTemplateProperty =
DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(BoardGameControl));
public BoardGameControl()
{
InitializeComponent();
itemscontrol.ItemTemplate = DataTemplate;
}

我试着在我的应用程序中这样使用:

<controls:BoardGameControl>
<controls:BoardGameControl.DataTemplate>
<DataTemplate>
<Button Content="Hi"/>
</DataTemplate>
</controls:BoardGameControl.DataTemplate>
</controls:BoardGameControl>

我也尝试过其他一些方法,但都没有奏效。

我如何才能避免为每个游戏定义一个新的ItemsControl,而让UserControl或Style根据情况接受不同的按钮?

更新

我结合了我标记为已接受的解决方案和@Joe对此帖子的评论。

我没有创建UserControl,而是创建了一个具有所需属性的自定义控件,然后根据自己的喜好在Generic.xaml文件中设置样式。我还从自定义控件中删除了DataTemplate属性,而是在App.xaml中为每个不同的VM添加了DataTemplates。

下面你会发现我的新自定义控件的代码和样式。

public class GameBoardControl : Control
{
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(nameof(Columns), typeof(int), typeof(BoardGameControl));
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register(nameof(Rows), typeof(int), typeof(BoardGameControl));
static GameBoardControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GameBoardControl), new FrameworkPropertyMetadata(typeof(GameBoardControl)));
}
}
<Style TargetType="{x:Type controls:GameBoardControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:GameBoardControl}">
<ItemsControl ItemsSource="{Binding Squares}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Columns, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:GameBoardControl}}}"
Rows="{Binding Rows, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:GameBoardControl}}}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

请注意,我总是将ItemsSource绑定到Squares,我能够做到这一点,因为我所有的游戏都有一个名为Squares的ObservableCollection,其中存储了Square View模型。

DataTemplate 示例

<DataTemplate DataType="{x:Type mvvmtoolkit:MemorySquareVM}">
<Button Command="{Binding RelativeSource={RelativeSource AncestorType=controls:GameBoardControl}, Path=DataContext.PressSquareCommand}" 
CommandParameter="{Binding}"
Background="{Binding Position, Converter={local:PositionToColorConverter}}"
Style="{StaticResource MemoryButton}"/>
</DataTemplate>

如果我正确理解您需要什么,那么您需要稍微更改实现:您不应该在Code Behind中使用值赋值,而是需要在XAML中使用绑定。

public BoardGameControl()
{
InitializeComponent();
// itemscontrol.ItemTemplate = DataTemplate;
}
<ItemsControl ItemTemplate="{Binding DataTemplate, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:BoardGameControl}}}">

我还强烈建议在这种情况下(当属性被添加到元素时,它的行为会发生变化,等等(不要使用UserControl,而是使用Custom Control。

为什么我的实现不起作用?

在Sharp:上的实现

public static readonly DependencyProperty DataTemplateProperty =
DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(BoardGameControl),
new PropertyMetadata((d, e) => ((BoardGameControl)d).itemscontrol.ItemTemplate = (IEnumerable) e.NewValue));
public BoardGameControl()
{
InitializeComponent();
// itemscontrol.ItemTemplate = DataTemplate;
}

我会制作一个自定义控件,但我不知道如何自己实现ItemsControl/UniformGrid的逻辑

如果您在Studio中工作,请选择";添加"->quot;创建元素";在上下文菜单中。将出现一个组合框;自定义控制WPF";来自它。您将有一个派生自Control的类代码。添加您需要的属性和行为逻辑。而";主题/通用;文件也将被创建-这是默认的主题。这个主题将为您的元素提供一个模板-根据需要进行编辑。