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