WPF DataGrid CanUserAddRows = True
Asked Answered
C

2

7

I seem to have a problem with adding rows to a DataGridthrough the interface itself. Here is a screenshot of the UI:

Screenshot

As you can see, 0 rows were found in the database so nothing shows up in the DataGrid on the right side. But id like there to be one empty row there, for manually adding rows. The DataGrid.CanUserAddRows is set to True but has no effect. Here is the xaml for the DataGrid, I have taken the liberty of removing some of the code to make it smaller.

PrivilegeDetailsView.xaml

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

Here is the ViewModel (VM) for the xaml View above:

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

This is the VM for each row in the DataGrid

RoleHasPrivilegeOnObjectEntityViewModel.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();
        }
    }

    ...
}

I know this is a lot of code, I have stripped away a lot and put in place a few dots (...) to show that more code exist. NOTE: Im using EF5 and PRISM

How can I make the DataGrid accept new rows through the GUI?

Complicity answered 13/1, 2014 at 6:37 Comment(0)
C
5

What I ended up doing was partially/Mostly what Maverik suggested.

I changed ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> to be ObservableCollection<RoleHasPrivilegeOnObjectEntityViewModel> and created a default constructor, which it didn't previously have.

The issue then was that the RoleHasPrivilegeOnObjectEntityViewModel needs some fields and properties set in order to function, so I created a public Initialize function to provided the necessary parameters.

I added an event handler to the DataGrid's InitializingNewItem event, where i called the Initialize function.

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

When trying to adda new row, clicking the ComboBox didn't fire off the InitializeNewItem event. But clicking any other column fired off the InitializeNewItem event, and since at first each Row's VM had it's own AvailableObjectTypes property, the ComboBox ItemSource was not set if the ComboBox was selected before any other column, thus making it empty.

That was not an acceptable behaviour so moving AvailableObjectTypes to the PrivilegeDetailsViewModel and changing the ComboBox's ItemSource binding to this helped

ItemsSource="{Binding DataContext.AvailableObjectTypes, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Complicity answered 15/1, 2014 at 14:26 Comment(0)
A
15

I believe your problem is using ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> as ItemsSource. In order for DataGrid to be able to create a new row, there has to be a type that can be constructed with an empty constructor.

If you changed it to say ObservableCollection<RoleHasPrivilegeOnObjectEntityViewModel> instead, i'm pretty sure your rows will start getting added.

Ancohuma answered 13/1, 2014 at 10:48 Comment(2)
And also the class needs a Default Constructor (empty)? Or maybe I can listen to an even or something for my own implementation for creating a new row, as my VM for each row kinda needs some parameters when constructed.Complicity
In that case, I guess you could use the CellEditEnding event and add a row manuallyAncohuma
C
5

What I ended up doing was partially/Mostly what Maverik suggested.

I changed ObservableCollection<IRoleHasPrivilegeOnObjectListItemViewModel> to be ObservableCollection<RoleHasPrivilegeOnObjectEntityViewModel> and created a default constructor, which it didn't previously have.

The issue then was that the RoleHasPrivilegeOnObjectEntityViewModel needs some fields and properties set in order to function, so I created a public Initialize function to provided the necessary parameters.

I added an event handler to the DataGrid's InitializingNewItem event, where i called the Initialize function.

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

When trying to adda new row, clicking the ComboBox didn't fire off the InitializeNewItem event. But clicking any other column fired off the InitializeNewItem event, and since at first each Row's VM had it's own AvailableObjectTypes property, the ComboBox ItemSource was not set if the ComboBox was selected before any other column, thus making it empty.

That was not an acceptable behaviour so moving AvailableObjectTypes to the PrivilegeDetailsViewModel and changing the ComboBox's ItemSource binding to this helped

ItemsSource="{Binding DataContext.AvailableObjectTypes, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Complicity answered 15/1, 2014 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.