# Wednesday, 17 June 2009

We’ve added some new classes to the SoftSource training calendar, including a one-day Blend 2 class, and SQL 2008 for Developers.  Other offerings coming up are “Agile in a Day”, WPF, WCF, ASP.NET 3.5, LINQ, Silverlight, and of course, C#.  Discounts available for multiple students from the same organization, and custom on-site training also.

Wednesday, 17 June 2009 10:19:27 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 11 June 2009

A month or so ago I posted on a solution for simulating “default button” semantics in a Silverlight app, meaning that if you are entering text in a text box and you hit the enter key, the “default button” for the “page” should be pressed.  Very natural for form entry, etc. 

An issue came up (discovered by John Papa) with the solution in a Prism app, because my solution depends on being able to find the “default” button in the visual tree using the FindName method.  That means that you have to be high enough up the visual tree to find the button, since it only works “down” the tree.  In a Prism app, it’s not necessarily clear where “high enough” might be.  Plus, because the solution requires unique names, and Prism modules may have nothing to do with one another, they may have duplicate names, etc.

Here’s a revision to the solution that doesn’t require unique names, and doesn’t require any static references that might interfere with proper garbage collection…

First, a new object called DefaultButtonHub that keeps track of the relationship between text boxes and buttons.  It also exposes an Attached Property that takes a DefaultButtonHub reference so we can hook up text boxes and buttons to the “hub” in XAML.

public class DefaultButtonHub
{
   ButtonAutomationPeer peer = null;

   private void Attach(DependencyObject source)
   {
       if (source is Button)
       {
           peer = new ButtonAutomationPeer(source as Button);
       }
       else if (source is TextBox)
       {
           TextBox tb = source as TextBox;
           tb.KeyUp += OnKeyUp;
       }
       else if (source is PasswordBox)
       {
           PasswordBox pb = source as PasswordBox;
           pb.KeyUp += OnKeyUp;
       }
   }

   private void OnKeyUp(object sender, KeyEventArgs arg)
   {
       if(arg.Key == Key.Enter)
           if (peer != null)
               ((IInvokeProvider)peer).Invoke();
   }

   public static DefaultButtonHub GetDefaultHub(DependencyObject obj)
   {
       return (DefaultButtonHub)obj.GetValue(DefaultHubProperty);
   }

   public static void SetDefaultHub(DependencyObject obj, DefaultButtonHub value)
   {
       obj.SetValue(DefaultHubProperty, value);
   }

   // Using a DependencyProperty as the backing store for DefaultHub.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty DefaultHubProperty =
       DependencyProperty.RegisterAttached("DefaultHub", typeof(DefaultButtonHub), typeof(DefaultButtonHub), new PropertyMetadata(OnHubAttach));

   private static void OnHubAttach(DependencyObject source, DependencyPropertyChangedEventArgs prop)
   {
       DefaultButtonHub hub = prop.NewValue as DefaultButtonHub;
       hub.Attach(source);
   }

}

Basically we’re expecting that both the text boxes and the button will register themselves with the “hub”.  If it’s a button that’s being registered, we wrap it in a ButtonAutomationPeer so we can “press” it later.  If it’s a text box, we hook up a KeyUp handler that will “press” the button if it’s there.  The requirement in the XAML is only marginally heavier than in my previous solution…we have to add a resource of type DefaultButtonHub, and point the button and text boxes at it using the {StaticResource} markup extension.

<UserControl x:Class="DefaultButton.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:my="clr-namespace:DefaultButton"
    Width="400" Height="300">
    <UserControl.Resources>
        <my:DefaultButtonHub x:Key="defaultHub"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBox x:Name="theText" Grid.Row="0"
                 my:DefaultButtonHub.DefaultHub="{StaticResource defaultHub}"/>
        <Button x:Name="theButton" Grid.Row="1" Content="Default"
                Click="theButton_Click" my:DefaultButtonHub.DefaultHub="{StaticResource defaultHub}"/>
    </Grid>
</UserControl>

Note that the new DefaultHub attached property is applied to both the text box and the button, each pointing the the single resource.  This way everything gets wired up property, there isn’t any problem with name resolution (aside from the usual resource name scoping) and everything will get cleaned up if the form needs to be GC’d.

Thursday, 11 June 2009 12:03:22 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 05 June 2009

Last time, I talked about how to build a Domain Service from scratch using whatever POCO you have lying around.  Now it’s time to talk about how that works on the client side…

One of the coolest things about RIA Services is that you don’t even have to “Add Service Reference…” to get a reference to the Domain Service.  If your Silverlight project is linked to the ASP.NET project correctly (see the RIA Services doc for how this works) the build steps will take care of generating the right code in your Silverlight project, and away you go.  There are several ways of accessing the service client-side, from dead-easy to a bit more involved.  We’ll start with dead easy. 

The very easiest way to get things hooked up is to use the DomainDataSource control.  It wraps your DomainDataContext (the client-side generated bit) with a data source you can bind directly against in XAML.

