WPF 中具有多个 DataTemplate 的 ItemsControl
ItemsControl having multiple DataTemplate in WPF
我想画 canvas 不同的形状。如何使来自 ArrowsItems ObservableCollection 和 CircleItems ObservableCollection 的对象在 canvas 中可见?我还创建了 Shapes ObservableCollection,包括每个 Circle 和 Arrows Items。我认为原因可能在于数据绑定,但不知道在哪里。
目标是可以生成然后以编程方式绘制圆圈和箭头。
<Button Grid.Row="1" MaxWidth="1000" Command="{Binding CreateEllipse}">Utwórz</Button>
<Viewbox Grid.Row="2" Margin="0 20 0 0" Stretch="Uniform" StretchDirection="Both" VerticalAlignment="Stretch">
<ItemsControl Name="Shape" ItemsSource="{Binding Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="2000" Height="1200" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X, Mode=TwoWay}"/><Setter Property="Canvas.Top" Value="{Binding Y, Mode=TwoWay}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type core:CircleItem}">
<Viewbox Width="{Binding Width}" Height="{Binding Height}">
<!--MouseMove="Viewbox_MouseMove"-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding DataContext.MyCommand, ElementName=Shape}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<local:DragBehavior/>
</i:Interaction.Behaviors>
<Grid>
<Grid.RenderTransform>
<TranslateTransform X="{Binding TransformX}" Y="{Binding TransformY}" />
</Grid.RenderTransform>
<Ellipse Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Color}" />
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" TextAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Viewbox>
</DataTemplate>
<DataTemplate DataType="{x:Type core:ArrowItem}">
<Line X1="{Binding X1}" Y1="{Binding Y1}" X2="{Binding X2}" Y2="{Binding Y2}" Stroke="{Binding Color}" StrokeThickness="{Binding StrokeThickness}" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Viewbox>
也在我的 ViewModel 中:
public ObservableCollection<CircleItem> CircleItems { get; set; }
public ObservableCollection<ArrowItem> ArrowItems { get; set; }
public CompositeCollection Shapes { get; set; }
并且在将 CircleItem class 的一些对象添加到 CircleItems 并将 ArrowItem 添加到 ArrowItems 之后:
CompositeCollection coll = new CompositeCollection();
coll.Add(new CollectionContainer() { Collection = CircleItems });
coll.Add(new CollectionContainer() { Collection = ArrowItems });
Shapes = coll;
如果我没理解错的话,您希望针对不同的数据类型使用不同的模板。这很容易得到。其中一种方法(可能是最简单的一种):
- 为您要显示的每种类型创建数据模板
创建某种数据模板,select 正确的模板:
<!-- Data template for arrows -->
<DataTemplate x:Key="ArrowsDataTemplate" DataType="{x:Type core:ArrowItem}">
<!-- Create here your template for arrows -->
</DataTemplate>
<!-- create data templates for other stuff and then "selector" data template -->
<DataTemplate x:Key="ContentDataTemplate">
<ContentPresenter x:Name="itemContentPresenter"
ContentTemplate="{StaticResource CircleDataTemplate}" <!-- just the default one -->
Content="{TemplateBinding Content}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding MyItemTypeAsEnum}" Value="Arrow">
<Setter TargetName="itemContentPresenter" Property="ContentTemplate" Value="{StaticResource ArrowsDataTemplate"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<!-- Now in your control (for example content control), where you show this stuff, do: -->
<ContentControl ContentTemplate="{StaticResource ContentDataTemplate}"/>
现在,我假设您有一个带有 属性 MyItemTypeAsEnum 的基础项目,它将为您提供 CircleItem 的 Circle、ArrowItem 的 Arrow 等。但是如果您没有这样的 属性,您应该能够从您的视图模型中获取布尔值,该值将告诉您该项目是否为圆形等。
在您的主控件中,当您显示 thigs 时,您必须将 ContentTemplate 设置为您的 "selector" 模板。
确保在将视图模型分配给 DataContext 之前初始化 Shapes
属性。集合属性应该都是只读的,否则您将不得不从它们的设置器中触发 属性 更改通知。
public class ViewModel
{
public ObservableCollection<CircleItem> CircleItems { get; }
= new ObservableCollection<CircleItem>();
public ObservableCollection<ArrowItem> ArrowItems { get; }
= new ObservableCollection<ArrowItem>();
public CompositeCollection Shapes { get; }
= new CompositeCollection();
public ViewModel()
{
Shapes.Add(new CollectionContainer { Collection = CircleItems });
Shapes.Add(new CollectionContainer { Collection = ArrowItems });
}
}
我想画 canvas 不同的形状。如何使来自 ArrowsItems ObservableCollection 和 CircleItems ObservableCollection 的对象在 canvas 中可见?我还创建了 Shapes ObservableCollection,包括每个 Circle 和 Arrows Items。我认为原因可能在于数据绑定,但不知道在哪里。
目标是可以生成然后以编程方式绘制圆圈和箭头。
<Button Grid.Row="1" MaxWidth="1000" Command="{Binding CreateEllipse}">Utwórz</Button>
<Viewbox Grid.Row="2" Margin="0 20 0 0" Stretch="Uniform" StretchDirection="Both" VerticalAlignment="Stretch">
<ItemsControl Name="Shape" ItemsSource="{Binding Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="2000" Height="1200" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X, Mode=TwoWay}"/><Setter Property="Canvas.Top" Value="{Binding Y, Mode=TwoWay}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type core:CircleItem}">
<Viewbox Width="{Binding Width}" Height="{Binding Height}">
<!--MouseMove="Viewbox_MouseMove"-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding DataContext.MyCommand, ElementName=Shape}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<local:DragBehavior/>
</i:Interaction.Behaviors>
<Grid>
<Grid.RenderTransform>
<TranslateTransform X="{Binding TransformX}" Y="{Binding TransformY}" />
</Grid.RenderTransform>
<Ellipse Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Color}" />
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" TextAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Viewbox>
</DataTemplate>
<DataTemplate DataType="{x:Type core:ArrowItem}">
<Line X1="{Binding X1}" Y1="{Binding Y1}" X2="{Binding X2}" Y2="{Binding Y2}" Stroke="{Binding Color}" StrokeThickness="{Binding StrokeThickness}" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Viewbox>
也在我的 ViewModel 中:
public ObservableCollection<CircleItem> CircleItems { get; set; }
public ObservableCollection<ArrowItem> ArrowItems { get; set; }
public CompositeCollection Shapes { get; set; }
并且在将 CircleItem class 的一些对象添加到 CircleItems 并将 ArrowItem 添加到 ArrowItems 之后:
CompositeCollection coll = new CompositeCollection();
coll.Add(new CollectionContainer() { Collection = CircleItems });
coll.Add(new CollectionContainer() { Collection = ArrowItems });
Shapes = coll;
如果我没理解错的话,您希望针对不同的数据类型使用不同的模板。这很容易得到。其中一种方法(可能是最简单的一种):
- 为您要显示的每种类型创建数据模板
创建某种数据模板,select 正确的模板:
<!-- Data template for arrows --> <DataTemplate x:Key="ArrowsDataTemplate" DataType="{x:Type core:ArrowItem}"> <!-- Create here your template for arrows --> </DataTemplate> <!-- create data templates for other stuff and then "selector" data template --> <DataTemplate x:Key="ContentDataTemplate"> <ContentPresenter x:Name="itemContentPresenter" ContentTemplate="{StaticResource CircleDataTemplate}" <!-- just the default one --> Content="{TemplateBinding Content}"/> <DataTemplate.Triggers> <DataTrigger Binding="{Binding MyItemTypeAsEnum}" Value="Arrow"> <Setter TargetName="itemContentPresenter" Property="ContentTemplate" Value="{StaticResource ArrowsDataTemplate"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> <!-- Now in your control (for example content control), where you show this stuff, do: --> <ContentControl ContentTemplate="{StaticResource ContentDataTemplate}"/>
现在,我假设您有一个带有 属性 MyItemTypeAsEnum 的基础项目,它将为您提供 CircleItem 的 Circle、ArrowItem 的 Arrow 等。但是如果您没有这样的 属性,您应该能够从您的视图模型中获取布尔值,该值将告诉您该项目是否为圆形等。
在您的主控件中,当您显示 thigs 时,您必须将 ContentTemplate 设置为您的 "selector" 模板。
确保在将视图模型分配给 DataContext 之前初始化 Shapes
属性。集合属性应该都是只读的,否则您将不得不从它们的设置器中触发 属性 更改通知。
public class ViewModel
{
public ObservableCollection<CircleItem> CircleItems { get; }
= new ObservableCollection<CircleItem>();
public ObservableCollection<ArrowItem> ArrowItems { get; }
= new ObservableCollection<ArrowItem>();
public CompositeCollection Shapes { get; }
= new CompositeCollection();
public ViewModel()
{
Shapes.Add(new CollectionContainer { Collection = CircleItems });
Shapes.Add(new CollectionContainer { Collection = ArrowItems });
}
}