Привязка команд MVVM к моделям вложенных представлений - PullRequest
2 голосов
/ 04 апреля 2011

Я использую модели представлений на диаграмме классов ниже для представления табеля рабочего времени с использованием DataGrid.

Верхний класс (ActivityCollectionViewModel) - это DataContext для сетки; набор действий (ActivityViewModel), который он содержит, представляет собой отдельные позиции в сетке. У действия есть коллекция выделений (AllocationViewModel), которые составляют большинство ячеек (столбцов) DataGrid отдельных позиций.

Обратите внимание, что у AllocationVm (ячейка) есть собственная команда MakeFullDayCommand. В текущем проекте у меня есть эквивалентные команды как у родителя AllocationVm, так и у его деда. Я сделал это таким образом, думая, что смогу связать команду дедушки и бабушки, а затем использовать возможность collectionViewSource для поддержки выбранных vms chiild так, чтобы команда правильной ячейки всегда вызывалась.

На практике отслеживать сбивает с толку, и у меня возникают проблемы с привязкой в ​​одиночку, чтобы все было синхронизировано, поэтому я прибегнул к нескольким кодам за взломами в DataGrid, как показано ниже.

Поэтому я решил отступить и посмотреть, не может ли кто-нибудь предложить более простой и эффективный дизайн, чем тот, который у меня есть, или подтвердить, что это жизнеспособное решение и поможет мне разработать более эффективную стратегию привязки данных.

Приветствия
Berryl

Как это работает

Нижняя команда в контекстном меню ниже - это вложенная команда, о которой я говорю.

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 Ответ

1 голос
/ 04 апреля 2011

По своему опыту работы с сеткой данных (и что-то похожее на вашу) я с трудом пытался привязать ее к столбцам с помощью вложенных моделей представления.В прошлый раз, когда я пытался использовать его, я закончил тем, что скачал источник сетки данных и переписал ее, чтобы поддерживать связывание так, как мне нужно.Если бы я мог начать все сначала, я бы просто написал свой с нуля с моей ограниченной функциональностью.

Кроме того, может быть полезно взглянуть на другой способ отображения ваших данных для конечного пользователя, который может работатьнемного лучше в пользовательском опыте и кодировании и тестируемости.Похоже, пользователю будет сложно взглянуть на сетку и подумать: «Я должен щелкнуть правой кнопкой мыши по столбцу, чтобы получить полный рабочий день».

Кроме того, одним из достоинств WPF является возможность ДЕЙСТВИТЕЛЬНО легко выполнять управление.Может быть, это может быть лучший маршрут для вас?

...