Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Silverlight 2 - Simple Editing of Web Service Data in a DataGrid

Blogs

Mike Taulty's Blog

Elsewhere

Archives

Somebody asked how you use the DataGrid in Silverlight 2 in order to load data from a web service, modify it and submit it back so I thought I'd spend 5 minutes experimenting with that.

I wrote a very simple web service with WCF;

[DataContract]
public class Person
{
  [DataMember]
  public int Id { get; set; }
  [DataMember]
  public string FirstName { get; set; }
  [DataMember]
  public string LastName { get; set; }
  [DataMember]
  public int Age { get; set; }
}

[ServiceContract]
public interface IPeopleService
{
  [OperationContract]
  List<Person> GetPeople();

  [OperationContract]
  void UpdatePeople(List<Person> inserts, List<Person> updates,
    List<Person> deletes);
}

and implemented it in the simplest possible way ( no thread-safety, no concurrency );

public class PeopleService : IPeopleService
{
  static PeopleService()
  {
    people = new Dictionary<int, Person>();
    people.Add(1, new Person() { Id = 1, FirstName = "Mike", LastName = "Taulty", Age = 18 });
    people.Add(2, new Person() { Id = 2, FirstName = "Mike", LastName = "Ormond", Age = 78 });
    people.Add(3, new Person() { Id = 3, FirstName = "Daniel", LastName = "Moth", Age = 101});
  }
  public List<Person> GetPeople()
  {
    return (people.Values.ToList());
  }
  public void UpdatePeople(List<Person> inserts, List<Person> updates, List<Person> deletes)
  {
    if (inserts != null)
    {
      foreach (Person p in inserts)
      {
        p.Id = nextId++;
        people.Add(p.Id, p);
      }
    }
    if (deletes != null)
    {
      foreach (Person p in deletes)
      {
        people.Remove(p.Id);
      }
    }
    if (updates != null)
    {
      foreach (Person p in updates)
      {
        Person updateEntry = people[p.Id];
        updateEntry.FirstName = p.FirstName;
        updateEntry.LastName = p.LastName;
        updateEntry.Age = p.Age;
      }
    }
  }
  private static Dictionary<int, Person> people;
  private static int nextId = 4;
}

and I made sure that the web.config file that had been edited by the tool when I inserted the WCF service had basicHttpBinding rather than the wsHttpBinding you get by default.

From there, I added in a Silverlight project and created a simple UI.

image

It's just a DataGrid and a couple of buttons. I added a service reference to my WCF service in order to get a proxy class to use to call the service asynchronously.

In order to populate the DataGrid, I make a call to my webservice, get the data, convert the results into an ObservableCollection<T> where T is a local class that I called DataRow;

  public enum RowState
  {
    Clean,
    Modified,
    Inserted
  }
  public class DataRow
  {
    public DataRow()
    {
      State = RowState.Clean;
    }
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public RowState State { get; set; }
  }

and then I just set up the UI so that it data binds 4 text box columns to the properties on my DataRow class.

I use that RowState enum to determine for each row whether it has been modified or inserted and I also keep a separate List<DataRow> for any rows that have been deleted.

From a UI perspective, I couldn't find a simple property which I could set to get the DataGrid to display a "blank insert row" so I just added a keyboard handler for the Insert key and the Delete key and that's how I handle insert/delete from the UI. For insert, I just add a blank DataRow instance into the ObservableCollection.

At the point where the "Submit Changes..." button is pressed I just gather up which entries have been inserted, updated, deleted as arrays and send them back over to the web service.

So...all in all, there's no rocket-science here at all. The XAML for my UI ends up looking like this;

<UserControl x:Class="SilverlightApplication14.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition
        Height="48" />
      <RowDefinition
        Height="48" />
    </Grid.RowDefinitions>
    <Grid
      x:Name="gridLoading"
      Opacity="0.5"
      Visibility="Collapsed">
      <Rectangle
        Fill="Silver"
        RadiusX="10"
        RadiusY="10"/>
      <TextBlock
        FontSize="24"
        Text="Loading Grid..."
        HorizontalAlignment="Center"
        VerticalAlignment="Center" />
    </Grid>
    <d:DataGrid 
      x:Name="theGrid"
      Margin="10">
      <d:DataGrid.Columns>
        <d:DataGridTextBoxColumn
          Header="Id"
          DisplayMemberBinding="{Binding Id}" 
          IsReadOnly="True"/>
        <d:DataGridTextBoxColumn
          Header="First Name"
          DisplayMemberBinding="{Binding FirstName}" />
        <d:DataGridTextBoxColumn
          Header="Last Name"
          DisplayMemberBinding="{Binding LastName}" />
        <d:DataGridTextBoxColumn
          Header="Age"
          DisplayMemberBinding="{Binding Age}" />
      </d:DataGrid.Columns>
    </d:DataGrid>
    <Button
      Click="OnLoad"
      Grid.Row="1"
      Content="Load Data from Service"
      Margin="10" />
    <Button
      Click="OnSave"
      Grid.Row="2"
      Content="Submit Changes to Service" 
      Margin="10"/>
  </Grid>
