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
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.
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:
Silverlight