【问题标题】:Binding data from view to ItemsPanelTemplate将数据从视图绑定到 ItemsPanelTemplate
【发布时间】:2014-04-10 14:00:39
【问题描述】:

我一直在开发我的第一个 MVVM WPF 应用程序,我想在其中绘制一个包含节点和边的图形。目前,我正在视图后面的代码中执行所有绘图逻辑,迭代节点,相应地创建形状并将它们添加到画布中。

因为我不想跟踪形状,只想根据给定的数据(即节点)绘制它们,所以我决定创建一个 ObservableCollectionNodesEdges,并将ItemsControl 绑定到这些以自动绘制形状。

现在我专注于绘制节点,并提出了以下 XAML 代码:

<ItemsControl x:Name="ItemsControlCanvas" ItemsSource="{Binding Path=Nodes}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas>
                <Canvas.LayoutTransform>
                    <ScaleTransform ScaleX="{Binding ?}" ScaleY="{Binding ?}"/>
                </Canvas.LayoutTransform>
            </Canvas>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Ellipse Fill="Blue" Width="4" Height="4" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Top" Value="{Binding Path=Position.Y}" />
            <Setter Property="Canvas.Left" Value="{Binding Path=Position.X}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

在我看来,当用户在画布上滚动时,我设置了两个属性 ScaleXScaleY。在我早期版本的代码中,我将使用这些属性创建一个 ScaleTransform,并使用 LayoutTransform 将其应用于命名的画布。

public partial class GraphOverview : Page
{
    public double ScaleX { get; set; }
    public double ScaleY { get; set; }

这个版本的问题是我无法让它在我的新 XAML 代码中工作。我希望ItemsPanelTemplateScaleTransformScaleXScaleY 属性绑定到我认为的属性。只是,正常的ElementName 绑定由于某种原因不起作用。更清楚地说,画布可能不知道视图,我假设因为它是一个模板。此外,我无法从后面的代码中调用画布,即使它有名称。

我尝试了几种解决方案,摸索RelativeResources 等,但我想我不明白为什么ItemsPanelTemplate 与XAML 代码中的其他所有内容如此脱节。提前致谢!

更新

也许我应该澄清一下,通过将 ScaleXScaleY 属性移动到 ViewModel 并绑定到这些属性,可以轻松解决此问题。但在我看来,此类特定于 View 的属性不应驻留在 ViewModel 中。

【问题讨论】:

    标签: c# wpf mvvm binding


    【解决方案1】:

    当您说视图特定信息不应该在视图模型中时,您是正确的。但是在您的情况下,位置成为模型的一部分,因为位置只不过是您的视图应该表现的数据。在这种情况下,您可以将它们视为模型而不是视图的一部分。编写一个单独的模型类并在您的 ViewModel 类的可观察集合中使用它。我希望它能消除你的疑问。

    public class Node
    {
        public double ScaleX { get; set; }
        public double ScaleY { get; set; }
    }
    

    编辑
    回答您的问题。 -

    但是 Scale 属性指定了 Canvas 的行为方式,那么 View 是否还没有作为“模型”呢?创建一个新模型只是为了设置单个画布的比例属性对我来说似乎是多余的?视图应该只关心绘图数据,并且由于属性作为这些绘图的修改,它们属于视图,不是吗?

    您想根据提供的数据绘制图表。 Node 类将为您提供数据。它存储数据而不是画布应如何呈现的逻辑,XAML 代码使用此数据并具有在视图中显示它的逻辑。

    那么 View 还没有充当“模型”吗?

    您尝试的实现在视图中具有模型,其想法是将模型与视图分离。最初创建一个类来保存 2 个双精度值可能看起来是多余的。但是,如果您将它们从视图中抽象出来,则会有一些优势。

    • 在视图模型中,您可以为此节点类(模型)创建一个可观察的集合。如果 View 中有 ScaleX 和 ScaleY,则无法从 Viewmodel 访问它。
    • 将来您可能希望将 ScaleX 和 ScaleY 更改为不同的比例,例如对数刻度/不同的单位。在这种情况下,您必须更改 ViewModel 中的逻辑才能这样做,而不必担心更改 View。但如果视图中有这个 Observable 集合,则必须更改视图才能更改数据/模型。
    • 最后 - 您可以为 ViewModel 中的任何内容编写单元测试,但不能为 View 编写单元测试。

    通常 ScaleX 和 ScaleY 将成为视图的一部分,但在您的情况下,它们会更改并存储数据。因此,您需要将此 ScaleX 和 ScaleY 抽象到不同的层中,以保留 MVVM 概念。

    【讨论】:

    • 但是 Scale 属性指定了 Canvas 的行为方式,那么 View 是否还没有作为“模型”呢?创建一个新模型只是为了设置单个画布的比例属性对我来说似乎是多余的?视图应该只关心绘图数据,并且由于属性作为这些绘图的修改,它们属于视图,不是吗?
    • 感谢您进一步解释您的回答。你有一些很好的论据,并且会使用你的解决方案。
    【解决方案2】:

    我不确定您是否设置了视图的 DataContext,这将有助于显示您的更多代码

    internal ViewModel viewModel { get; set; }
    
    public View()
    {
          DataContext = (viewModel = new ViewModel());
    }
    

    您是否将 ObservableCollection 绑定到节点的 ItemSource?我不熟悉绘画,但是当我绑定到孩子时,我从来没有取得过成果。

    但我只开发了大约 4 个使用 MVVM 的项目。

    【讨论】:

    • 我可以使用ItemSource 成功绘制节点,如我的问题中所述。 Path=Nodes 指的是我的 GraphOverview ViewModel 中的 ObservableCollection&lt;Node&gt; Nodes 属性。但是我需要值的 ScaleXScaleY 属性存在于 View 本身的代码中,而不是 ViewModel 中。希望这能让事情变得更清楚。
    猜你喜欢
    • 2013-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-29
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 2017-10-25
    相关资源
    最近更新 更多