Published Thursday, March 27, 2008 4:09 PM by mtaulty

Silverlight 2 - Simple Editing of Web Service Data in a DataGrid

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.


Filed Under:

# Silverlight 2 - Simple Editing of Web Service Data in a DataGrid @ Thursday, March 27, 2008 11:18 PM

&lt;p&gt;Somebody asked how you use the DataGrid in Silv

Silverlight 2 - Simple Editing of Web Service Data in a DataGrid

# March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET @ Friday, March 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

ScottGu's Blog

# March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET @ Friday, March 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

BusinessRx Reading List

# March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET @ Friday, March 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

Mirrored Blogs

# WCFとSilverlightのDataGridの連携 @ Friday, March 28, 2008 2:02 AM

Silverlight 2 - Simple Editing of Web Service Data in a DataGrid  これでデータの追加/更新/削除もサポートされてるのかな?   ちゃんと試してみなければ...

どっとねっとふぁんBlog

# 3月28日链接篇: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET @ Friday, March 28, 2008 10:33 PM

【原文地址】 March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET 【原文发表日期

Joycode@Ab110.com

# Working with Silverlight 2.0's Data Grid @ Saturday, March 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

# More Silverlight 2.0 DataGrid posts @ Saturday, March 29, 2008 8:48 AM

Scott Guthrie lists a number of posts and videos about the Silverlight 2.0 DataGrid in March 28th Links

Hot Topics

# March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET &laquo; .NET Framework tips @ Sunday, March 30, 2008 2:31 AM

PingBack from http://aspnetguru.wordpress.com/2008/03/29/march-28th-links-aspnet-aspnet-ajax-aspnet-mvc-visual-studio-silverlight-net/

March 28th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET « .NET Framework tips

# Enlaces de Marzo: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET &laquo; Thinking in .NET @ Sunday, March 30, 2008 3:58 AM

PingBack from http://thinkingindotnet.wordpress.com/2008/03/30/enlaces-de-marzo-aspnet-aspnet-ajax-aspnet-mvc-visual-studio-silverlight-net/

Enlaces de Marzo: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET « Thinking in .NET

# ScottGu Silverlight Reference Page @ Tuesday, April 01, 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

ScottGu's Blog

# Silverlight技巧,诀窍,教程和链接 @ Tuesday, April 08, 2008 10:03 PM

Silverlight技巧,诀窍,教程和链接 【原文地址】SilverlightTips,Tricks,TutorialsandLinksPage 我将使用本页链接到有关Silve...

大宋提刑官

# Links do dia 28 de Mar&#231;o: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Visual Studio, Silverlight, .NET @ Friday, April 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

ScottGu's Blog em Português

# Silverlight Tips and Triks Link Page &laquo; Mandar Oak @ Tuesday, May 06, 2008 10:42 PM

PingBack from http://manddar.wordpress.com/2008/05/07/silverlight-tips-and-triks-link-page/

Silverlight Tips and Triks Link Page « Mandar Oak