<ria:DomainDataSource x:Name="PeopleDataSource" LoadMethodName="LoadPersons" AutoLoad="True">
  <ria:DomainDataSource.DomainContext>
      <services:PeopleDomainContext/>
  </ria:DomainDataSource.DomainContext>
</ria:DomainDataSource>
<dataControls:DataForm x:Name="dfPeople" CanUserAddItems="True" CanUserDeleteItems="true"
                     ItemsSource="{Binding ElementName=PeopleDataSource, Path=Data}" 
                     ItemEditEnded="dfPeople_ItemEditEnded">
</dataControls:DataForm>

The LoadMethodName attribute on the DomainDataSource tells it what method on the DomainContext to call in order to correctly populate the data context.  You can also pass parameters defined in XAML to the Load method if you only need to load a subset.  The DataForm control is bound to the Data property of the DomainDataSource, and away you go.  You get this

image

Because the Insert/Update/Delete methods are implemented on the server-side DomainService, the DataForm automagically enables the edit, add and delete buttons at the top.  If I edit or add a record, the save button shows up…

image

Pressing either the Save or Cancel button fires the ItemEditEnded event, which we can grab to submit the changes back to the server

private void dfPeople_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
{
  if (e.EditAction == DataFormEditAction.Commit)
      PeopleDataSource.SubmitChanges();
}

this is the very simplest case.  Calling SubmitChanges() here will send the edits/inserts up to the server right away.  For the sake of bandwidth, etc. I might want to implement my own “Save” button that batches up a whole set of change to the server rather than committing each edit individually.  You would still call PeopleDataSource.SubmitChanges, but not in response to the DataForm’s events. 

One of the great things about the way this works on the client side is that way the data validation attributes we set on the server-side POCO objects get propagated to the client.  For example, the server side LastName property looks like this (at least in the metadata class…)

[Required]
[RegularExpression("[a-zA-z]*")]
public string LastName;

The property that gets generated on the client side is

[DataMember()]
[RegularExpression("[a-zA-z]*")]
[Required()]
public string LastName
{
  get
  {
      return this._lastName;
  }
  set
  {
      if ((this._lastName != value))
      {
          this.ValidateProperty("LastName", value);
          this.RaiseDataMemberChanging("LastName");
          this._lastName = value;
          this.RaiseDataMemberChanged("LastName");
      }
  }
}

It maintains the [Required] and [RegularExpression] attributes.  Plus, in the property setter, it calls ValidateProperty, which uses reflection to examine those attributes and throw validation exceptions if necessary.  By default, then, I get UI on the Silverlight client for validation.

image

The DataForm provides the UI around both the field in question and the summary at the bottom.  In this case, I probably don’t want to tell the user what RegEx I’m validating against, so I need to add the ErrorMessage property to the validation attribute

[Required]
[RegularExpression("[a-zA-z]*", ErrorMessage="Alpha characters only please!")]
public string LastName;

image

When that error is corrected and I press the Save button, the Custom Validation routine is applied (see last post for details)

image 

When all the errors are corrected, and the save happens (and gets committed back to the server) the DataForm is updated to show the correct ID value (from the server) as well as the calculated Age property.

image

For next to no work, I get a pretty good user experience for browsing, editing, inserting and deleting records.  If I wanted a bit more control, I could load the data into the DataContext myself, and then setup the databinding, rather than using the DomainDataSource in XAML

private PeopleDomainContext _context = new PeopleDomainContext();

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
  if (_context.Persons.Count == 0)
  {
      _context.Loaded += (s, arg) => loadPeople();
      _context.LoadPersons();
  }
  else
      loadPeople();
}

private void loadPeople()
{
  dfPeople.ItemsSource = _context.Persons;
}

This would give me an opportunity to only load some people based on parameters, or whatever else I wanted to do to affect the loading of data before the data binding happens. 

Next time… more cool things you can do with the DomainDataContext

Friday, 05 June 2009 14:47:59 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, 02 June 2009

Most of the demos/samples I’ve looked at so far for RIA Services have started with a LINQ to SQL or ADO.NET Entity model and generated Domain Service classes from those.  I decided to start from something super simple (a POCO, if you will) and work up from there.  I started with a canonical  “Person” class

public partial class Person : INotifyPropertyChanged
{
   #region INotifyPropertyChanged Members

   public event PropertyChangedEventHandler PropertyChanged;

   #endregion

   protected virtual void Changed(string propertyName)
   {
       PropertyChangedEventHandler handler = PropertyChanged;
       if (handler != null)
       {
           handler(this, new PropertyChangedEventArgs(propertyName));
       }
   }

   private int _personId;
   public int PersonId
   {
       get
       {
           return _personId;
       }
       set
       {
           if (_personId != value)
           {
               _personId = value;
               Changed("PersonId");
           }
       }
   }

   private string _firstName;
   public string FirstName
   {
...
   }
   private string _lastName;
   public string LastName
   {
...
   }
   private System.DateTime _birthDate;
   public System.DateTime BirthDate
   {
...
   }
}

Nothing to see here.  It’s just a simple POCO that supports INotifyPropertyChanged for databinding.  Note that it’s a partial class…  The simplest path would be to add RIA Services attributes directly to these properties for things like data validation, but I wanted to try out all the features, so I split out the metadata into another file

