WPF Canvas子绑定ObservableCollection可以包含不同形状和TextBlocks的ViewModel吗?

时间:2016-08-22 08:47:14

标签: c# wpf canvas mvvm data-binding

我试图创建一个WPF画布控件,它可以绘制形状,曲线和文本块,选择和删除它们,所以我能想到这个工作的最好方法是使用MVVM绑定一个视图模型列表帆布儿童。

然而,Canvas Children无法直接绑定。所以我有点卡住了。看过互联网,但没有找到直接的答案。我发现有人建议采用以下方法来实现绑定Canvas Children,但我无法理解ViewModels" Nodes"是,以及如何将不同的形状,曲线和TextBlocks转换为节点。

任何帮助将不胜感激。提前致谢。

<ItemsControl ItemsSource="{Binding Path=Nodes}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Path=XPos}" />
            <Setter Property="Canvas.Top" Value="{Binding Path=YPos}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

1 个答案:

答案 0 :(得分:2)

您的视图模型应包含定义NodeXPos属性的基类YPos,以及特定节点类型的派生类,例如: TextNodeShapeNode

public class Node
{
    public double XPos { get; set; }
    public double YPos { get; set; }
}

public class TextNode : Node
{
    public string Text { get; set; }
}

public class ShapeNode : Node
{
    public Geometry Geometry { get; set; }
    public Brush Stroke { get; set; }
    public Brush Fill { get; set; }
}

public class ViewModel
{
    public ObservableCollection<Node> Nodes { get; } = new ObservableCollection<Node>();
}

在XAML中,您将为特定节点类型添加DataTemplates,如下所示。有关详细信息,请参阅MSDN上的Data Templating Overview文章。

<ItemsControl ItemsSource="{Binding Path=Nodes}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:TextNode}">
            <TextBlock Text="{Binding Text}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:ShapeNode}">
            <Path Data="{Binding Geometry}" Stroke="{Binding Stroke}" Fill="{Binding Fill}"/>
        </DataTemplate>
    </ItemsControl.Resources>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Path=XPos}" />
            <Setter Property="Canvas.Top" Value="{Binding Path=YPos}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

您现在可以向主视图模型类的实例添加不同的节点,并将窗口的DataContext设置为该实例:

public MainWindow()
{
    InitializeComponent();

    var vm = new ViewModel();

    vm.Nodes.Add(new TextNode
    {
        XPos = 50,
        YPos = 100,
        Text = "Hello, World."
    });

    vm.Nodes.Add(new ShapeNode
    {
        XPos = 100,
        YPos = 200,
        Geometry = new EllipseGeometry { RadiusX = 50, RadiusY = 50 },
        Fill = Brushes.Red
    });

    DataContext = vm;
}

如果希望视图对节点的属性更改做出反应,Node类应实现INotifyPropertyChanged接口。

如果项目应该是可选择的,则应该用ListBox替换ItemsControl。 ItemContainerStyle的TargetType将是ListBoxItem,您可以将其IsSelected属性绑定到Node类上的相应属性。