WPF数据表格CanUserAddRows属性为True

7
我似乎在通过界面向DataGrid添加行时遇到了问题。以下是UI的截图:

Screenshot

如您所见,数据库中没有找到任何行,因此右侧的DataGrid中没有显示任何内容。但我希望有一个空行,以便手动添加行。 DataGrid.CanUserAddRows设置为True,但没有效果。 以下是DataGridxaml代码,我已经删除了一些代码以使其更小。
<UserControl ...
             d:DataContext="{d:DesignInstance impl:PrivilegeDetailsViewModel}">
    <DataGrid ...
              ItemsSource="{Binding RolesHasPrivilegesOnObjects}" 
              AutoGenerateColumns="False"
              CanUserAddRows="True">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Type" CanUserSort="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="{x:Type int:IRoleHasPrivilegeOnObjectListItemViewModel}">
                        <Image Source="{Binding Icon}" ToolTip="{Binding ToolTip}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTextColumn Width="*" Header="Name" Binding="{Binding Name}"/>
            <DataGridCheckBoxColumn Header="Select" Binding="{Binding HasSelect, UpdateSourceTrigger=PropertyChanged}">
                <DataGridCheckBoxColumn.ElementStyle>
                    <Style TargetType="CheckBox">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding CanHaveSelect}" Value="True">
                                <Setter Property="IsEnabled" Value="True"/>
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding CanHaveSelect}" Value="False">
                                <Setter Property="IsEnabled" Value="False"/>
                                <Setter Property="HorizontalAlignment" Value="Center"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </DataGridCheckBoxColumn.ElementStyle>
            </DataGridCheckBoxColumn>
            ...
        </DataGrid.Columns>
    </DataGrid>
</UserControl>

PrivilegeDetailsView.xaml.cs

public partial class PrivilegeDetailsView : IPrivilegeDetailsView
{
    public PrivilegeDetailsView() { InitializeComponent(); }

    public DataGrid PrivilegesOnObjectsDataGrid { get { return PrivilegeDataGrid; } }

    public IViewModel ViewModel
    {
        get { return (IViewModel)DataContext; }
        set { DataContext = value; }
    }
}

这是上面那个xaml视图的ViewModel(VM):
PrivilegeDetailsViewModel.cs
public class PrivilegeDetailsViewModel : ViewModelBase, IPrivilegeDetailsViewModel
{
    private readonly IEventAggregator _eventAggregator;
    private readonly IPrivilegeViewModel _privilegeViewModel;
    private readonly IRoleHasPrivilegeOnObjectViewModelAdapterRepository _roleHasPrivilegeOnObjectViewModelAdapterRepository;
    private ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> _rolesHasPrivilegesOnObjects;

    public PrivilegeDetailsViewModel(IPrivilegeDetailsView view,
                                     IRoleHasPrivilegeOnObjectViewModelAdapterRepository roleHasPrivilegeOnObjectViewModelAdapterRepository,
                                     IPrivilegeViewModel privilegeViewModel,
                                     IEventAggregator eventAggregator) : base(view)
    {
        _roleHasPrivilegeOnObjectViewModelAdapterRepository = roleHasPrivilegeOnObjectViewModelAdapterRepository;
        _privilegeViewModel = privilegeViewModel;
        _eventAggregator = eventAggregator;
        Initialize();
    }

    protected override sealed void Initialize()
    {
        _privilegeViewModel.PropertyChanged += PrivilegeViewModelOnPropertyChanged;
        _eventAggregator.GetEvent<ToggleSelectPrivilegeEvent>().Subscribe(ToggleSelectPrivilege);
        ...
    }

    public new IPrivilegeDetailsView View
    {
        get { return (IPrivilegeDetailsView)base.View; }
    }

    public ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> RolesHasPrivilegesOnObjects
    {
        get { return _rolesHasPrivilegesOnObjects; }
        set
        {
            _rolesHasPrivilegesOnObjects = value;
            OnPropertyChanged();
        }
    }

    public void Save()
    {
        if(RolesHasPrivilegesOnObjects == null) return;
        _roleHasPrivilegeOnObjectViewModelAdapterRepository.SaveChanges(RolesHasPrivilegesOnObjects);
    }        

    private void ToggleExecutePrivilege(object obj)
    {
        var toggle = !View.PrivilegesOnObjectsDataGrid.SelectedItems.Cast<IRoleHasPrivilegeOnObjectListItemViewModel>()
                          .All(x => x.HasExecute);
        foreach(var selectedItem in View.PrivilegesOnObjectsDataGrid
                                        .SelectedItems
                                        .Cast<IRoleHasPrivilegeOnObjectListItemViewModel>()
                                        .Where(selectedItem => selectedItem.Object
                                                                           .CanHavePrivilege("EXECUTE"))) {
                                            selectedItem.HasExecute = toggle; 
                                        }
    }

    ...

