Search code examples
c#silverlightsilverlight-4.0wcf-ria-services

Silverlight Concurrent Data Sources for a Grid's Control


I have the following data model:

Camp -> CampEvent <- Event.

Camp has CampId and Name associated with it. Event has EventId, Name, Start/End (Dates). CampEvent has (CampId,EventId)PK, CampId FK, EventId FK.

The tables are used to create a Domain Model and a Domain Service which is consumed from the client side on Silverlight.

I am successfully able to display the Event's table in Silverlight using a grid.

The Grid has two template columns - one to display a checkbox, and another to display the name of the event.

So now the problem is somehow I need to check the checkboxes when this control goes in edit mode.

I've noticed that the Grid doesn't have OnDataBound event, or it doesn't have a way of setting the state of each checkbox to checked other than through binding.


Solution

  • Well, apparently in Silverlight you don't have the luxury of messing around with the contents of a GridViewRow. You can, however, achieve this by changing the underlying data source.

    In the above scenario we have a control which is used for creating an instance of Camp and associating it with one or many events. In a sense the control can either Create or Update a "Camp" object and its relationships with Events. The control state is controlled by an Enumeration public enum Mode { Create, Update }; and depending on which value this property has, the control will do either one, or both of the following binding binding operations:

    1. Get all event data and display it in a grid composed of rows having a checkbox & label.
    2. Check the boxes denoting the events in which a specific camp participates.

    This was all nice and dandy in theory but in principle I realized Silverlight needs a discrete data source to bind to. I created a collection of a CampEvent custom object in which every element has a boolean prperty IsChecked, along with the event name and event Id. The CampEvent object is not a Domain Entity object and is only used for binding.

    So to achieve my goal, these are the steps I took.

    1. Declare ObservableCollection where T is used for only binding. In this case the underlying data source for T is our Event, and a Linq to Entity query was used to grab the Id and the Name of the Event and transforms it into a CampEvent object with its IsChecked property set to false by default.

    2. If the Control is in Create mode I am done. The checkboxes in the Grid's template column are two-way bound to the IsChecked property of the underlying data source. Step one is enough to create the default UI with all check boxes unchecked. Otherwise go to 3

    3. Well, number 2 was wrong, so therefore the control was in "Update" mode. If the SelectedCamp property of the control is set (and this property is of type Camp). At this point we create a Linq to Entitities query where we ask the domain service to Include the Events associated with the specified camp.

    4. Once the response from the query arrives, we iterate through every Event object that is associated with the camp. For every event that was received, we check to see if it exists in our ObservableCollection data source. If it does, we set the IsChecked property to true for that item. Once we data bind the grid, all events that are associated with a specific camp will be "Checked".

    5. Mission accomplished.

    Few words on Database Structure, Domain Models generated by the Entity Framework, and WCF RIA.

    Well, as it turns out EF will get you maybe 80% there out of the box. The tool is not intelligent enough to know what a many-to-many relationship is. In the case with camp and events we have the following structure:

    camp -> participates in many -> events
    (many) events -> have many -> camps (as participants)
    

    So to make this happen we need a "joiner" table between camps and events. To do this properly, the joiner table should in theory have at the very least two columns:

    CampId -> Foreign Key EventId -> Foreign Key

    Now to create a primary key for the Table, we should have:

    CampId + EventId -> Composite Primary Key.

    Making our table have only 2 fields. Now this is super important because this relationship creates the Navigation property possible in EF.

    When the domain model is generated, the EF will not create the joiner table in the model. However to enable the navigation property between Camp and Event and vise versa there are a couple of things that have to happen on the underlying Domain Service meta data object.

    **1. Locate the Camp metadata info. Decorate the IEnumerble<Event>Events property with:

    [Include]
    [Association("CampEvent", "CampId", "EventId", IsForeignKey=True)]
    

    And to explain what those mean: The Include says whenever you query the domain model, please include every Event for the specified camp(s). The Association says there is an association table between camp and event for the navigation property to work. In the lookup table the camp has CampId identifier and Event has EventId. Use those to find all Events for the specified camp(s)**.

    2. Do the same for whatever other navigational properties you have.