Introducing An MVVM-Friendly DomainDataSource: The DomainCollectionView


[This sample references out-of-date API. This newer post discusses the updates made for the V1 SP2 Preview and shows how to implement filtering.]

There’ve been plenty of talk about an MVVM-friendly DomainDataSource. I’m pretty sure it means completely different things to different people. I even took a stab at defining it a while back. What we’ve included in the Microsoft.Windows.Data.DomainServices assembly in the Toolkit closely follows that outline.

Since this is the first look at this component, I feel compelled to tell you what we’ve provided is more like an engine than a car. There’s a lot of power and flexibility in the DomainCollectionView design, but there are still a lot of ways to misuse it. We haven’t spent nearly as much time nailing down the use cases as we did on the DomainDataSource (for good or for bad).

Without further introduction, let’s dig in.

What is the DomainCollectionView?

The DomainCollectionView (herein referred to as the DCV) is a collection view implementation. The generic version of the class implements all the following interfaces. Most controls are designed to recognize and work against these interfaces.

  • ICollectionView
  • IEditableCollectionView
  • IPagedCollectionView
  • IEnumerable
  • IEnumerable<T>
  • INotifyPropertyChanged
  • INotifyCollectionChanged

For fear of pushing you to the brink of insanity, I’ll forego listing the full API. Instead, I’ll just say there are a lot of properties and methods for you to get acquainted with. Here’s a list of the ones you’ll commonly find yourself using.

  • CurrentItem, CurrentPosition, and MoveCurrentToXx: This is treated as the selected item in most list controls
  • GroupDescriptions: This collection defines the way items are grouped and also affects server-side sorting
  • PageIndex, MoveToXxPage: Calling MoveToPage will prompt the view to load the page at the specified index
  • PageSize: This defines the number of items to load on each page
  • Refresh, DeferRefresh: Calling Refresh will prompt the view to load using the current view settings
  • SortDescriptions: This collection defines the way items are sorted and affects server-side sorting
  • SourceCollection: This is the collection that backs the view and only items in the collection will appear in the view
  • TotalItemCount, SetTotalItemCount: This is used to calculate the total number of pages

How Do I Use a DomainCollectionView?

The DCV is designed using the following triad.

image

In the design, the View component is the DCV. It’s primary functions are to funnel interface calls to the Loader and to respond to changes in the Loader and Source components.

The Loader component is the CollectionViewLoader. It is responsible for handling an asynchronous Load call and raising the LoadCompleted event upon completion. The DomainCollectionViewLoader is the default implementation and can be created with two callbacks allowing you to handle loading and load completion.

The Source component can be any collection that implements IEnumerable. However, observable collections tend to provide the best results. When the Loader gets data, it should update the source and the source should notify the view. EntityList works well as the source, especially in paging scenarios.

image

Your responsibilities fall mostly along the bottom of the triangle. When the view is refreshed (by a you or a control), it will ask you to load the appropriate data. This usually breaks down into the following steps.

  1. Create an EntityQuery and apply the view state to the query. There are a number of EntityQuery extension methods, including Sort and Page, to make this easy on you.
  2. Load the query.
  3. In the completion callback, update the source collection with the new data. You may need to update the view as well if you queried for the TotalEntityCount (used in paging).

Let’s See Some Code

At this point, it will probably be easiest to see the pieces working together in code. This sample shows a read-only DataGrid where sorting and paging are both being done on the server. Here’s the DomainService I’ll be working with. The only thing to note here is I had to override Count to support paging since I’m deriving from the base DomainService.

  [EnableClientAccess()]
  public class SampleDomainService : DomainService
  {
    // Overridden to support paging over POCO entities.
    // If you're extending a derived DomainService
    // implementation this is already done for you.
    protected override int Count<T>(IQueryable<T> query)
    {
      return query.Count();
    }

    public IQueryable<SampleEntity> GetAllEntities() {…}
  }

On the client, I’m using a stripped-down version of the MVVM pattern for simplicity. The view isn’t terribly interesting, but I’ll start there anyway. Both the DataGrid and the DataPager are bound to the view model.

  <UserControl ...>

    <UserControl.DataContext>
        <vm:SampleViewModel />
    </UserControl.DataContext>
    
    ...


<
Grid ...> <Border ...> <Grid> ... <sdk:DataGrid Name="dataGrid1" Grid.Row="0" ItemsSource="{Binding CollectionView}" IsEnabled="{Binding IsGridEnabled}" IsReadOnly="True"/> <sdk:DataPager Name="dataPager1" Grid.Row="1" Source="{Binding CollectionView}" IsEnabled="{Binding IsGridEnabled}"/> </Grid> </Border> </Grid> </UserControl>

image

