嵌套视图模型的MVVM命令绑定

时间:2011-04-04 15:27:08

标签: wpf silverlight data-binding mvvm

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

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

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

在实践中,跟踪是很困惑的,我无法单独绑定以保持所有内容同步,因此我在DataGrid中使用了几个代码,如下所示。

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

干杯,
Berryl

工作原理

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

enter image description here

代码背后

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

    /// <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);
            }
        }
    }

enter image description here

1 个答案:

答案 0 :(得分:1)

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

除此之外,查看向最终用户显示数据的不同方式可能是有益的,这可能在用户体验,编码和可测试性方面更好一些。看起来用户很难看到网格,并认为“我应该右键点击列来制作一整天。”

此外,WPF的一部分优点是能够轻松制作控件。也许这对你来说可能是更好的选择?