【问题标题】:WPF/MVVM - binding collection of child controlsWPF/MVVM - 子控件的绑定集合
【发布时间】:2016-07-13 12:00:10
【问题描述】:

请从他的 WPF/MVVM 专家那里得到一些建议。

我来自 Windows 窗体背景,我正在将个人项目从 VB.NET 移植到 WPF。我从直接重写代码隐藏文件中的逻辑开始,尽管使用了离散的数据访问层。但是后来我发现了 MVVM,并且我“做错了”,应该将逻辑抽象为查看模型访问数据模型。我想我已经吸收了新的范例,我的文件背后的代码现在(几乎)是空的。

(顺便说一句,我认同 MVVM 背后的基本原理,但由于路由事件和命令等的所有“管道”,我不确定“更容易测试和调试”的论点,我很欣赏需要,但在我看来,有时会模糊而不是澄清正在发生的事情。但我是一个非常新手,也许它会随着练习而来。)

无论如何 - 这就是让我摸不着头脑的原因;该应用程序是水肺潜水中心的潜水计划/记录工具。从逻辑上讲,它就像一个日历或日记应用程序,需要记录每次潜水的时间和地点以及每次潜水的人员。主屏幕类似于日记的一页,我有一个封装潜水信息的用户控件。更改所选日期时,日记页面将被清除并重新填充新所选日期的潜水。视图模型检索并公开新日期的“潜水”对象(类)列表。该容器是一个 Stackpanel,我想清除它的子控件,然后创建新的 Dive 用户控件并将其添加到子控件集合中。

我的问题是 - 执行此操作的合适位置在哪里 - 查看背后的代码或查看模型?前者相对容易,但在我看来打破了这种模式,但我对如何在视图模型中实现它感到困惑。顺序需要是

  • 用户选择新日期(日历控件)
  • 处理选定的日期更改事件
  • 清除 Stackpanel 子控件
  • 从 db 中检索新日期的潜水列表并为每个潜水生成一个用户控件
  • 将用户控件添加为 stackpanel 子控件。

我想我要问的是 - 有没有办法将子控件集合绑定到视图模型中的对象集合,以便动态响应 - 这是一种明智的方法吗?

【问题讨论】:

  • 您在底部的列表是 100% winforms 和 0% mvvm。在尝试重写之前,您可能想要启动几个原型,在其中进行非常琐碎的 MVVM 工作,作为了解如何完成不同任务的一种方式。如果您不了解 mvvm 在 wpf 中的工作原理,请不要尝试在生产代码中学习它。
  • 谢谢 Will,我确实承认我是 MVVM 新手,并且我已经开发了一些“玩具”应用程序。仍然对绑定的力量感到困惑..
  • 更多玩具应用!我已经保存了我的所有。有数百个不同的主题。这是一种很好的学习方式。

标签: wpf mvvm data-binding


【解决方案1】:

执行此操作的适当方法是将StackPanel 包装在ItemsControl 中,并将ItemsSource 绑定到所选日期的Dives。当您的DivesOfThatDay 发生更改时(确保实现 INotifyChanged 或 DependencyProperty,或使用 ObservableCollection),将自动生成每个条目。

<ItemsControl ItemsSource="{Binding DivesOfThatDay}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type Dive}">
            <!-- Your Template -->
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

在 MVVM 中,您应该始终避免在代码隐藏中生成控件。您的 View 应该从您的 ViewModel 中获取数据并自行完成 - 否则 MVVM 将毫无用处。在您的情况下,它应该像这样工作:

  1. 用户在视图中选择一个新日期
  2. 因为 view 中的日期控件绑定到 viewmodel 中的某些属性,所以该属性会更新。
  3. viewmodel 通过清除其当前的潜水集合并获取新的潜水来对这一变化做出反应。
  4. model 数据(来自数据库)被封装在 viewmodelDive)中并添加到 viewmodel 中的潜水集合中强>
  5. 视图 (ItemsControl) 收到有关此更改的通知(通过INotifyPropertyChangedINotifyCollectionChanged 或其他方式)并告诉其ItemContainerGenerator 更新控件。
  6. ItemsContainerGenerator 为 viewmodel 中的每个 Dive 生成一个 view,并将其添加到堆栈面板中。

或者简而言之V→VM→M→VM→V

【讨论】:

  • 我建议他将Dives 设为ObservableCollection,在新的集合实例上引发PropertyChanged,不要介意其他方法。不会出错的。
  • @EdPlunkett "Can't" 可能有点夸张。我看到很多人使用dataThatDoesntUpdate = new ObservableCollection ... 并想知道他们做错了什么。但我同意 - ObservableCollection 或任何其他实现 INotifyCollectionChanged 的类都非常适合这种情况。他的基本做法Retrieve list of dives for new date from db and generate a user control for each绝对不应该手动完成。
  • 没错,没有什么是不能永远做错的。完全同意使用 ItemsControl+DataTemplate 创建控件。
  • 如果您要更改集合,请使用 ObservableCollection。如果您只是替换集合,只需使用 INPC IEnumerable 属性。两者都做……有点傻。如果我有一个 OC 属性,它是只读的,并且不实现属性更改通知。
  • 出色的曼弗雷德。这听起来很到位。我会试一试。感谢您将我推向更高的学习曲线。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
  • 2015-09-15
相关资源
最近更新 更多