如何在MVVM友好的方法中向DataGrid添加新行

7

我有以下的DataGrid

<DataGrid CanUserDeleteRows="True" 
          CanUserAddRows="True"
          SelectedItem="{Binding SelectedResource, Mode=TwoWay}"
          ItemsSource="{Binding Path=Resources, Mode=TwoWay,
                                UpdateSourceTrigger=PropertyChanged, 
                                IsAsync=True}"> ... </<DataGrid>

我正在使用MVVM模式绑定到一个ObservableCollection<ResourceViewModel> Resources,这很好用。我有一个按钮可以添加新行,通过向Resources集合中添加新的ResourceViewModel来完成 - 非常好。现在,我希望用户能够单击空白的最后一行,这将自动在DataGrid中创建一个新记录。

我确保DataGrid具有CanUserAddRows=True。我确保绑定到的集合ResourcesResourceViewModel)中的类具有默认构造函数(无参数),并且我确保集合类型不是只读的。当用户单击最后一行时,会触发默认构造函数,但为了正确实例化新的ResourceViewModel对象,我需要引用Resources集合的网格或...

我想我可以在CellBeginEdit事件上使用AttachedCommand,然后在那里向可观察集合中添加新的ResourceViewModel是否有标准方法可以做到这一点?

注意,我已阅读以下问题,但这些对我没有帮助

  1. WPF DataGrid - 新行事件?
  2. 如何使用DataGrid和MVVM添加行

编辑。事实证明,由于WPF DataGrid中的一个错误,我正在做这个方面遇到问题。请参见Nigel Spencer's Blog。然而,他的修复方法目前对我无效...


Nigel Spencer的博客文章更新链接为Problems binding to SelectedValue with WPF DataGrid - Tone
2个回答

3
据我所知,您知道如何正确地向您的数据绑定集合中添加新项,以导致在DataGrid中添加新项,而您的问题实际上涉及到用户如何单击DataGrid中的最后一行来执行此操作。处理视图模型中的某些事件的一般方法是创建一个附加属性(如果不存在),该属性会为您处理该事件。
例如,您可以创建一个附加属性,将处理程序附加到相关事件,并另外创建一个类型为ICommand的属性,在调用事件处理程序时可以执行该命令。然后,您可以在您的视图模型中编写该ICommand的功能(其中添加一个新项到您的数据绑定集合),并将您的ICommand实现数据绑定到附加ICommand属性
因为我相当确定您知道如何定义附加属性,所以我不会对此进行解释。如果我误解了您或没有让自己清楚,请告诉我。

1
谢谢回复。问题是我设置了一个附加命令在“InitializingNewItem”事件上触发。当我将DataGrid的最后一行置于编辑模式并在网格尝试创建新项之前,它就会触发。在这里,我可以添加新项,但DataGrid继续尝试使用默认构造函数实例化项。我希望能够阻止网格自动执行此操作,因为我可以使用命令和事件来完成我想要的操作... - MoonKnight
那么问题就变成了,最好的方法是什么?A. 在添加新行后取消事件(如何取消我不知道)。B. 让网格自己完成这个操作 - 但是如何正确初始化默认构造函数中的对象?有没有标准的做法? - MoonKnight
很遗憾,我很少使用这些控件,因此不能为您提供高级建议。祝你好运解决问题。 - Sheridan

2

这是一个附加属性,用于注册添加行的命令(假设源集合包含一个通用类型参数):

public static readonly DependencyProperty RegisterAddCommandProperty = DependencyProperty.RegisterAttached("RegisterAddCommand", typeof(bool), typeof(DataGridExtensions), new PropertyMetadata(false, OnRegisterAddCommandChanged));
public static bool GetRegisterAddCommand(DependencyObject obj)
{
    return (bool)obj.GetValue(RegisterAddCommandProperty);
}
public static void SetRegisterAddCommand(DependencyObject obj, bool value)
{
    obj.SetValue(RegisterAddCommandProperty, value);
}
static void OnRegisterAddCommandChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if (sender is DataGrid)
    {
        var DataGrid = sender as DataGrid;
        if ((bool)e.NewValue)
            DataGrid.CommandBindings.Add(new CommandBinding(AddCommand, AddCommand_Executed, AddCommand_CanExecute));
    }
}

public static readonly RoutedUICommand AddCommand = new RoutedUICommand("AddCommand", "AddCommand", typeof(DataGridExtensions));
static void AddCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    var DataGrid = sender as DataGrid;

    var ItemsSourceType = DataGrid.ItemsSource.GetType();
    var ItemType = ItemsSourceType.GetGenericArguments().Single();

    DataGrid.Items.Add(Activator.CreateInstance(ItemType));
}
static void AddCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = (sender as DataGrid).CanUserAddRows;
}

然后,您可以将该命令应用于某个按钮,例如:

<Button Command="{x:Static Extensions:DataGridExtensions.AddCommand}"/>

不要忘记指定命令的目标。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接