# 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]  | 
# Thursday, 28 May 2009

Shaun and I will be doing a one-day training, “Practical Silverlight 3”, in lovely San Francisco, CA on July 8th.  It’s a full day of Silverlighty goodness, starting with the basics and movin’ on up.  We like to think of it as a 300 level technical introduction to the subject, so you will come away with a better understanding of the fundamentals, and where to look next.

Topics include

  • An introduction to Silverlight 3
  • All about controls (styling, templating, building your own)
  • Integrating with the browser
  • Communicating with the server via WCF, REST/POX, Sockets, you name it

If you are or will be in NorCal, come check it out.  It’s a good introduction for a very reasonable price.

Thursday, 28 May 2009 16:38:16 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, 26 May 2009

SoftSource developer Prashant Sinha is going to be presenting on “Production Debugging for Silverlight and ASP.NET” at Code Camp.  Prashant is a debugging wiz, and teaches our two-day .NET debugging course, so this should be a very useful session. 

Tuesday, 26 May 2009 13:51:11 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 21 May 2009

In my book, I talked a bit about programming by contract and how that makes everyone’s lives easier.  Say I have a method that divides one integer by another

public double Divide(int dividend, int divisor)
{
  return dividend / divisor;
}

I’d like to be able to let callers know that they can’t pass a 0 for the divisor, because that will result in a DivideByZeroException, and nobody wants that.  In the past I had a couple of choices on how to express that, mostly involving writing different kids of code, because C# doesn’t have a native expression of design by contract like Eiffel does.  One way is to use Debug.Assert

public double Divide(int dividend, int divisor)
{
  Debug.Assert(divisor != 0);

  return dividend / divisor;
}

That way any caller that passes a 0 will get an assertion dialog at runtime, which brings it pretty dramatically to everyone’s attention.  The assumption is that using the Debug.Assert will flush out all cases where people are incorrectly calling my method during development, so it’s OK that the assertion will get compiled out of my Release build.  However, that doesn’t make it impossible for a caller to pass a 0 at runtime and cause the exception.  Another option is explicitly checking parameters and throwing a different exception.

public double Divide(int dividend, int divisor)
{
  if (divisor == 0)
      throw new ArgumentException("divisor cannot be zero", "divisor");

  return dividend / divisor;
}

Patrick argues that this has now moved from defining contract to defining behavior, and I can agree with that, although I’d probably argue that it defines both contract and behavior since I’ve extended the functionality of the Debug.Assert to the release build, while also protecting my internal state from bad data.  But that’s really a separate discussion… :)

Now thanks to the Microsoft Code Contracts project, I have a third option.  The Code Contracts project is the evolution of the work done on Spec#, but in a language neutral way.  The Code Contracts tools are currently available from DevLabs for VS 2008, as well as shipping with VS 2010 B 1.  Just at the moment, there are more features in the DevLabs version that what made it into the 2010 beta.  With Code Contracts, I can rewrite my Divide method like this

public double Divide(int dividend, int divisor)
{
  Contract.Requires(divisor != 0);

  return dividend / divisor;
}

I like the syntax, as it calls out quite explicitly that I’m talking about contract, and making an explicit requirement.  The default behavior of this method at runtime is identical to Debug.Assert, it brings up an assertion dialog and brings everything to a screeching halt. However, it’s configurable at build time, so I can have it throw exceptions instead, or do whatever might be appropriate for my environment if the contract is violated.  I can even get the best of both worlds, with a generic version of Requires that specifies an exception

public double Divide(int dividend, int divisor)
{
  Contract.Requires<ArgumentException>(divisor != 0, "divisor");

  return dividend / divisor;
}

I could configure this to bring up the assertion dialog in Debug builds, but throw ArgumentNullException in Release builds.  Good stuff.

The example above demonstrates a required “precondition”.  With Code Contracts, I can also specify “postconditions”. 

public void Transfer(Account from, Account to, decimal amount)
{
  Contract.Requires(from != null);
  Contract.Requires(to != null);
  Contract.Requires(amount > 0);
  Contract.Ensures(from.Balance >= 0);

  if (from.Balance < 0 || from.Balance < amount)
      throw new InsufficientFundsException();

  from.Balance -= amount;
  to.Balance += amount;

}

This isn’t the greatest example, but basically the Transfer method is promising (with the Contract.Ensures method) that it won’t ever leave the Balance a negative number.  Again, this is arguably behavior rather than contract, but you get the point. 

