What Was I Thinking?

Follies & Foils of .NET Development
posts - 95 , comments - 352 , trackbacks - 0

Silverlight (3) ComboBox binding made easy.

Manish Dalal wrote an excellent post on how to effectively data bind a silverlight combobox for foreign key scenarios.  Using his approach, you create a ListProvider class that receives a DomainContext, internally loads the data into the context, and exposes the loaded data through an easily bound property.

Having written a few of these, I’ve refactored the list provider functionality to a generic base class.  The heavy lifting is done via reflection and it should be noted that my code assumes you take the default naming convention of the entities and entity collections in the DomainContext class.

 

Defining the class

Here’s my base class:

public abstract class ListProviderBase<T, DC> : INotifyPropertyChanged where T : Entity, new()where DC : DomainContext
  {
      protected DC context;
      readonly Type itemType = typeof(T);
      public event Action<LoadOperation<T>> PickListLoadingComplete;
      public DC DomainContext
      {
          set
          {
              context = value;
       
              // Get the query object for the specified item Type
              MethodInfo queryMI = context.GetType().GetMethod("Get" + itemType.Name + "Query");
              EntityQuery<T> query = (EntityQuery<T>) queryMI.Invoke(context, null);

              // Load the data into the context, upon completion, call back to PickListDataLoad_Complete
              context.Load<T>(query, PickListDataLoad_Complete, itemType);
              onPropertyChanged("DomainContext");
          }
          get
          {
              return context;
          }
      }

      /// <summary>
      /// call back that fires when the data list loading is complete
      /// </summary>
      /// <param name="obj"></param>
      private void PickListDataLoad_Complete(LoadOperation<T> obj)
      {

          string msg = String.Format(((Type)obj.UserState).Name + " Pick List loaded with {0} Entries",
                                     obj.Entities.Count());
          Debug.WriteLine(msg);
          if (PickListLoadingComplete != null)
              PickListLoadingComplete(obj);
          onPropertyChanged("PickList");
      }

      /// <summary>
      /// The list of entities to appear in the list.  Bind to this element.
      /// </summary>
      public EntityList<T> PickList
      {
          get
          {
              // return the corresponding EntityList property.
              // Note:  This assumes the ItemList uses the default naming convention 
              //        in the domain context (ie the list of customer objects is accessed via the Customers property)
              return (EntityList<T>)readProperty(itemType.Name+"s", context);
          }
      }

      /// <summary>
      /// using reflection, reads the specified property from the specified object
      /// </summary>
      /// <param name="propertyName"></param>
      /// <param name="obj"></param>
      /// <returns></returns>
      private static object readProperty(string propertyName, object obj)
      {
          PropertyInfo propInfo = obj.GetType().GetProperty(propertyName);
          object propValue = propInfo.GetValue(obj, null);
          return propValue;
      }

      private void onPropertyChanged(string propertyName)
      {
          if (PropertyChanged != null)
              PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
      public event PropertyChangedEventHandler PropertyChanged;
  }

Within my application, I sub class the base class and specify my application’s DomainContext.

public abstract class NBOListProviderBase<T> :ListProviderBase<T,NBODomainContext> where T:Entity,new(){}

when I want to populate a combo with a list of related departments, its a single line of code to create the ListProvider;

public class DepartmentListProvider : NBOListProviderBase<Department> { }

Defining the Control

Add the definition as static resource in my XAML
   <Grid.Resources>
       <app:DepartmentListProvider x:Name="departmentListProvider" />
   </Grid.Resources>
 and define the combo box as follows:
                            <ComboBox  ItemsSource="{Binding PickList, Source={StaticResource departmentTypeListProvider}}"
                                       SelectedItem="{Binding Department, Mode=TwoWay}"
                                       DisplayMemberPath="DepartmentName"  />

This binds the control’s source to the PickList property of the instance of the DepartmentTypeListProvider object, instructs the combobox to show the DepartmentName values as the list content, and store the selected item in the Department property of the data context.

Loading the Data

In the Loaded event of the form hosting the combobox, be sure to set the DomainContext property of the list provider.  This will populate the data into the combobox and expose it via the PickList property.  I’ve written a helper method (again using some reflection) to make this easier.

 

public static class ViewHelper
   {
       public static void SetListProviderContext(FrameworkElement ResourceContainer, string ListProviderResourceName, DomainContext Context)
       {
           var listProvider = ResourceContainer.Resources[ListProviderResourceName];
           var propInfo = listProvider.GetType().GetProperty("DomainContext");
           propInfo.SetValue(listProvider, Context, null);
       }
   }

 To Load our combobox data, place the following code in the loaded event of the form that hosts the combobox:

 

ViewHelper.SetListProviderContext(LayoutRoot, "departmentListProvider", this.dds.DomainContext);

When the form renders, the combobox list will be loaded.  See Manish’s comments on overriding the equality operator to ensure the correct value is automatically selected editing existing data.

Print | posted on Thursday, October 1, 2009 6:52 PM | Filed Under [ Silverlight ]

Feedback

Gravatar

# re: Silverlight (3) ComboBox binding made easy.

This looks like an awesome idea and I would like to get it to work. However, I am coding in vb and having some difficulty.
Could you help with the conversion. I have tried converting using Developer Fusion converter. I am having difficulty with the code for :in C# "context.Load<T>(query, PickListDataLoad_Complete, itemType);"
in vb "context.Load(Of T)(query, PickListDataLoad_Complete, itemType)" The error i am getting in VB is "argument not specified for parameter obj.."

Also in vb "Implements INotifyPropertyChanged " the error is "ListProviderBase must implement EventPropertyChanged"

Thank you in advance ,
Rennie
12/8/2009 3:32 PM | Rennie Cockram
Gravatar

# re: Silverlight (3) ComboBox binding made easy.

Could you please confirm exactly how I implement:
Within my application, I sub class the base class and specify my application’s DomainContext.

public abstract class NBOListProviderBase<T> :ListProviderBase<T,NBODomainContext> where T:Entity,new(){}
when I want to populate a combo with a list of related departments, its a single line of code to create the ListProvider;

public class DepartmentListProvider : NBOListProviderBase<Department> { }

Thanks
3/5/2010 6:16 AM | Steven
Gravatar

# re: Silverlight (3) ComboBox binding made easy.

Like Steven, I'm also interested in how to implement this base class...
5/5/2010 4:36 PM | Drammy
Gravatar

# re: Silverlight (3) ComboBox binding made easy.

Scratch that - worked it out.

I just created 2 new classes; NBOListProviderBase & (in the above example) DepartmentListProvider. I then copied the relevant code as specified in the blog.

Good work!
5/5/2010 4:41 PM | Drammy
Gravatar

# re: Silverlight (3) ComboBox binding made easy.

I would like to add ordering to the PickList. Any ideas?
5/5/2010 5:13 PM | Drammy
Gravatar

# re: Silverlight (3) ComboBox binding made easy.

thank you. binding sample.

Spam-dev
6/8/2010 2:27 AM | spam-dev
Gravatar

# re: Silverlight (3) ComboBox binding made easy.

now i already understand. thanks!
9/14/2010 2:24 AM | water heater installation
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: