【问题标题】:Binding Shapes.Path items to a ItemsControl将 Shapes.Path 项绑定到 ItemsControl
【发布时间】:2015-06-10 17:39:52
【问题描述】:

我一直在试图弄清楚如何将ObservableCollection<FrameworkElements> 绑定到 ItemsControl。我有一个现有项目,它严重依赖背后的代码和没有绑定的画布,我正在尝试更新它以使用 mvvm 和 prism。

ObservableCollection 将填充许多 Path 项。它们是从我使用的外部库生成的。当我手动操作画布本身时,库可以正常工作。

这是来自 ViewModel 的代码的 sn-p:

    ObservableCollection<FrameworkElement> _items;
    ObservableCollection<FrameworkElement> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            this.NotifyPropertyChanged("Items");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

支持 XAML

    <ItemsControl ItemsSource="{Binding Items}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas x:Name="canvas" IsItemsHost="True">
                        <Canvas.Background>
                            <SolidColorBrush Color="White" Opacity="100"/>
                        </Canvas.Background>

                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Path/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

我遇到的问题是路径从不绘制。关于我哪里出错以及从哪里开始调试过程有什么建议吗?

【问题讨论】:

  • 原始实例有效,我只是在声明集合时忘记了public。

标签: c# wpf mvvm prism


【解决方案1】:

您的视图模型应包含路径的抽象表示,例如

public class PathData
{
    public Geometry Geometry { get; set; }
    public Brush Fill { get; set; }
    public Brush Stroke { get; set; }
    public double StrokeThickness { get; set; }
    // ... probably more Stroke-related properties
}

如果你现在有一个 PathData 对象的集合,比如

public ObservableCollection<PathData> Paths { get; set; }

您的 ItemsControl 可能有一个 ItemsTemplate,如下所示:

<ItemsControl ItemsSource="{Binding Paths}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Path Data="{Binding Geometry}" Fill="{Binding Fill}"
                  Stroke="{Binding Stroke}" StrokeThickness="{Binding StrokeThickness}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

您现在可以像这样添加 PathData 实例:

Paths.Add(new PathData
{
    Geometry = new RectangleGeometry(new Rect(100, 100, 100, 100)),
    Fill = Brushes.AliceBlue,
    Stroke = Brushes.Red,
    StrokeThickness = 2
});

【讨论】:

  • 你在你的 ViewModel 中创建 UI 并让我对使用路径数据大发雷霆?老兄。
  • @Will 此视图模型中没有 UI。只是几何图形和画笔,它们都不是 UIElement。
  • 大声笑,Geometry 是一个 WPF 类。好了,剪完头发。先生,你好。
  • @Will 当然可以,就像视图模型中常见的许多其他 WPF 类型一样,例如颜色、画笔、ImageSource、Transform 或任何其他可以合理绑定视图属性的类型。
  • 我正在使用的库正在生成路径元素。将这些 Path 对象中的几何图形提取到第二个类中,然后在 UI 中重新创建该类与直接将预先生成的类添加到 UI 中有何不同?
【解决方案2】:

ObservableCollection&lt;FrameworkElement&gt;是的,你可以停在那里。那不是 MVVM。此外,您将项目模板定义为(空白,我猜您可以说)路径对象。您不会将其绑定到您放入可观察集合中的路径的属性。

一个更好的实现是有一个包含路径 data 的 ObservableCollection&lt;string&gt;,AKA the stuff that actually defines the path's shape,然后将 this 绑定到您的路径.

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas x:Name="canvas" IsItemsHost="True" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Path Data="{Binding}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

如果您在运行时使用 Snoop 检查您的 UI,您会看到有很多 Path 对象,但它们都有空数据。

另外,如果您为路径形状使用血腥画布,则需要在路径上设置 Canvas.Left 和 Canvas.Top 附加属性。一个真正的 MVVM 英雄会这样定义一个模型

public PathData{
    public double Left {get;set;}
    public double Top {get;set;}
    public string Data {get;set;}
}

然后在您的 ViewModel 中公开这些

public ObservableCollection<PathData> Items {get;private set;}

然后将你的路径绑定到它们

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Path Canvas.Left="{Binding Left}"
              Canvas.Top="{Binding Top}"
              Data="{Binding Data}"/>
    </DataTemplate>
</ItemsControl.ItemTemplate>

【讨论】:

  • Data 已经包含最终坐标时,为什么要设置 Left 和 Top?没有 Left 和 Top 的 Canvas 非常有意义,因为它不会缩放或平移其子级。
  • @Clemens 是吗?还是它们都将在画布中以 0,0 的原点进行渲染?如果您不使用画布来控制项目的位置,为什么还要使用画布?
  • 您正在通过 Data 属性中的 Geometry 控制位置。例如。一个以 (100, 100) 为中心的 EllipseGeometry。那不是在 (0, 0) 处呈现的,对吧?
  • @Clemens 你在假设他想要做什么?我不是。在我的示例中,我在转换之前使用 Data 值,即 svg 格式(或与之密切相关)的文本。你说的是 svg 被转换成什么。
  • 您的假设是每个数据都相对于 Left 和 Top 指定的某个位置,我的假设是每个数据都相对于 (0, 0)。 IMO 后者更简单,这就是 OP 试图做的。而且数据当然不应该是一个字符串,而是一个几何。这不是 SVG,而是 WPF。
猜你喜欢
  • 2016-08-12
  • 1970-01-01
  • 2011-01-09
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多