A really nifty feature is that I can write an interface definition and associate it with a set of contract calls, so that anyone who implements the interface will automatically “inherit” the contract validation.  The syntax is a bit weird, but you can see why it would need to be like this…

[ContractClass(typeof(ContractForCalucluator))]
interface ICalculator
{
   int Add(int op1, int op2);
   double Divide(int dividend, int divisor);
}

[ContractClassFor(typeof(ICalculator))]
class ContractForCalucluator : ICalculator
{
   #region ICalculator Members

   public int Add(int op1, int op2)
   {
       return default(int);
   }

   public double Divide(int dividend, int divisor)
   {
       Contract.Requires(divisor != 0);

       return default(double);
   }

   #endregion
}

Now any class that implements ICalculator will have the contract validated for the Divide method.  Cool.  The last thing I want to point out is that the team included a handy sample of how to work with contract validation in your MSTest unit test code.  The Contract class exposes an event called ContractFailed, and I can subscribe to the event to decide what happens on a failure.  For a test assembly, I can do this

[AssemblyInitialize]
public static void AssemblyInitialize(TestContext tc)
{
  Contract.ContractFailed += (sender, e) =>
  {
      e.SetHandled();
      e.SetUnwind();
      Assert.Fail(e.FailureKind.ToString() + " : " + e.Message);
  };
}

which will translate contract failures into test failures with very explicit error messages.  In the case of my Divide method if I run this test

[TestMethod()]
public void DivideContractViolation()
{
  Calculator target = new Calculator(); 
  int dividend = 12; 
  int divisor = 0; 
  double actual;
  actual = target.Divide(dividend, divisor);
  Assert.Fail("Should have failed");
}

I get a test failure of

Test method CalculatorTests.CalculatorTest.DivideContractViolation threw exception:  System.Diagnostics.Contracts.ContractException: Precondition failed: divisor != 0 divisor --->  Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.Fail failed. Precondition : Precondition failed: divisor != 0 divisor.

Cool.  This is definitely something I’ll be looking at more in the future.

Thursday, 21 May 2009 14:50:38 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 20 May 2009

Not this weekend but next (5/30) is PDX Code Camp 2009.  A whole day of free software learning and general nerdy hanging out.  I’ll be talking about building business apps in Silverlight 3, and my SoftSource compadre Tim Johnson is going to be talking about making your web application more accessible for people with disabilities.  There are also a host of other sessions including some big names like Ward Cunningham, so it’s an event not to be missed, and cheap at twice the price!

Wednesday, 20 May 2009 13:45:43 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 

This came up in class yesterday, so I did a little digging.  Everyone may already know this, but it came as news to me. :)

If I’ve got a collection in memory, as well as a LINQ to SQL DataContext

List<FavoriteFood> foods = new List<FavoriteFood>(){
	new FavoriteFood{CustomerID = "ALFKI", Favorite="Chips"},
	new FavoriteFood{CustomerID = "ANATR", Favorite = "Fish"}};

NorthwindDataContext context = new NorthwindDataContext();

and I want to do an INNER JOIN between the foods list and the Customer table in Northwind, it would seem like this should do it

var bad = (from cust in context.Customers
         join f in foods on cust.CustomerID equals f.CustomerID
         select new
         {
             cust.ContactName,
             f.Favorite
         }).ToList();

but sadly, no.

image

if you stop to think about it, it totally makes sense that it wouldn’t work like that, since there’s no way to translate that into SQL in any rational way to send to SQL server. 

OK, so the next step would be to first get the customers in memory, then do the join

//this executes the whole query, thus retrieving the entire customer table
var customers = (from c in context.Customers
             select c).ToList();

//an inner join between the customers from SQL and the in-memory list
var inner = from cust in customers
     join f in foods on cust.CustomerID equals f.CustomerID
     select new
     {
         cust.ContactName,
         f.Favorite
     };

That works, but I’ve had to pull the entire Customer table into memory just to join two rows.  If I wanted to do a LEFT OUTER JOIN, I’d need that anyway, like so

//here's the left outer join between the customer list from SQL 
//and the in-memory favorites
var leftouter = from cust in customers
             join f in foods on cust.CustomerID equals f.CustomerID into custFoods
             from custFood in custFoods.DefaultIfEmpty()
             select new
             {
                 cust.ContactName,
                 Favorite = custFood == null ? null : custFood.Favorite
             };