</UserControl>

and the code behind the UI ends up looking like this ( pretty rough and ready stuff this );

  public partial class Page : UserControl
  {
    public Page()
    {
      InitializeComponent();
      
      this.Loaded += OnLoaded;
      theGrid.CommittingCellEdit += OnCommittingCellEdit;
      theGrid.KeyDown += OnGridKeyDown;
      deletions = new List<DataRow>();
    }
    void OnCommittingCellEdit(object sender, DataGridCellCancelEventArgs e)
    {
      DataRow dr = e.Element.DataContext as DataRow;

      if ((dr != null) && (dr.State != RowState.Inserted))
      {
        dr.State = RowState.Modified;
      }
    }
    void OnGridKeyDown(object sender, KeyEventArgs e)
    {
      if (e.Key == Key.Delete)
      {
        DataRow dr = theGrid.SelectedItem as DataRow;

        if (dr != null)
        {
          data.Remove(dr);

          if (dr.State != RowState.Inserted)
          {
            deletions.Add(dr);
          }
        }
      }
      else if (e.Key == Key.Insert)
      {
        data.Add(new DataRow()
        {
          State = RowState.Inserted
        });
      }
    }
    void OnLoaded(object sender, RoutedEventArgs e)
    {
      data = new ObservableCollection<DataRow>();
      theGrid.DataContext = data;
      theGrid.ItemsSource = data;
    }
    void OnSave(object sender, EventArgs args)
    {
      var deleted = 
        deletions.ConvertAll<Person>(DataRowToPerson).ToArray();

      var updated = (from u in data
                    where u.State == RowState.Modified
                    select DataRowToPerson(u)).ToArray();

      var inserted = (from i in data
                     where i.State == RowState.Inserted
                     select DataRowToPerson(i)).ToArray();

      PeopleServiceClient proxy = new PeopleServiceClient();
      proxy.UpdatePeopleCompleted += OnUpdatesCompleted;
      proxy.UpdatePeopleAsync(inserted, updated, deleted);
    }
    void OnUpdatesCompleted(object sender, AsyncCompletedEventArgs e)
    {
      OnLoad(null, null);
    }
    static Person DataRowToPerson(DataRow row)
    {
      return (new Person()
      {
        Id = row.Id,
        FirstName = row.FirstName,
        LastName = row.LastName, 
        Age = row.Age 
      });
    }
    static DataRow PersonToDataRow(Person person)
    {
      return (new DataRow()
      {
        Id = person.Id,
        FirstName = person.FirstName,
        LastName = person.LastName,
        Age = person.Age
      });
    }
    void OnLoad(object sender, EventArgs args)
    {
      data.Clear();
      deletions.Clear();
      gridLoading.Visibility = Visibility.Visible;

      PeopleServiceClient proxy = new PeopleServiceClient();
      proxy.GetPeopleCompleted += OnGetPeopleCompleted;
      proxy.GetPeopleAsync();
    }
    void OnGetPeopleCompleted(object sender, GetPeopleCompletedEventArgs e)
    {
      gridLoading.Visibility = Visibility.Collapsed;

      foreach (var person in e.Result)
      {
        data.Add(PersonToDataRow(person));
      }
    }
    private ObservableCollection<DataRow> data;
    private List<DataRow> deletions;
  }

Note that at some point I expect that you'll have to use Dispatcher.Invoke to get back to the UI thread when these asynchronous calls complete but in the current beta it seems that you don't have to do that. Maybe it'd be better practise to put it in anyway to future proof the code a bit but, as I say, that code is pretty hacky anyway :-)

I've uploaded the whole project here in case it might help anybody.


Posted Thu, Mar 27 2008 4:09 PM by mtaulty
Filed under:

Comments

