Manish Dalal's blog

Exploring .net!

Silverlight Business Application Part 1: Add new item

This is part one of Building Business Application with Silverlight series that showcases the basic building blocks of a data centric application. 

In classical applications, the DataGrid or similar UI control takes over the control of data and servers as both the visualizer and data container. When writing logic in such applications, developer deals with DataGrid directly. However with XAML (Silverlight/WPF) there is more stricter separation of duties, UI provides data visualization and all manipulation is done to the data itself.

In order to add new item (row to DataGrid), you have to first add new item to underlying data source, People. When a new Person is added to Person collection (People), a new row appears in the DataGrid. Behind the scenes, when you add new item to the People, the observable collection fires collection changed event that triggers DataGrid to update the UI.

Add a new button to UI by expanding root grid to two rows and adding stack panel to hold the button as shown

<UserControl x:Class="Silverlight.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    >
    <Grid x:Name="LayoutRoot" Background="White" Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button x:Name="addButton" Content="Add" Margin="5"/>
        </StackPanel>
        <data:DataGrid x:Name="peopleDataGrid" Grid.Row="1" />
    </Grid>
</UserControl>

Add handler for button click event in code behind

public Page() {
    InitializeComponent();
    this.Loaded += new RoutedEventHandler(Page_Loaded);
    this.addButton.Click += new RoutedEventHandler(addButton_Click);
}

void addButton_Click(object sender, RoutedEventArgs e) {
    _data.Add(new Person());
}

F5 and run the application. Now when you click Add button a new empty row is added to the DataGrid

image

You can also setup keyboard handlers to allow user to insert item.

public Page() {
    InitializeComponent();
    this.Loaded += new RoutedEventHandler(Page_Loaded);
    this.addButton.Click += new RoutedEventHandler(addButton_Click);
    this.peopleDataGrid.KeyDown += new KeyEventHandler(peopleDataGrid_KeyDown);
}

void peopleDataGrid_KeyDown(object sender, KeyEventArgs e) {
    if (Key.Insert == e.Key) {
        _data.Add(new Person());
    }
}

Add New Item – Take 2

Instead of allowing user to add infinite empty rows, another option is to always provide empty row at the end to allow user to add data. When that row is utilized, a new row is automatically added (similar to behavior seen in Sql Server Management Studio when editing data in a table).

To see this in action, just add following code to the People class (and comment out/remove all previous Add functionality)

private Person emptyPerson;

public People() {
    emptyPerson = new Person();
    emptyPerson.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(emptyPerson_PropertyChanged);
    base.InsertItem(this.Count, emptyPerson);
}

void emptyPerson_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
    emptyPerson.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(emptyPerson_PropertyChanged);
    emptyPerson = new Person();
    emptyPerson.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(emptyPerson_PropertyChanged);
    base.InsertItem(this.Count, emptyPerson);
}

protected override void InsertItem(int index, Person item) {
    if (index >= this.Count) {
        index = this.Count - 1;
        if (index < 0) index = 0;
    }
    base.InsertItem(index, item);
}

Here we are creating and tracking a emptyPerson object. Whenever user makes change to any of the emptyPerson properties, we add another new emptyPerson to the list at the end. Note that InsertItem is also overridden to ensure that all other items are inserted above the last item, which is emptyPerson.

We will also need to modify Page.xaml.cs to instruct DataGrid to commit changes when we automatically add new empty person

Add collection changed event handler for People as

void Page_Loaded(object sender, RoutedEventArgs e) {
    _data = People.GetTestData();
    this.peopleDataGrid.ItemsSource = _data;
    this._data.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_data_CollectionChanged);
}

void _data_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
    this.peopleDataGrid.EndEdit(true, true);
}

When you run the application, a new empty line is always positioned at the end.

image

One issue with above solution is that it will create new empty row even if user just changes a single property and then backs out of the change. A more elegant solution would be to insert new row only when user has committed changes. We will revisit Add and enhance it to provide this behavior when we implement IEditableObject interface in future.

Technorati Tags:

Comments

Michael Washington said:

Thank you for this. Iobservable collection is not a simple subject to understand.

# August 26, 2008 11:46 AM

manish.dalal said:

adefwebserver,

you are welcomed. I plan to post more details about other interfaces as well, inclusing IEditableObject

Elijah Manor,

There are couple of third party DataGrids... though it is quite easy to support filtering and searching(LINQ). As for paging, you can code that very easily, just call back end web service and add new rows to existing collection. You do not need paging with full client

# August 27, 2008 8:27 PM

... said:

Nice site you have!

# January 26, 2009 12:58 PM

... said:

Sehr wertvolle Informationen! Empfehlen!

# March 12, 2009 6:28 PM

Credit Risk Evaluation said:

Thats really very nice blog, I am impressed.

# November 20, 2009 6:34 AM

Sara said:

in silverlight 4, for "this.peopleDataGrid.EndEdit(true, true);

" in "_data_CollectionChanged", I faced with this Error: System.Windows.Controls.DataGrid' does not contain a definition for 'EndEdit' and no extension method 'EndEdit' accepting a first argument of type 'System.Windows.Controls.DataGrid' could be found (are you missing a using directive or an assembly reference?

What I should do?!?

# May 10, 2010 9:08 PM