我使用数据绑定在画布上绘制不同类型的路径。画布在ItemsControl中,我使用MiltiBinding转换器。
<ItemsControl x:Name="Items" ClipToBounds="True">
<ItemsControl.ItemsSource>
<MultiBinding Converter="{StaticResource CanvasDraw}">
<Binding Path="Coords" />
<Binding Path="Holes" />
<Binding Path="MagnetAreas" />
<Binding Path="PathElements" />
<Binding Path="Image" />
</MultiBinding>
</ItemsControl.ItemsSource>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center" Width="0" Height="0"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate x:Key="img">
<Image Source="{Binding Image}" Width="200" Height="100"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Coord}">
<Path Data="{Binding Geometry}" Style="{StaticResource Coord}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:Hole}">
<Path Data="{Binding Geometry}" Style="{StaticResource Hole}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:MagnetArea}">
<Path Data="{Binding Geometry}" Style="{StaticResource MagnetArea}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:PathElement}">
<Path Data="{Binding Geometry}" Style="{StaticResource PathElement}" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=PosX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=PosY}" />
<Setter Property="Panel.ZIndex" Value="{Binding Path=ZIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
我的ViewModel由4个不同类型的ObservableCollections(Coords、Holes、MagnetReas、PathElements(组成,每个都派生自同一个类(Element(,所以在转换器中,我只创建Element类型的组合集合。每个元素都有自己的属性Geometry,该属性绑定到DataTemplates的路径数据。
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
ObservableCollection<Element> combinedCollection = new();
if (values == null || values.Length <= 0)
return combinedCollection;
foreach (var element in (ObservableCollection<Coord>)values[0])
combinedCollection.Add(element);
foreach (var element in (ObservableCollection<Hole>)values[1])
combinedCollection.Add(element);
foreach (var element in (ObservableCollection<MagnetArea>)values[2])
combinedCollection.Add(element);
foreach (var element in (ObservableCollection<PathElement>)values[3])
combinedCollection.Add(element);
return combinedCollection;
}
到目前为止,一切都很顺利。但我也想在画布上画一幅图像。我想我也必须通过DataTemplate来做到这一点(手动将Image元素添加到画布中不起作用(,但我不知道如何更改我的Converter和绑定Paths来实现这一点,因为这个DataTemplate有不同的类型。属性ImageSource Image也在我的ViewModel中。上面的代码显然不起作用,但至少,当属性Image更改时,转换器是正确触发的。
这里不需要多重绑定。在运行时更改源集合时,它将无法正常工作。您需要使用CompositeCollection。使用它的一个小困难是它不可冻结,因此与默认源的绑定在其中不起作用。此外,您需要将单个属性转换为具有一个元素的IEnumerable。
下面是这样一个实现的例子:
<ItemsControl x:Name="Items" ClipToBounds="True"
ItemsControl="{DynamicResource collection}">
<ItemsControl.Resources>
<CompositeCollection x:Key="collection">
<CollectionContainer
Collection="{Binding DataContext.Coords,
Source={x:Reference Items}}"/>
<CollectionContainer
Collection="{Binding DataContext.Holes,
Source={x:Reference Items}}"/>
<CollectionContainer
Collection="{Binding DataContext.MagnetAreas,
Source={x:Reference Items}}"/>
<CollectionContainer
Collection="{Binding DataContext.PathElements,
Source={x:Reference Items}}"/>
<CollectionContainer
Collection="{Binding DataContext.Image,
Source={x:Reference Items},
Converter={local:ObjectToIEnumerable}}"/>
</CompositeCollection>
</ItemsControl.Resources>
[ValueConversion(typeof(object), typeof(IEnumerable))]
public class ObjectToIEnumerableConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new Enumer(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private struct Enumer : IEnumerable
{
private readonly object _value;
public Enumer(object value)
{
_value = value;
}
public IEnumerator GetEnumerator()
{
yield return _value;
}
}
public static ObjectToIEnumerableConverter Instance { get; } = new ObjectToIEnumerableConverter();
}
[MarkupExtensionReturnType(typeof(ObjectToIEnumerableConverter))]
public class ObjectToIEnumerableExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ObjectToIEnumerableConverter.Instance;
}
}
还有一个使用ContentPresenter的解决方案(用于ListBox-ListBoxItem(。但我还没有测试它的工作。
<CompositeCollection>
-------------
-------------
<ContentPresenter
Content="{Binding DataContext.Image,
Source={x:Reference Items}}"/>
</CompositeCollection>