[MetadataTypeAttribute(typeof(RiaServices.Web.Person.PersonMetadata))]
public partial class Person
{

   internal sealed class PersonMetadata
   {
       [Key]
       [Required]
       public int PersonId;

       [Required]
       [RegularExpression("[a-zA-z]*")]
       public string FirstName;

       [Required]
       [RegularExpression("[a-zA-z]*")]
       public string LastName;

       [Required]
       public DateTime BirthDate;
   }
}

This is kind of an interesting trick, and allows me to separate out all the RIA specific metadata from the original class definition.  This makes total sense when you look at the way they handle LINQ to SQL, for example.  In that case you already have Entity classes defined by the LINQ to SQL wizard, so this extra metadata class allows you to associate the right attributes without touching the (generated) LINQ to SQL classes.  Clever.  Notice that the metadata class uses fields, not properties, and just matches the names for the sake of simplicity.

In this case, the additional metadata defines data validation rules that get enforced both server and client side.  There are other attributes to enforcing string length and ranges. 

This all works because RIA Services generates “proxy” entity classes on the client side that are Silverlight compilable and also DataContracts (for serializing, which is cool…).  However, what happens if I have a calculated property that’s not just storage?  There’s a solution for that too, and it involves another piece of the partial class

public partial class Person
{
   [Shared]
   public int Age
   {
       get
       {
           this.Changed("Age");
           return Convert.ToInt32(Math.Floor((DateTime.Now - _birthDate).Days / 365.25));
       }
   }

}

This bit goes in a file called Person.shared.cs, and it will be copied to the client project and compiled there as well as on the server.  The [Shared] attribute marks the bits that need to be thus propagated.  Again, clever.  Of course, any such shared code has to compile in Silverlight.

The other piece of code I want to share (using the same method) is a custom validator.  In addition to the [Required] or [RegularExpression] attributes used above, you can register a custom validation routine that can examine the state of the entity as a whole.  The validation routine looks like this

[Shared]
public class PersonValidator
{
   public static bool IsPersonValid(Person p, ValidationContext context, out ValidationResult result)
   {
       bool valid = true;

       result = null;

       if (p.Age > 130)
           valid = false;

       if (!valid)
       {
           result = new ValidationResult("Birthdate is invalid, people can't be that old", new []{"BirthDate"});
       }

       return valid;

   }
}

That’s in a file called PersonValidator.shared.cs, so that it will be available client and server-side.  It’s associated with the Person entity with an additional attribute

[CustomValidation(typeof(PersonValidator), "IsPersonValid")]
[MetadataTypeAttribute(typeof(RiaServices.Web.Person.PersonMetadata))]
public partial class Person
...

With the Person entity all ready, I can expose it to the client by creating a new DomainService class with methods for Get, Insert, Update, Delete, etc.

[EnableClientAccess()]
public class PeopleDomainService : DomainService
{
   public IQueryable<Person> GetPersons()
   {

       return PeopleData.Persons.AsQueryable<Person>();
   }

   public IQueryable<Person> GetPerson(int personId)
   {
       return (from p in PeopleData.Persons
               where p.PersonId == personId
               select p).AsQueryable<Person>();
   }

   public void UpdatePerson(Person person)
   {
       Person oldP = (from p in PeopleData.Persons
                      where p.PersonId == person.PersonId
                      select p).FirstOrDefault();

       if (oldP != null)
       {
           PeopleData.Persons.Remove(oldP);
           PeopleData.Persons.Add(person);
       }
   }

   public void InsertPerson(Person person)
   {
       if (person.PersonId == 0)
       {
           int max = PeopleData.Persons.Max(p => p.PersonId);
           person.PersonId = max + 1;
       }

       PeopleData.Persons.Add(person);
   }

   public void DeletePerson(Person person)
   {
       Person oldP = (from p in PeopleData.Persons
                      where p.PersonId == person.PersonId
                      select p).FirstOrDefault();

       if (oldP != null)
       {
           PeopleData.Persons.Remove(oldP);
       }
   }
}

PeopleData.Persons in this case is a List<Person> that’s populated with some sample data.  The [EnableClientAccess] attribute causes the build-time bits to generate a client side proxy for calling the service without the client project needing a service reference.  It really makes the Silverlight and the Web projects feel like parts of the same app rather than disconnected pieces. 

The corresponding class that is generated on the client side is a DomainDataContext, which feels much like a LINQ to SQL DataContext only it’s lazy-loaded like the Astoria ones.  The GetPersons method on the server results in LoadPersons on the client, etc.  If I hadn’t implemented the Insert/Update/Delete methods on the server side, the DomainDataContext would simple behave like a read only data source.  This model works really well with the DataForm class.  If I set the ItemsSource of the DataForm to the Persons “table” in the client side data context, it will properly enable/disable the add/delete buttons depending on the capabilities of the data context.  Neat. 

Coming in future posts… hooking up the PersonDataContext to the Silverlight 3 UI, and in ASP.NET

Tuesday, 02 June 2009 15:53:18 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |