Search code examples
c#wpfdatagridobservablecollectionitemsource

User editable datagrid with empty row, even if observableCollection is empty


I'm using a datagrid, bound to an observablecollection with TwoWay binding. My goal is, that a user generates a list of data, starting from an empty collection. So I enabled the option CanUserAddRow.

In the code, I generate the obsevrable collection with the following code:

private ObservableCollection<Ticket> idlessTicketList = new ObservableCollection<Ticket>();

The Ticket class, which the ObservableCollection consists of, looks as follows:

public class Ticket
{
    public Ticket() { }

    public bool ticketUsed { get; set; }
    public string ticketNumber { get; set; }
    public string ticketCustomer { get; set; }
    public string ticketText { get; set; }
    public double ticketTime { get; set; }
    public Decimal ticketTypeNr { get; set; }
    public string ticketTypeText { get; set; }

}

In the MainWindow Method I set the itemSource of my Datagrid to my ObservableCollection:

public MainWindow()
{

    InitializeComponent();
    gridIdlessTickets.ItemsSource = idlessTicketList;

}

My problem is now, that the empty row to add a new row is not displayed at startup. If I add a new row by code myGridd.Add(row), then the empty row is displayed correctly and everythings works a expected.

How must the ObservableCollection be initialized and referenced to the itemSource correctly? Where is the best place to initialize an itemSource?

Thanks in advance


Solution

  • This should work for you. Let me know if it helped:

    XAML:

        <Window>
          <Grid>
            <Datagrid ItemsSource="{Binding idlessTicketList }" SelectionMode="Single" SelectionUnit="Cell" IsReadOnly="False" 
                      CanUserAddRows="True" CanUserDeleteRows="False" AutoGenerateColumns="False">
    
            //ColumnDefinition...etc
                    <DataGridTextColumn Header="TicketNumber" Binding="{Binding TicketNumber}"  />
                    <DataGridTextColumn Header="TicketCustomer" Binding="{Binding TicketCustomer}"
            </Datagrid>
    <Button name="ThisIsYourUpdateButton" Command="{Binding UpdateMyTicket}" Width="200" Content="Update me now"/>
          </Grid>
        </Window>
    

    Code Behind (.xaml.cs):

    public MainWindow()
    {
    
        InitializeComponent(); //parses the XAML...
        DataContext = new MainWindowViewModel(); //outsources the view code to the    
        //"controller" to make the view only display and nothing else...
    
    }
    

    ViewModel: (MainWindowViewModel.cs)

            using System;
            using System.Collections.ObjectModel;
            using System.ComponentModel;
            using System.Windows;
    
            namespace YourNameSpace
            {
                public class MainWindowViewModel : INotifyPropertyChanged
                {
                    public ICommand UpdateMyTicket => new DelegateCommand<object>(ExecuteUpdateTicket);
                    public Ticket TicketInstance {get;set;}
                    public event PropertyChangedEventHandler PropertyChanged;
                    public virtual void OnPropertyChanged(string propertyName)
                    {
                        if (this.PropertyChanged != null)
                        {
                            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                        }
                    }
    
                    private ObservableCollection<Ticket> _idlessTicketList;
                    public ObservableCollection<Ticket> idlessTicketList
                    {
                        get { return _idlessTicketList; }
                        set
                        {
                            _idlessTicketList = value;
                            OnPropertyChanged("idlessTicketList");
                        }
                    }
    
                    //Constructor
                    public MainWindowViewModel()
                    {
                        idlessTicketList = new ObservableCollection<Ticket>();
                    }
                    public void ExecuteUpdateTicket(obj param)
                    {
                      //if the button is clicked you update your Ticket class properties here!
                    }
                 }
              }
    

    Add DelegateCommand.cs class like this:

        using System;
    
    namespace YourNamespaceName 
    { 
        public class DelegateCommand<T> : System.Windows.Input.ICommand
        {
            private readonly Predicate<T> _canExecute;
            private readonly Action<T> _execute;
    
            public DelegateCommand(Action<T> execute)
                : this(execute, null)
            {
            }
    
            public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
            {
                _execute = execute;
                _canExecute = canExecute;
            }
    
            public bool CanExecute(object parameter)
            {
                if (_canExecute == null)
                    return true;
    
                return _canExecute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
            }
    
            public void Execute(object parameter)
            {
                _execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
            }
    
            public event EventHandler CanExecuteChanged;
            public void RaiseCanExecuteChanged()
            {
                if (CanExecuteChanged != null)
                    CanExecuteChanged(this, EventArgs.Empty);
            }
        }
    }