Finally, we’ll get to the interesting piece, the view model. The two properties the view is bound to, CollectionView and IsGridEnabled, are the least interesting part so I’ll skip right past those to the constructor. This is where all the relationships get set up.

  public SampleViewModel()
  {
    this._source = new EntityList<SampleEntity>(
this._context.SampleEntities);
this._loader = new DomainCollectionViewLoader<SampleEntity>( this.LoadSampleEntities, this.OnLoadSampleEntitiesCompleted);
this._view = new DomainCollectionView<SampleEntity>(
this._loader,
this._source); // Swap out the loader for design-time scenarios if (DesignerProperties.IsInDesignTool) { DesignTimeLoader loader = new DesignTimeLoader(); this._view =
new DomainCollectionView<SampleEntity>(loader, loader.Source); } // Go back to the first page when the sorting changes INotifyCollectionChanged notifyingSortDescriptions = (INotifyCollectionChanged)this.CollectionView.SortDescriptions; notifyingSortDescriptions.CollectionChanged += (sender, e) => this._view.MoveToFirstPage(); using (this.CollectionView.DeferRefresh()) { this._view.PageSize = 10; this._view.MoveToFirstPage(); } }

You can start to see things come together in the first couple of lines. The source is initialized with its backing EntitySet, the loader is initialized with a couple of callbacks, and the view is initialized with references to the first two.

The second things to notice is the block that adds design-time data. I’ve created a custom loader that returns a few sample entities and, at design time, construct a view using it instead. Not to get too sidetracked, but this snippet shows how easy it is to create a design-time loader.

  private class DesignTimeLoader : CollectionViewLoader
  {
    private static readonly IEnumerable<SampleEntity> source = new[] {…}

    public IEnumerable<SampleEntity> Source
{
get { return DesignTimeLoader.source; }
} public override bool CanLoad { get { return true; } } public override void Load(object userState) { this.OnLoadCompleted(
new AsyncCompletedEventArgs(null, false, userState)); } }

Also, it’s worth noting there are plenty of other approaches to obtain sample data. I chose this one to highlight the simplicity of writing a custom loader and because it only required a few lines of code.

Finally, there are two more things to look at near the end of the constructor. First, I’ve added an event handler to the SortDescriptions collection. Now, every time the collection is updated with a new SortDescription, I’ll make sure the table moves back to the first page. This isn’t a necessary addition, but I liked the behavior a little bit better. Once everything is set up, I make a couple updates to the view in a DeferRefresh block. Any time you’re updating more than one view property and then reloading, it’s good practice to wrap it with a DeferRefresh. This tells the view to forego all recalculations until you’ve finished your update. Upon exiting the using block, the Refresh method will be invoked to update the view.

The next piece we’ll look at are the two callbacks I passed into the constructor of the DomainCollectionViewLoader. As far as RIA data loading goes, they’re both pretty standard.

  private LoadOperation<SampleEntity> LoadSampleEntities()
  {
    this.IsGridEnabled = false;

    return this._context.Load(
      this._context.GetAllEntitiesQuery().SortPageAndCount(this._view));
  }

  private void OnLoadSampleEntitiesCompleted(LoadOperation<SampleEntity> op)
  {
    this.IsGridEnabled = true;

    if (op.HasError)
    {
      // TODO: handle errors
      op.MarkErrorAsHandled();
    }
    else if (!op.IsCanceled)
    {
      this._source.Source = op.Entities;

      if (op.TotalEntityCount != -1)
      {
        this._view.SetTotalItemCount(op.TotalEntityCount);
      }
    }
  }

In the LoadSampleEntities callback, the most interesting part is the SortPageAndCount method. There are a number of EntityQuery extensions included in the CollectionViewExtensions class (Sort, Page, etc.) that understand how to apply the state of the view to the query. For instance, SortPageAndCount orders according to the sort and group descriptions, skip/takes using the page index and count, and requests the total number of items if that has not already been determined.

The OnLoadSampleEntitiesCompleted is also pretty simple. In the successful load block, the source is updated with the entities that were just loaded. Also, if the TotalEntityCount was requested, it is used to update the view’s TotalItemCount (which is used by the DataPager to determine the total number of pages). Enabling and disabling the grid is not a required step, but I liked how it looked.

Those are the significant bits of the sample. With a little setup and a couple callbacks, the DCV makes server-side paging simple and obtainable. If you’re interested in seeing it run, here’s a link to the source.

Cool, I Want More

The DomainCollectionView in Microsoft.Windows.Data.DomainServices in the Toolkit supports the view functions of sorting, grouping, and paging. In addition, you can apply server-side filtering as part of your load callback (using a .Where clause). These four pieces will be the core of nearly every query you put together on the client.

Even with all that, there are a number of features that existed in the DomainDataSource that could be pulled into the DCV. I included an extensive list of features in this post. Let me know what’s missing and what you’d like added the most.

As I mentioned earlier, this stuff is powerful and flexible to allows you to do cool stuff, but doesn’t work terribly hard to prevent you from doing things wrong. I’ve tried to provide some guidance with this post and the sample to keep things running smooth, but I’m sure you’ll take this in directions I haven’t anticipated. If you run into bugs or have questions about the best way to do things, send them my way (Email Kyle).