but I want an inner join without pulling down all the customers, so I need to only fetch those rows that will join from Customer

//this is how you manage the IN clause
var x = from c1 in context.Customers
     where (from cf in foods
           select cf.CustomerID).Contains(c1.CustomerID)
     select c1;

//Note that to join the result in x back to the foods collection you would have to 
//execute the query just like with customers above...
var littleInnerJoin = from filteredCustomer in x.ToList()
                   join f in foods on filteredCustomer.CustomerID equals f.CustomerID
                   select new
                       {
                           filteredCustomer.ContactName,
                           f.Favorite
                       };

It’s two steps, but now I’ve just loaded the rows that will join into memory, and then done the join with LINQ to Objects.  It bears a little thinking about, but doesn’t seem like too much overhead, IMHO.

Work | LINQ
Wednesday, 20 May 2009 13:38:43 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 07 May 2009

Let’s say I’ve got to the effort of creating a modal dialog or it’s equivalent in a Silverlight application.  What I would like to complete the picture is a “default” button, so that if I hit the Enter key while in a text box, the dialog will be “submitted”.  There are probably several ways of achieving this end, but I wanted something that was simple, and encapsulated in an attached property so the consumer wouldn’t have to deal with much code.  I wrote the attached property so that I could use it on text boxes like this

<TextBox x:Name="theText" VerticalAlignment="Center" Margin="10,0,10,0" 
	Grid.Row="0" my:DefaultButtonService.DefaultButton="theButton"/>

where “theButton” here is the default button I want to be pressed when the Enter key happens inside my text box.  The downside is that I have to apply this property to every text box on the page, but so be it, that seems like a relatively small price to pay.  So I got as far as finding the named button in the visual tree, but the the question was, how to “press” the button.  The Button class in Silverlight has a protected OnClick that would do the trick, if it wasn’t protected.  I could derive my own control from Button and expose the OnClick method, but ewww.  If I did that then every dialog that wanted this behavior would have to remember to use the derived Button class.  I tried reflecting over the Button and Invoking OnClick anyway, but turns out you get a security exception.  OK.  Then, thanks to a presentation Stuart gave us yesterday on Accessibility, I remembered the Automation framework in Silverlight.  That turned out to be super easy, just create an instance of ButtonAutmationPeer from the Button, then Invoke it.  Cool.

public static class DefaultButtonService
{
   public static readonly DependencyProperty DefaultButtonProperty =
       DependencyProperty.RegisterAttached("DefaultButton", typeof(string), typeof(DefaultButtonService), new PropertyMetadata(OnDefaultButtonChanged));


   public static string GetDefaultButton(DependencyObject d)
   {
       return (string)d.GetValue(DefaultButtonProperty);
   }

   /// <summary>
   /// Sets the CommandParameter property.
   /// </summary>
   public static void SetDefaultButton(DependencyObject d, string value)
   {
       d.SetValue(DefaultButtonProperty, value);
   }

   private static void OnDefaultButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
       TextBox tb = d as TextBox;
       if (tb != null)
       {
           tb.KeyUp += new KeyEventHandler(tb_KeyUp);
       }
   }

   static void tb_KeyUp(object sender, KeyEventArgs e)
   {
       switch (e.Key)
       {
           case Key.Enter:
               string name = (string)((DependencyObject)sender).GetValue(DefaultButtonProperty);
               object root = App.Current.RootVisual;
               object button = ((FrameworkElement)root).FindName(name);
               if (button is Button)
               {
                   ButtonAutomationPeer peer = new ButtonAutomationPeer((Button)button);

                   IInvokeProvider ip = (IInvokeProvider)peer;
                   ip.Invoke();
               }
               break;
       }
   }
}
Thursday, 07 May 2009 13:27:54 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, 07 April 2009

We just added a set of new one day classes starting in May, including “Agile in day”, Silverlight 3, and UI design patterns for WPF and Silverlight.  These are lecture-style classes that will give you an in depth look at each subject.  Like our previous Silverlight event, these are 300-400 level, technical sessions that will give you a leg up on each subject in a day.  Because they are presented lecture style, we can offer them for only $125 each, so feel free to collect the whole set. :-)  Details and registration here.  We may be adding some additional topics soon, so check back often.

Tuesday, 07 April 2009 10:41:19 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |