【问题标题】:MVVM command binding of nested view models嵌套视图模型的 MVVM 命令绑定
【发布时间】:2011-07-29 06:49:16
【问题描述】:

我正在使用下面类图中的视图模型使用 DataGrid 进行时间表演示。

顶层类(ActivityCollectionViewModel)是网格的DataContext;它持有的活动集合(ActivityViewModel)是网格中的行项目。该活动有一个分配集合 (AllocationViewModel),它们是行项目 DataGrid 单元格(列)的大部分。

请注意,AllocationVm(单元格)有它自己的命令,MakeFullDayCommand。在当前的设计中,我在 AllocationVm 的父级和祖父级都有等效的命令。我这样做是因为我认为我可以绑定祖父母的命令,然后使用 collectionViewSource 的能力来维护选定的子虚拟机,以便始终调用正确的单元格命令。

实际上,跟踪很混乱,而且我无法单独绑定以保持所有内容同步,因此我在 DataGrid 中使用了一些代码,如下所示。

所以我想我会退后一步,看看是否有人可以提出比我所拥有的更简单更有效的设计,或者确认这是一个可行的解决方案并帮助我制定更好的数据绑定策略。

干杯,
浆果

工作原理

下面上下文菜单中的底部命令是我所指的嵌套命令。

代码隐藏

这段代码丑陋且难以测试!

    /// <summary>
    /// Synchronize the <see cref="ActivityViewModel.SelectedAllocationVm"/> here so the input binding
    /// key (F8) is always working on the correct command.
    /// </summary>
    private void OnCurrentCellChanged(object sender, EventArgs e)
    {
        if (sender == null) return;
        var grid = (DataGrid)sender;
        if (grid.CurrentColumn == null) return;
        var selectedActivity = (ActivityViewModel)grid.CurrentItem;

        if (_isEditableDayOfTheWeekColumn(grid.CurrentColumn))
        {
            var dowCol = (DayOfTheWeekColumn)grid.CurrentColumn;
            var index = Convert.ToInt32(dowCol.DowIndex);
            selectedActivity.SetSelectedAllocationVm(index);
        }
        else
        {
            selectedActivity.SetSelectedAllocationVm(-1);
        }
    }

    /// <summary>
    /// Invoke the MakeFullDayCommand when the user double clicks an editable cell; 
    /// synchronize the selected allocation view model first.
    /// </summary>
    private void OnDoubleClick(object sender, MouseButtonEventArgs e)
    {
        if (sender == null) return;
        var grid = (DataGrid)sender;
        if (grid.CurrentColumn == null) return;

        if (!_isEditableDayOfTheWeekColumn(grid.CurrentColumn)) return;

        var selectedActivity = (ActivityViewModel) grid.CurrentItem;
        var dowCol = (DayOfTheWeekColumn)grid.CurrentColumn;
        var index = Convert.ToInt32(dowCol.DowIndex);
        var allocationVm = selectedActivity.SetSelectedAllocationVm(index);
        if (allocationVm.MakeFullDayCommand.CanExecute(null))
        {
            allocationVm.MakeFullDayCommand.Execute(null);
        }
    }

    /// <summary>
    /// Manipululate the context menu to show the correct description of the MakeFullDayCommand.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Controls.ContextMenuEventArgs"/> instance containing the event data.</param>
    void OnContextMenuOpening(object  sender, ContextMenuEventArgs e) {
        if (sender == null) return;
        var grid = (DataGrid)sender;
        if (grid.CurrentColumn == null) return;

        const int INDEX_OF_MAKE_FULL_DAY_CMD = 1;
        if (_isEditableDayOfTheWeekColumn(grid.CurrentColumn)) {
            var selectedActivity = (ActivityViewModel) grid.CurrentItem;
            var dowCol = (DayOfTheWeekColumn) grid.CurrentColumn;
            var index = Convert.ToInt32(dowCol.DowIndex);
            var allocationVm = selectedActivity.SetSelectedAllocationVm(index);
            var menuItem = allocationVm.MakeFullDayCommand.ToMenuItem();

            if (grid.ContextMenu.Items.Count == 1) {
                Log.Info("{0}", allocationVm.MakeFullDayCommand.HeaderText);
                grid.ContextMenu.Items.Add(menuItem);
            }
            else {
                var currentItem = (MenuItem) grid.ContextMenu.Items.GetItemAt(INDEX_OF_MAKE_FULL_DAY_CMD);
                if (currentItem.Command != menuItem.Command) {
                    // remove the outdated menu item before adding back the new one
                    grid.ContextMenu.Items.Remove(currentItem);
                    grid.ContextMenu.Items.Add(menuItem);
                }
            }
        }
        else
        {
            if (grid.ContextMenu.Items.Count == 2)
            {
                // we aren't on an editable cell - remove the command altogether
                grid.ContextMenu.Items.RemoveAt(INDEX_OF_MAKE_FULL_DAY_CMD);
            }
        }
    }

【问题讨论】:

    标签: wpf silverlight data-binding mvvm


    【解决方案1】:

    根据我对数据网格(以及您的数据网格)的经验,我很难尝试通过嵌套视图模型将其绑定到列。上次我尝试使用它时,我最终下载了数据网格的源并重写了一堆以支持我需要的绑定方式。如果我可以重新开始,我会用我有限的功能从头开始编写自己的。

    除此之外,了解向最终用户显示数据的不同方式可能会有所帮助,这种方式在用户体验、编码和可测试性方面可能会更好一些。似乎用户很难查看网格并认为“我应该右键单击该列以度过一整天”。

    此外,WPF 的部分优点在于能够非常轻松地进行控制。也许这对您来说可能是一条更好的路线?

    【讨论】:

    • 我已经在使用子类 DataGrid,因为我需要三个独立但相关的用于此演示文稿。不确定我是否有能力编写一种新的控件,或者它最终会与 DataGrid 有多大不同。您有什么想法可以作为起点吗?
    • @Berryl - 如果没有该领域的专业知识,真的很难说。 IMO,网格用于数据人员会查看的核心数据。看起来你在那里有一个星期,一周有多个可能的“行”。也许以天/时间为中心的东西会更合适?内置日历(您可以先为此创建一个新模板)可能是您的起点。
    • 顺便说一句,现在似乎应该有 somebody 已经为 DataGrid 获得了良好的 mvvm 设计模式。很难想象一个非平凡的应用程序不会以一些视图模型嵌套到单元级别而告终。干杯
    猜你喜欢
    • 2010-11-28
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 2013-05-31
    • 1970-01-01
    • 2011-10-07
    相关资源
    最近更新 更多