Comments (68)

  1. MichaelD! says:

    This is great stuff.  Thank you for posting this Kyle!

  2. Manuel Felício says:

    Hi Kyle,

    It's nice to see WCF RIA Services client API converging to the API I made myself. Makes me feel that we're doing things the right way.

    I would like to hear your comments on the API I've posted here mfelicio.wordpress.com/…/architecting-silverlight-lob-applications-part-6-building-an-mvvm-framework

    .

    I haven't downloaded the new toolkit but I'm interested in knowing more of your DomainCollectionView. How does it support EntityCollections? Does the source need to be EntitySets?

    I've build a large project using my own API. Should we move to new API? We use ICollectionView's for EntitySets, EntityCollections, IEnumerables, and always based on a query that can be either executed on the server or on the client. Is this scenario supported with the current toolkit?

    Best regards,

    Manuel Felício.

  3. kylemc says:

    @Manuel

    I like the approach in your post. You've abstracted a lot more than I'd like to, but I haven't been able to settle on those details yet.

    The DomainCollectionView isn't tied to entities specifically and supports all types of collections.

    I'd stick with your own API for now. This stuff is still pretty new and will probably change a bit. You should look into marrying your IDomainQuery with the CollectionViewLoader. It might produce some good feedback about the extensibility points.

  4. janoveh says:

    Greate work!

    Could you please elaborate on a recommended INSERT and REMOVE strategy for a paging scenario like in your demo app?  I am interested as to where the Add and Remove operations should be done (on _view or _source or…) and a recommended approach as to when/where to call _context.SubmitChanges()…

  5. fel0nious monk says:

    does this replace CollectionViewSource, PagedCollectionView, etc?

  6. kylemc says:

    @janoveh

    You should be able to add/remove to either the _view or _source with the same results. Controls specifically designed to work with IEditableCollectionView should be bound to the former, but if you're just implementing a command, either is fine.

    Also, because the source is being overwritten with every load, you need to be prepared to keep track of the new and deleted items. For instance, you might want to carry a new item from page to page or leave it on the page where it was created. An alternate approach would be to set DomainCollectionViewLoader.IsBusy to true once an item had been added or removed to prevent the view from reloading.

    You can call SubmitChanges whenever you feel like it. This seems to generally be based on preference or application requirements.

  7. kylemc says:

    @felonious

    Not necessarily. You should use this view if it best fits your needs. This is the only implementation that allows you to shape your data on the server, and it's primary use is paging. For that reason, it should nearly always replace the PagedCollectionView since in-memory paging on the client is somewhat contrary to the intent of paging. I'm not sure how you've been using the CollectionViewSource, but I doubt you'd get much value out of replacing it with the DCV.

  8. sladapter says:

    Kyle,

    Thanks for posting the code.

    I've noticed when I navigate to different pages, there is always a service trip, even when the data is already loaded (say I move back from page2 to page1). The _context.SampleEntities contains all the loaded data. Why is a service trip needed when the data already in the _context?

    Another question is that do I really need to create a EntityList as the source of this DomainCollectionView? Can't I just use the _context.SampleEntities as the source of this CollectionView?

  9. kylemc says:

    @sladapter

    The power of the DCV is that is leaves everything in your hands. It's up to you to define the caching and loading semantics of paging. You could do this in your callbacks, but it might make sense to encapsulate it in a custom CollectionViewLoader implementation. The current sample shows an approach that does exactly what you've described; calls the service for each page.

    You're not necessarily tied to an EntityList, but you should understand that there're no implicit filter semantics in the DCV like you'd find in the PagedCollectionView. Everything you add to the _source collection will show up in the view. You could temper this via the Filter property, but that seems more complex than using an EntityList to start with.

    These are the kind of things that I like to hear as feedback. For instance, would it be worth the effort to create canned paging semantics (like the DDS) or are those things you'll want to control yourself?

  10. sladapter says:

    Thanks Kyle.  I'll play it more before I can give some meaningful feedback.

    My current thought is that there are too many CollectionViews and Entity Collections (EntitySet, EntityList, DataServiceCollection when I use WCF DataService).  That just make us very confused and I can't even remember all the names/namespaces, dlls of those collections or Views. To understand the difference between each other is even more challenging.  I hope you guys can consolidate some of them.

  11. fel0nious monk says:

    @kylemc

    Honestly, I'm not sure why i'm using the CVS .. other than demos and samples I've found for MVVM have used them. I know I'll need paging, and was going to switch to using the PCV here shortly. I'm with sladapter on this one: the intended use-cases for some of the views/collections are fuzzy, at best.

    I'm not sure I understand why the use-cases for CVS and DCV are mutually exclusive. Could you elaborate a bit on that?

  12. kylemc says:

    Did either of you see this post?

    blogs.msdn.com/…/collection-binding-options-in-wcf-ria-services-sp1.aspx

    I can go into more detail, but it covers the basic use cases.

  13. fel0nious monk says:

    i had not — and that explains it quite well.

    you deserve a gold star or something more substantial for your help, lol 😀

  14. mitkodi says:

    Hi Kyle,

    When I tried to use DomainCollectionView as a data source in a ViewModel I couldn't find property like HasChanges (neither in DomainCollectionView nor EntityList) to bind to it IsEnabled of my Submit and Rject Changes buttons. What would be your suggestion for that (of course I can use underlying EntitySet for this, but I prefere to use one data source for all bindings)?

    Thank you!

  15. mitkodi says:

    … and one more question Kyle. What should be expected behavior of DoaminCollectionViewLoader.IsBusy? Should we "manualy" set it or it could be automaticaly set by loader to true during a loading?

    Thanks!

  16. kylemc says:

    @mitkodi

    There isn't a property like that, though you could use EntityList.EntitySet.HasChanges.

    It's up to you to set the IsBusy flag. The stock loader doesn't take any inititive to control this, but you could write your own CollectionViewLoader that had a more prescriptive policy so you didn't have to do it manually.

    I anticipate with this release people will regularly need to tweak the CollectionViewLoader they use to get the specific behavior the require or to simplify scenarios common to their application.

  17. fel0nious monk says:

    Kyle,

    how do I apply server-side sorting? Currently my grid is only sorting on the current page. I've got a pretty simple setup, identical to yours above.

  18. Rasika says:

    Hi Kyle

    Thanks for such a wonderful sample, this is exactly I was looking for specially when the DomainService return POCO collection

    as in your code below

           public IQueryable<SampleEntity> GetEntities(byte count)

           {

               this.WaitALittle();

               List<SampleEntity> entities = new List<SampleEntity>();

               for (byte i = 1; i < count; i++)

               {

                   entities.Add(this.GetEntity(i));

               }

               return entities.AsQueryable();

           }

    Could you please explain how to handle the .Where clause just taking one field of SimpleEntity as an example.

  19. kylemc says:

    @Rasika

    I'm not positive which example you want. If you want to run a where clause within the operation, it would just look like this.

     entities.Where(e => e.Int32 = 3);

    If you want to filter it from the client, it is easier to pass it as a parameter.

     public SampleEntity GetEntity(int int32)

     {

       // …

       SampleEntity entity = entities.Where(e => e.Int32 = int32).FirstOrDefault();

       // …

     }

  20. Matt Weyland says:

    Thanks for the good example, I have a couple of questions I am hoping you can help with.

    Taking your example I wanted to be able to dynamically filter my data on the server side and return that set of data to be displayed in a data grid with a  pager control.  Both the ItemsSource and the Source properties (respectively) are bound to the ICollectionView object.

    In my VM I have created a new Loader object that calls the respective service method and filters as expected.  The data loads in the gird appropriately, however the datapager control still seems to think that all of the data are still being used.  I may see 12 pages of data but have that filtered down to only three records.  

    I'm creating a new domaincolleciontview object for both instances and am using the same loaded complete method you outlined in your example.  

    I can provide more information if you need.  Any help you can provide would be helpful.

    Thanks.

  21. kylemc says:

    @Matt

    I think this thread has the answer to your problem.

    forums.silverlight.net/…/212923.aspx

  22. Matt Weyland says:

    Thanks for the link, I tried that but am I am still getting the same results on the pager. It's still showing all of the pages, and when I navigate through the pages it only displays the first page, it never really re-loads the data set.

    I thought I'd take another approach on this, and new up a view each time I make a WCF service call to get data. This seems to be somewhat working but now on the property changed handler for the ICollectionView its throwing an {System.ArgumentOutOfRangeException: PageIndex must be greater than or equal to 0.}  

    Do you have a recommendation on how one might go about implementing a re-load of the data. I'm preforming my filtering on the server side with a specific method.

    I am following the approach outlined in your example, just that I'm tying it to a DB for a more real world learning example.  

  23. kylemc says:

    The Pager's going to show a modulo of the TotalItemCount (you can set this using view.SetTotalItemCount). Take a look at the value you're passing to that method. If the TotalEntityCount returned from the query seems incorrect, you can consider using an out parameter like I describe half way through this thread.

    forums.silverlight.net/…/212264.aspx

  24. nico42_81 says:

    Hi Kyle!

    Thanks for your exemple,

    I make a ViewModel the same way as in your example with a DomainCollectionView,

    I add some GroupDescriptions  from my user control and it work fine, the problem comes when I try to remove a GroupDescriptions, I get an ArgumentOutOfRange Exception from the LoadOperationCompleted event at this point:   this._source.Source = op.Entities;

    I also noticed that if I only have one GroupDescription in my DomainCollectionView and I tried to delete it I didn't get the exception.

    I can provide more informations if needed,

    Thanks,

  25. kylemc says:

    @nico

    Feel free to contact me using the 'Email Blog Author' link above and I may be able to help you debug the issue.

  26. mitkodi says:

    Hi Kyle,

    Is it possible the loader’s load callback to have parameters? For example I need to pass LoadBehavior to it and think that the easiest way to do it could be if the loader’s Load method can pass its userState parameter to the load callback (which is a parameterless function at the moment).

  27. kylemc says:

    @mitkodi

    I think you have it backward. The loader is invoked by the view when the view determines something should be refreshed. What you do in that callback is entirely up to you. For example, if you want to change the LoadBehavior, just upate the callback as follows.

     return this._context.Load(

       this._context.GetAllEntitiesQuery().SortPageAndCount(this._view),

       LoadBehavior.KeepCurrent,

       true);

  28. mitkodi says:

    Thank you Kyle.

    But what if I want the view to be able to call the loading with different LoadBehavior values?

  29. kylemc says:

    @mitkodi

    I'm not sure I understand. In this case the 'view' is a collection view (and not the V in MVVM) and I'm not sure I understand when it's state would affect the load behavior you use. Either way though, you're writing the Load callback in your view model so it shouldn't be too tough to figure out which LoadBehavior you want in the callback.

    Another option you have is to write your own CollectionViewLoader instead of using the default DomainCollectionViewLoader.

  30. mitkodi says:

    Sorry Kyle I was not clear.

    I wrote “view” meaning the UI part of the MVVM. Of course I can bind something from the View to a property in the ViewModel and when the View calls (through a Command for instance) the ViewModel to reload data, the Load callback could use that property to determine the requested LoadBehavior (in general it could be not only LoadBehavior but anything else). And for example in a multi data pages scenario when I want to clear the underlying EntitySet when going between data pages and to keep changes when the user calls reloading of the current data page, I don’t know how to make the single Load callback to behave differently in these two cases.

  31. kylemc says:

    @mitkodi

    It still seems to me like you should have all this information in your ViewModel. For instance, if you kept track of the last page index you loaded (or kept a set of loaded page indices) you could compare that value against the page you're about to load (CollectionView.PageIndex).

    Part of the confusion may be the sorting, paging, etc. are all encapsulated in extension methods. I meant for these methods to simplify common scenarios, but I also recommend developers use reflector to browse the CollectionViewExtensions class. It should clarify how to work a little more closely with the collection view.

    Also, if you have more questions, feel free to contact me directly using the 'Email Blog Author' link above.

  32. Shane says:

    Hi Kyle,

    Thanks for this article. I am a newbie to RIA Services. I have got the example working fine but am unable to take it any further. I have 2 questions that I would be very grateful if you could help me with in terms of your example.

    1. How do I add, remove items from the DomainCollectionView so that it is reflected in the DataGrid i.e. what do I do on the ViewModel and the DomainService?

    2. Is there a way I can bind the datagrid from an ICommand instead of the Constructor? I have tried putting the code from the Constructor in an ICommand but it doesn't seem to work.

    Thanks again,

    Shane

  33. kylemc says:

    @Shane

    Take a look at the IEditableCollectionView interface implemented by the DCV. Those are the methods you'll be interested in. Specifically, AddNew, CommitNew, and Remove are the three you'll need. In your DomainService, you'll need Insert and Delete methods as described here

    msdn.microsoft.com/…/ee707373(v=VS.91).aspx

    You could bind from an ICommand, but it's not the best idea. It's better to set the ViewModel as the DataContext before the xaml is parsed so the binding engine can resolve everything. If you want to swap out things like the DataGrid's ItemsSource, you'll have to familiarize your self with the INotifyPropertyChanged pattern used in ViewModel development.

  34. Raj says:

    Hi Kyle,

    Thanks for the post. I have tried the sample you explained above. It works fine. I have also wired up the CollectionView to the Dataform Itemssource and it didn't work. My dataform didn't load with the data.

    <dataForm:DataForm  Grid.Row="0" x:Name="MyDataForm" Width="600" Height="500"

                                 CommandButtonsVisibility="None"  AutoEdit="True"  IsEnabled="{Binding IsGridEnabled}"  

                              ItemsSource="{Binding CollectionView}">

    Please let us know why it didn't work.

    Thanks!

    Raj

  35. Raj says:

    It worked for me. Thanks.

  36. kylemc says:

    @Raj

    Good to hear you worked it out.

  37. Raj says:

    Hi Kyle,

    I have one more question.

    I am working with a parentchild sample now. I am binding the CollectionView to the DataGrid. I have added 2 bottons to Add, Update items at the bottom of the page. On click of add/update button, i need to open a child window (Dataform) to add/update item. I know that i have to use the same binding (CollectionView in this case) to the both the parent and child. Dataform's current item is bind to CollectionItem's current item. So, on click of update button, i create a new instance of the child control and set the datagrid's datacontext to the child control context  and it is working. I am not sure how i can add a new item in the view model. Please help me to add a new item using the collection view.

    Thanks!

    Raj

  38. Scott Silvi says:

    Hey Kyle –

    I'm struggling to fire off the Load & LoadCompleted operations… My domain service can display my Accounts in codebehind, trying to use this in MVVM. That domainservice has the following code:

    public IQueryable<P_ACCOUNT> GetP_ACCOUNT()

    {

    return this.ObjectContext.P_ACCOUNT;

    }

    ViewModel:

    private readonly DomainCollectionView<P_ACCOUNT> _view;

    private readonly DomainCollectionViewLoader<P_ACCOUNT> _loader;

    private readonly EntityList<P_ACCOUNT> _source;

    public SampleViewModel()

    {

    this._source = new EntityList<P_ACCOUNT>(this._context.P_ACCOUNTs);

    this._loader = new DomainCollectionViewLoader<P_ACCOUNT>(this.LoadAccounts, this.OnLoadAccountsCompleted);

    this._view = new DomainCollectionView<P_ACCOUNT>(this._loader, this._source);

    }

    private LoadOperation<P_ACCOUNT> LoadAccounts() {…}

    private void OnLoadAccountsCompleted(LoadOperation<P_ACCOUNT> op) { … }

    My ViewModel is bound properly. VM constructor fires on page load, but Load & LoadCompleted never fire. Any ideas why?

    Thanks,

    Scott

  39. william s says:

    kyle,

    Are you planning to do any tutorials(MVVM wcf ria sp1) using authorization authentication

    It does not have to be customized, you could use sql membership….Thank you….    

  40. kylemc says:

    @Scott

    Your LoadAccounts method will only get called when you explicitly call Refresh() or DeferRefresh() on the view (or do things like sorting or paging).

    @william

    I hadn't thought about it, but that doesn't mean I won't…

  41. Tim says:

    So, how is Microsoft.Windows.Data.DomainServices.dll meant to be distributed?  I was not able to compile the example because it was missing.  I have VS22010 SP1 – and so have the toolkit SP1 also installed.  Scoured my hard drive and does not seem to be present.

    I have John Papa's example from Firestarter and so was able to get a copy of the dll – but this is a bit confusing.  Is it part of WCF RIA SP1 or not?  How are we meant to get it?

    Otherwise, thanks for the great articles keep 'em coming.

    Would especially appreciate more examples of insert/delete ops.  Way too many examples from MSFT folks with pretty involved designs — e.g. Bookshelf — which don't mention add/deletes at all.  Most of us building LOB apps have to actually create and maintain data — as opposed to just displaying it.

    thx

  42. kylemc says:

    @Tim

    Even though we don't version the toolkit directly with the framework, you should pick up the latest version each time you update RIA.

    At this point the December 2010 release is the most current.

    http://www.silverlight.net/…/riaservices

  43. JoeBrockhaus says:

    Kyle,

    I've got a question about advanced binding in the SL forums (http://j.mp/if18dN).

    Basically, I need to be able to use DataGridTemplateColumns to have more control of what goes on inside cells …

    The idea was to simply wrap each Entity in its own ViewModel that exposes properties for polled and static business calculations. Implementing this has been problematic, however.

    We've done this for other grids, but those grids aren't working with a DCV, just a CollectionView.

    Any ideas?

    TIA

    ~ Joe

  44. kylemc says:

    @Joe

    There's no reason this approach shouldn't work with the DCV. Based on the thread, it looks like the issue was likely related to the source collection you were using and not the DCV specifically. (By the way, feel free to use the non-generic DCV if it makes more sense for your scenarios)

  45. JKears says:

    Hi Kyle big fan of your work 🙂

    I have a view model which I have a DomainCollectionView<T>, DomainCollectionViewLoader<T> and EntityList<T> I am succesfully loading the source through the loader and binding the ICollectionView of the DomainCollectionView to a DataGrid and Pager in one View and have an edit UI Data Form in other UI View.  This all works very well!!!

    What I now need is create a second and separate UI View (bound to same ViewModel) that also has a DataGrid and DataPager and whos data is a filter of the same source EntityList<T> as is used in the first DCV thus I want this DataGrid and Pager to be filtered, while the existing data grid remains unfiltered.  Ideally I would like it if the user pages through the filtered data grid/pager that it executes the original loader pulling down all unfiltered data but that the new ICollectionView shows the correct number of pages of filtered data on the new View while the original (unfiltered DCV) works as is.

    So do I need to create a separate DCV instance and bind my new DataGrid to it or is the an easy way to create filtered collection of the existing DomainCollectionView without applying the filter to that existing DCV?

    My initial thoughts are that I want to use the same loader and source when I create a new DCV and then apply the filtering on this new DCV, but would like to get your feedback.  

    Thanks in advance.

  46. I should also mention that this is a PRISM application and both the existing UI view and the new UI View will potentially be shown at the same time.

  47. kylemc says:

    @JKears

    I'd recommend you don't share any of it :(. The compelling reason for this is the complexity of getting the right page counts. In your second view, you want the data filtered with the correct page count for the filtered data. Since the page count is calculated at the server based on your query, the count returned by your first loader and the count returned by your second won't ever match up. Additionally, it might be easier to write these two scenarios using separate code paths. I'll have another sample out soon, but any time you reset your filter, make sure to re-query the TotalItemCount (using the EntityQuery).

  48. Hi Kyle,

    Even though I have many records in the database, the TotalEntityCount is never greater than the View PageSize. This has the effect of always keeping my Pager at page 1, even though there should be many pages.

    From the tooltip on the TotalEntityCount it mentions that the query must set the IncludeTotalCount = true in  order for it to return the total count of the Entities on the server (which is why I do that below) however setting that to true on the query fails to yield the expected count.

    The backing service is a standard WCF RIA service.

    …. In the example below, I set the PageSize = 4 , I have 6 records in the database, and this returns the first 4 (which is expected) however I am also expecting to see TotalEntityCount = 6.  It equals 4.  

    Can you please help me understand why?

    … DCV Initialization …

              _source = new EntityList<TaskLog>(BusinessTrackerDs.Context.TaskLogs);

               _loader = new DomainCollectionViewLoader<TaskLog>(LoadTaskLogs, OnLoadTaskLogsCompleted);

               _view = new DomainCollectionView<TaskLog>(_loader, _source);

               var notifyingSortDescriptions = (INotifyCollectionChanged)CollectionView.SortDescriptions;

               notifyingSortDescriptions.CollectionChanged += (sender, e) => _view.MoveToFirstPage();

               using (CollectionView.DeferRefresh())

               {

                   _view.PageSize = 4;

                   _view.MoveToFirstPage();

               }

    ———————————–

    … LoadTaskLogs …

    public LoadOperation<TaskLog> LoadTaskLogs()

           {

               IsGridEnabled = true;

               if (UserModel != null && UserModel.CurrentUser != null)

               {

                   IsGridEnabled = false;

                   var qry = (BusinessTrackerDs.Context.GetTaskLogsQuery().SortPageAndCount(_view)

                       .Where(t => t.UserID == UserModel.CurrentUser.ID)

                       .Where(t => t.CreatedOn >= DateTime.Today)

                       .OrderBy(t => t.UpdatedOn));

                   qry.IncludeTotalCount = true;

                   var op =  Context.Load(qry);

                   return op;

               }

               return null;

           }

    ————————————

    … OnLoadTaskLogsCompleted …

              public void OnLoadTaskLogsCompleted(LoadOperation<TaskLog> op)

           {

               IsGridEnabled = true;

               if (op.HasError)

               {

                   // TODO: handle errors

                   op.MarkErrorAsHandled();

               }

               else if (!op.IsCanceled)

               {

                   _source.Source = op.Entities;

                   if (op.TotalEntityCount != -1)

                   {

                       _view.SetTotalItemCount(op.TotalEntityCount);

                   }

               }

           }

  49. … and here is the backing service operation …

         public IQueryable<TaskLog> GetTaskLogs()

           {

               return this.ObjectContext.TaskLogs

                   .Include("ParentTask")

                   .Include("ChildTask")

                   .Include("TaskStatus")

                   .Include("SubTaskLogs")

                   .OrderBy(taskLog => taskLog.ID);

           }

  50. Sometimes I should just keep trying …

    The issue was related to the placement of the .SortPageAndCount(_view) within my LINQ query.

    I moved it to be the last element of the query as shown below and all is working properly now.

    var qry = Context.GetTaskLogsQuery()

                       .Where(t => t.UserID == UserModel.CurrentUser.ID)

                       .Where(t => t.CreatedOn >= DateTime.Today)

                       .OrderBy(t => t.UpdatedOn)

                       .SortPageAndCount(_view);

    Is that a normal behaviour or a bug?

  51. kylemc says:

    That's pretty normal. Think of SortPageAndCount as a sequence of OrderBy-ThenBy-Skip-Take. When the DomainService is determining the TotalEntityCount, it will pull of a Skip-Take pair from the end of your query, but it won't go looking for them any deeper (in the expression tree).

  52. weitzhandler says:

    I am trying to make a simple DomainCollectionView<TContext, TEntity> class that will simplify the use of the process.

    – The query can be provided at construction (via Func<TContext, EntityQuery<TEntity>> argument) or be set to a public property.

    – If the instance works in design mode, it should automatically generate values (maybe there should be an indicator property for this too, also should be able to provide a design-time loader).

    – There should be a Load() (parameterless) method, and some other overloads, that uses the query of the Query property.

    – To summarize, my goal is to have a class the manages everything so I don't have to pollute my ViewModel with various classes and things.

  53. Anonymous says:

    Hi.

    First of all, great piece of functionality. I deserted my old implementation in favor of DCV.

    However, I'm still lacking the ability to introduce custom sorting on a property using lambda expression.

    Say I have a string property which should be sorted alphanumerically.

    How can I achieve such thing?

  54. kylemc says:

    There really isn’t a good way to do this. If you want to implement an alternative sort algorithm, you’ll have to do it in your DomainService code on the server. You then need to tell the server to apply that algorithm (instead of the standard one). There are a few of ways I can think to do this.

    1. Create two query methods; GetMyEntities() and GetMyEntitiesAlphaNumeric()

    2. Pass a parameter to your query method; GetMyEntities(SortType sortType)

    3. There may also be a way to specify a default algorithm in EF or on the DB column. I don’t know if there is, though.

    If you’re statically applying a default sort (all entities sorted by ‘MyProperty’) this isn’t too hard, but if you’re trying to apply this dynamically (column ‘MyProperty’ always sorted alphanumerically) it will be significantly more challenging.

  55. weitzhandler says:

    Please make available via NuGet, thanks a lot!

  56. weitzhandler says:

    Generic design time loader:

    public class DesignTimeLoader<TEntity>  : CollectionViewLoader where TEntity :   Entity, new()

    {

     private IEnumerable<TEntity> _Source;

     public IEnumerable<TEntity> Source

     {

       get

       {

         return _Source ?? (_Source = Enumerable.Range(0, 10).Select(e=> new TEntity()).ToList());

       }

     }

     public override bool CanLoad

     {

       get

       {

         return true;

       }

     }

     public override void Load(object userState)

     {

       OnLoadCompleted(new AsyncCompletedEventArgs(null, false, userState));

     }

    }

  57. Lou Campopiano says:

    Hi Kyle Great Post!

    I have created a ViewModel as you have described, and it works great when there is no filter applied. The filtering does work, however when I click to add a new item on the data form, i get the following error message:

    "cannot change currency when an item has validationerrors or it is being edited and AutoCommit is false"

    My CollectionView is set as follow:

    public ICollectionView CollectionView

           {

               get {

                   this._view.Filter = new Predicate<object>(PersonnelCorrespondsToFilter);

                   return this._view; }

           }

           public bool PersonnelCorrespondsToFilter(object obj)

           {

               Personnel personnel = obj as Personnel;

               if (_filterActive) return personnel.LastName.ToLower().Contains(SearchString.ToLower());

               return true;

           }

    When I comment out the that sets the _view.Filter = new Predicate…. everything works fine.

    Can you tell me what I am missing here?

    Thank you

  58. kylemc says:

    I would not recommend setting the Filter in the CollectionView property accessor (as it might have side effects). If you are trying to implement server-side filtering (usually the better option), take a look at this post instead (blogs.msdn.com/…/domaincollectionview-updates-for-mix-11.aspx). If you really want client-side filtering, try just setting the predicate once when the view is constructed.

  59. Josh says:

    Kyle, thanks for writing this, it is very helpful. I have a large set of data that I think the DCV/DCVL would be perfect for. I don't want to have to load all of this data and pass it down to the client, I'd rather page it and load only the required data upon request. The one catch seems to be that I need to be able to page AND group, and from what I have read here and elsewhere (in particular one post on Stack Overflow that cites a quote from an email from you) it sounds like it is essentially not possible to do paging and grouping at the same time- at least not with the desired effect. Is this true?

  60. kylemc says:

    @Josh. In short, the grid and other built-in controls don't really support server-side grouping well. You can use grouping with paging, but you might find the UI misleading. If you're writing your own controls or templates, you have quite a lot more flexibility and should be able to get the data you want.

    As far as the DCV goes, it will correctly turn client-side grouping descriptors into a server-side OrderBy query so it may still be worth investigating.

  61. Josh says:

    Thanks for the response Kyle. I'm not writing my own controls, but I do have my own templates defined on the grid, in particular the group rows are customized. We are having loading efficiency issues with this large set of data, which is why we are considering the DCV, but in our scenario the data being displayed is only relevant when grouped. We only have one level of grouping, but there are several fields that the user can choose to group by. Can you provide any idea of what kinds of shortcomings we might face in this scenario? Thanks again!

  62. kylemc says:

    @Josh

    It's just a count issue. With paging, the group count will never exceed the max number of items on a page even if there are more items on the server. For example, if you have a group of size 70, but a page size of 10, you'll see seven pages of the same group where it tells you the size is 10. If your groups are typically smaller than the page size or you don't mind having groups split across pages than it probably isn't a problem.

  63. Josh says:

    Oh ok, I think we can probably figure out a way to manage that, such as captioning the group count with something like "10 (On This Page)" or something like that, or possibly eliminate the count all together. Thank you.

  64. Josh says:

    Now that I have spent some time implementing this, I can sort of see what is going on. I have a template defined for the DataGridRowGroupHeader of the grid control to which I am binding the DCV. In this template I have customized it to display some information about the group (namely the date that it is grouped by, and the number of records in that group). In order to achieve this result, I use a paramaterized ValueConverter, where the parameter is just an int to identify which property I want to display.

    Anyway, the grouping more or less works, if I ignore the actual group header. But I noticed that the group headers are not getting the bound data populated until I scroll them far enough off screen, and then scroll back, suddenly the correct values are there. So for example, I have a page size of 25, and on the first page the first group contains 17 items, then there are three groups with 1 item and then one group with 5 items. The headings in the group header are all blank on the first group, but when I scroll down to the second group, it is not blank, it has the correct date and record count shown. The same is true for the other groups below. Then if I scroll back up to the first group, now it has the correct date and record count shown. When I place a break point in the Convert method of the value converter, I can see that the reason the values are blank is that an empty CollectionViewGroupInternal object is being passed into it from the binding- until I scroll down, then at some point, suddenly it is no longer an empty CollectionViewGroupInternal; the group has items, and I can grab properties from those items in order to display in the group header. This is all on the first page. If I go to subsequent pages, I get the same effect though.

    Is that the weird behavior I should expect, or is something else going on here?

  65. kylemc says:

    @Josh  Could we take the rest of this off the comment thread? Feel free to email me directly using the "Email Blog Author" link above.

  66. Josh says:

    @Kyle – Sure thing, sent you a PM. Thanks!

  67. James says:

    Dear Mr. Kyle

    Thank you for helping us newbies to understand Silverlight. I have read at least two books, followed a dozen samples but up to now am unable to send data to a database table using mvvm and RIA Services. please help me with a world sample that can help me grasp this. Currently am using Silverlight business application template in visual studio 2010. Grateful for your Kind Response

    James

  68. kylemc says:

    @James

    You can get the full source from this link. code.msdn.microsoft.com/MVVM-Sample-for-WCF-RIA-115b1f38