Я использую модели представлений на диаграмме классов ниже для представления табеля рабочего времени с использованием DataGrid.
Верхний класс (ActivityCollectionViewModel) - это DataContext для сетки; набор действий (ActivityViewModel), который он содержит, представляет собой отдельные позиции в сетке. У действия есть коллекция выделений (AllocationViewModel), которые составляют большинство ячеек (столбцов) DataGrid отдельных позиций.
Обратите внимание, что у AllocationVm (ячейка) есть собственная команда MakeFullDayCommand. В текущем проекте у меня есть эквивалентные команды как у родителя AllocationVm, так и у его деда. Я сделал это таким образом, думая, что смогу связать команду дедушки и бабушки, а затем использовать возможность collectionViewSource для поддержки выбранных vms chiild так, чтобы команда правильной ячейки всегда вызывалась.
На практике отслеживать сбивает с толку, и у меня возникают проблемы с привязкой в одиночку, чтобы все было синхронизировано, поэтому я прибегнул к нескольким кодам за взломами в DataGrid, как показано ниже.
Поэтому я решил отступить и посмотреть, не может ли кто-нибудь предложить более простой и эффективный дизайн, чем тот, который у меня есть, или подтвердить, что это жизнеспособное решение и поможет мне разработать более эффективную стратегию привязки данных.
Приветствия
Berryl
Как это работает
Нижняя команда в контекстном меню ниже - это вложенная команда, о которой я говорю.
Код позади
Этот код неприятен и его сложно проверить!
/// <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);
}
}
}