    private void PrivilegeViewModelOnPropertyChanged(object s, PropertyChangedEventArgs e)
    {
        switch(e.PropertyName)
        {
            //When the SelectedSchema changes in the parent VM, I get the new rows to be shown in the DataGrid.
            case "SelectedSchema":
                RolesHasPrivilegesOnObjects = _roleHasPrivilegeOnObjectViewModelAdapterRepository
                                                  .GetPrivilegesOnObjectsAssociatedWith((IRoleEntityViewModel)_privilegeViewModel.SelectedRole,
                                                  (IContainerEntityViewModel)_privilegeViewModel.SelectedSchema);
                break;
        }
    }
}

这是DataGrid中每一行的VMRoleHasPrivilegeOnObjectEntityViewModel.cs
public class RoleHasPrivilegeOnObjectEntityViewModel : EntityViewModelBase<RoleHasPrivilegeOnObjectEntityViewModel, 
                                                       RoleHasPrivilegesOnObject>, 
                                                       IRoleHasPrivilegeOnObjectListItemViewModel
{
    private readonly RoleHasPrivilegesOnObject _roleHasPrivilegesOnObject;

    public RoleHasPrivilegeOnObjectEntityViewModel(RoleHasPrivilegesOnObject roleHasPrivilegesOnObject)
    {
        _roleHasPrivilegesOnObject = roleHasPrivilegesOnObject;
        Role = new RoleEntityViewModel(_roleHasPrivilegesOnObject.Role);
        Object = new ObjectEntityViewModel(_roleHasPrivilegesOnObject.Object);
    }

    public override EntityType EntityType { get { return EntityType.NONE; } }

    public override RoleHasPrivilegesOnObject OriginalEntity { get { return _roleHasPrivilegesOnObject; } }

    public IRoleEntityViewModel Role { get; set; }

    public IObjectEntityViewModel Object { get; set; }

    public string ToolTip { get { return _roleHasPrivilegesOnObject.ToolTip; } }

    public bool HasExecute
    {
        get { return _roleHasPrivilegesOnObject.HasExecute; }
        set
        {
            _roleHasPrivilegesOnObject.HasExecute = value;
            OnPropertyChanged();
        }
    }

    public bool CanHaveExecute { get { return _roleHasPrivilegesOnObject.CanHaveExecute; } }

    public override string Icon { get { return Object != null ? Object.Icon : string.Empty; } }

    public override string NAME
    {
        get { return _roleHasPrivilegesOnObject.NAME; }
        set
        {
            _roleHasPrivilegesOnObject.NAME = value;
            OnPropertyChanged();
        }
    }

    ...
}

我知道这是很多代码,我已经删除了很多并用几个点 (...) 表示还有更多的代码。注意:我正在使用EF5PRISM

如何通过GUI使 DataGrid 接受新行?

2个回答

15

我认为你的问题是使用了 ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> 作为 ItemsSource。为了让 DataGrid 能够创建新行,必须有一个可以使用空构造函数构造的类型。

如果你改成使用 ObservableCollection<RoleHasPrivilegeOnObjectEntityViewModel>,我非常确定你的行将开始添加。


这个类需要一个默认构造函数(空的)吗?或者我可以监听一个事件之类的,为了我的自己创建新行的实现,因为每一行的VM在构造时都需要一些参数。 - furier
在这种情况下,我想你可以使用 CellEditEnding 事件并手动添加一行。 - Maverik

5
我最终做的部分/大部分是Maverik所建议的。
我将 ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> 更改为 ObservableCollection<RoleHasPrivilegeOnObjectEntityViewModel>,并创建了一个默认构造函数,以前它没有。
然后问题就是需要设置一些字段和属性才能让RoleHasPrivilegeOnObjectEntityViewModel正常运行,所以我创建了一个公共的Initialize函数来提供必要的参数。
我在DataGridInitializingNewItem事件中添加了一个事件处理程序,其中调用了Initialize函数。
private void PrivilegesOnObjectsDataGridOnInitializingNewItem(object s, InitializingNewItemEventArgs e)
{
    var newItem = e.NewItem as RoleHasPrivilegeOnObjectEntityViewModel;
    if (newItem == null) return;
    var role = _privilegeViewModel.SelectedRole;
    var schema = _privilegeViewModel.SelectedSchema;        
    newItem.Initialize(role.OriginalEntity, schema.OriginalEntity);
}

尝试添加新行时,单击 ComboBox 没有触发 InitializeNewItem 事件。但单击任何其他列都会触发 InitializeNewItem 事件。由于一开始每个 RowVM 都有自己的 AvailableObjectTypes 属性,如果在任何其他列之前选择 ComboBox,则不会设置 ComboBoxItemSource,从而使其为空。
这种行为是不可接受的,因此将 AvailableObjectTypes 移动到 PrivilegeDetailsViewModel 中,并更改 ComboBoxItemSource 绑定可以解决该问题。
ItemsSource="{Binding DataContext.AvailableObjectTypes, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"

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