Here’s what I did. I took the WPF DataGrid and used it in my project. More specifically;
Then add a reference to the DataGrid’s assembly ( I have mine in d:\wpftoolkit\ );
Then “built a UI” which is an overstatement 🙂
<Window x:Class="BlogPost.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="600" Width="800" xmlns:grid="http://schemas.microsoft.com/wpf/2008/toolkit"> <Grid> <Grid.Resources> <Style x:Key="myStyle" TargetType="{x:Type Control}"> <Setter Property="Margin" Value="10" /> <Setter Property="FontSize" Value="16" /> </Style> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="4*" /> <RowDefinition /> </Grid.RowDefinitions> <TextBox x:Name="txtCountry" Style="{StaticResource myStyle}" Text="{Binding Country}" /> <grid:DataGrid x:Name="dataGrid" Grid.Row="1" Style="{StaticResource myStyle}" AutoGenerateColumns="True" ItemsSource="{Binding}" /> <Button Grid.Row="2" Style="{StaticResource myStyle}" Content="Submit Changes" Click="OnSubmitChanges" /> </Grid> </Window>
And then added some LINQ to SQL classes from my Northwind database;
and dropped in all the tables;
then went and edited my code behind for my XAML file;
public partial class Window1 : Window, INotifyPropertyChanged { public Window1() { InitializeComponent(); this.Loaded += OnLoaded; } public string Country { get { return (country); } set { country = value; Requery(); FirePropertyChanged("Country"); } } void Requery() { dataGrid.DataContext = dataContext.Customers.Where(c => c.Country == txtCountry.Text); } void OnLoaded(object sender, RoutedEventArgs e) { dataContext = new NorthwindDataContext(); txtCountry.DataContext = this; } void FirePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } void OnSubmitChanges(object sender, EventArgs args) { // This needs error handling in the real world. dataContext.SubmitChanges(); } NorthwindDataContext dataContext; string country; public event PropertyChangedEventHandler PropertyChanged; }
and what surprised me was that I got more than I bargained for with this DataGrid.
I expected it to behave just like the one in Silverlight 2 Beta 2 ( it may have changed for the RC, I haven’t checked yet ) in that I expected the grid to do a fine job of displaying (and updating) the data but I expected it to do nothing around insert or delete because that was my previous experience with the Silverlight 2 Beta 2 DataGrid.
However, here it’s different!
Firstly, I should say that Update works. That is, I can go and bring up the UI, set a country (by typing and tabbing).
Note that my Northwind DB is very much broken in terms of the data it contains but, regardless, I can make an update;
and then click “Submit Changes” and that just works.
However, I can also do an insert by typing into the blank row at the bottom of the grid to create a new row (note – it’s a bit weird to create a new row here because I have a filter for Spain so I really should force the Country to be Spain or at least default it). I’ll come back to that in a second. In the meantime I can do;
and then click Submit Changes and that submits a record for me and I can also highlight that new row,
and then press the DELete key and then “Submit Changes” and that deletes the row for me.
Magic! But how on Earth does that work?
The first thing to think about is that I’m setting the DataContext on the DataGrid to an IQueryable<Customer> and then the DataGrid is going to cause the query to be enumerated. How does it do that?
I think what happens is that we set the DataContext to a DataQuery<Customer> which is an internal class of LINQ to SQL implementing IQueryable. This also implements IListSource and the DataGrid calls into that implementation’s GetList() function which looks to ultimately cause the creation of something that is derived from BindingList<Customer> and from thereon we’re in business in that the DataGrid can (and does) call into InsertItem, RemoveItem in order to do its Inserts/Deletes.
I guess the last mystery is how I ensure that when someone has typed “Spain” into my UI then if they insert a row then the country should be set to “Spain” ( at the very least ).
I found a handy InitializingNewItem event and used that to initialise things (just pasting the 1 new/1 modified function below);
void OnLoaded(object sender, RoutedEventArgs e) { dataContext = new NorthwindDataContext(); txtCountry.DataContext = this; dataGrid.InitializingNewItem += OnGridRowInsert; } void OnGridRowInsert(object sender, InitializingNewItemEventArgs e) { ((Customer)e.NewItem).Country = txtCountry.Text; }