Silverlight 2 - Simple Editing of Web Service Data in a DataGrid wrote Silverlight 2 - Simple Editing of Web Service Data in a DataGrid
on Thu, Mar 27 2008 11:18 PM
&lt;p&gt;Somebody asked how you use the DataGrid in Silv
ScottGu's Blog wrote March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET
on Fri, Mar 28 2008 1:08 AM
Here is the latest in my link-listing series .&amp;#160; Also check out my ASP.NET Tips, Tricks and Tutorials
BusinessRx Reading List wrote March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET
on Fri, Mar 28 2008 1:16 AM
Here is the latest in my link-listing series .&amp;#160; Also check out my ASP.NET Tips, Tricks and Tutorials
Mirrored Blogs wrote March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET
on Fri, Mar 28 2008 1:53 AM
Here is the latest in my link-listing series .&amp;#160; Also check out my ASP.NET Tips, Tricks and Tutorials
どっとねっとふぁんBlog wrote WCFとSilverlightのDataGridの連携
on Fri, Mar 28 2008 2:02 AM
Silverlight 2 - Simple Editing of Web Service Data in a DataGrid  これでデータの追加/更新/削除もサポートされてるのかな?   ちゃんと試してみなければ...
Joycode@Ab110.com wrote 3月28日链接篇: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET
on Fri, Mar 28 2008 10:33 PM
【原文地址】 March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET 【原文发表日期
Hot Topics wrote Working with Silverlight 2.0's Data Grid
on Sat, Mar 29 2008 8:30 AM
Mike Taulty has a blog post about editing Web Service Data in a DataGrid in Silverlight 2.0
Hot Topics wrote More Silverlight 2.0 DataGrid posts
on Sat, Mar 29 2008 8:48 AM
Scott Guthrie lists a number of posts and videos about the Silverlight 2.0 DataGrid in March 28th Links
March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET « .NET Framework tips wrote March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET &laquo; .NET Framework tips
on Sun, Mar 30 2008 2:31 AM
Enlaces de Marzo: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET « Thinking in .NET wrote Enlaces de Marzo: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET &laquo; Thinking in .NET
on Sun, Mar 30 2008 3:58 AM
ScottGu's Blog wrote ScottGu Silverlight Reference Page
on Tue, Apr 1 2008 10:32 PM
I'll be using this page to link to Silverlight 2 articles and posts (both ones I write as well ones by
大宋提刑官 wrote Silverlight技巧,诀窍,教程和链接
on Tue, Apr 8 2008 10:03 PM
Silverlight技巧,诀窍,教程和链接 【原文地址】SilverlightTips,Tricks,TutorialsandLinksPage 我将使用本页链接到有关Silve...
ScottGu's Blog em Português wrote Links do dia 28 de Mar&#231;o: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET
on Fri, Apr 18 2008 1:55 PM
Aqui est&#225; a &#250;ltima vers&#227;o da minha lista de links . Verifique tamb&#233;m minha p&#225;gina de Dicas, Truques e
Silverlight Tips and Triks Link Page « Mandar Oak wrote Silverlight Tips and Triks Link Page &laquo; Mandar Oak
on Tue, May 6 2008 10:42 PM
Readed By Wrocław NUG members wrote March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET
on Sun, Jun 15 2008 8:30 AM
Here is the latest in my link-listing series .&amp;#160; Also check out my ASP.NET Tips, Tricks and Tutorials
c observablecollection wrote c observablecollection
on Wed, Jul 9 2008 5:41 PM
木野狐(Neil Chen) wrote Silverlight 文章收集
on Mon, Jul 21 2008 7:35 PM
(以下内容全部整理自博客堂Scottgu博客中文版)Silverlight技巧,诀窍,教程和链接 【原文地址】SilverlightTips,Tricks,...
NixusG wrote Silverlight 2: A developer's perspective
on Wed, Sep 17 2008 1:02 PM
Silverlight 2: A developer's perspective
truejob wrote Silverlight技巧,诀窍,教程和链接
on Mon, Jan 5 2009 8:19 PM

Silverlight技巧,诀窍,教程和链接

【原文地址】SilverlightTips,Tricks,TutorialsandLinksPage

我将使用本页链接到有关Silve...

Epic Destination » Silverlight 2: A developer’s perspective wrote Epic Destination &raquo; Silverlight 2: A developer&#8217;s perspective
on Sun, Sep 4 2011 1:09 PM

Pingback from  Epic Destination  » Silverlight 2: A developer’s perspective