QueryableDomainServiceCollectionView initial FilterDescriptors not shown on RadGridView

12 posts, 1 answers
  1. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 24 Feb 2011 Link to this post

    Hello,

    I use the MVVM approach to populate a radgridview via a QueryableDomainServiceCollectionView. I create an EntityQuery and based on that I create the QueryableDomainServiceCollectionView. After this step I set AutoLoad to true and the grid will show the loaded data. If I filter the grid via the gridviewfiltericon, the FilterDescriptors property of the QueryableDomainServiceCollectionView is updated and the data is reloaded. The grid also indicates that there is filtering active on that column.

    So far so good.

    What I would like to do now, is to set some initial filters on the QueryableDomainServiceCollectionView. (see code snippet). Problem with this approach is that the gridview doesn't hightlight the filtericon so the user doesn't know the data is being filtered. More importantly, the user cannot delete the filter so that all data is fetched from the server. The data fetched is however correct according to the filterdescriptor.
    public void LoadData()
            {
                    Query = _context.GetAllScheduleDayListDtoQuery();
                    ScheduleDayCollectionView = new AntonPagedCollectionView<ScheduleDayListDto>(_context, Query);
                    ScheduleDayCollectionView.FilterDescriptors.Add(new FilterDescriptor { Member = "IsAlreadyCoupled", Value = true, Operator = FilterOperator.IsEqualTo });
                    ScheduleDayCollectionView.AutoLoad = true;
            }

    How can I make sure that the RadGridView displays the FilterDescriptor so that users can turn the filtering off or change the filtering?

    Kind regards,
    Sodi We
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 24 Feb 2011 Link to this post

    Hello Sodi We,

    Let me try to explain how things actually work.

    There is an interface called IQueryableCollectionView. It includes several things, but the ones that matter here are FilterDescriptors. This interface is a "contract" between different Telerik controls/components such as RadGridView, RadDataFilter, and RadDomainDataSource (or QDSCV in your case).

    Now, when the ItemsSource of the grid is set to something (QDSCV in this case), the grid will check whether this class implements the IQueryableCollectionView interface. If it does - it will know that this is another Telerik class and will start synchronizing all the descriptors automatically.

    When the user performs filtering through the RadGridView UI funnels here is what actually happens:

    1. The grid is filtered on the client-side. A ColumnFilterDescriptor is added to its FilterDescriptors.
    2. Since its ItemsSource (QDSCV) is recognized as one of "our" components, the FilterDescriptor(s) are automatically transferred to the the QDSCV, i.e. they are synchronized with the QDSCV.
    3. Since the QDSCV has AutoLoad set to true, it immediately sends a new request to the server with this new filtering criteria.
    4. The filtered data comes back from the server and the grid is repopulated with it.

    So, if you want to start the whole page pre-filtered, you have to add the appropriate ColumnFilterDescriptors to RadGridView, which in turn will trigger an asynchronous request to the server and sooner or later the filtered data will come back from the server.

    The important thing here is that if you want to make RadGridView show any kind of filtering UI -- you have to work with the ColumnFilterDescriptor class. It provides the link between actual filtering criteria and the UI that represents it. In other words -- you can add a plain FilterDescriptor to RadGridView and the data will be filtered, but the grid will never know which column does this FilterDescriptor belong to and will never light up its funnels. That is why there is the ColumnFilterDescriptor class. It provides the link between filtering and a specific column -- so that the filtering UI can read all the info from the descriptor and initialize itself, i.e. the funnel will turn orange and when you open up the filtering control you will have the respective distinct values checked, for example.

    I have this blog post that covers the while pre-filtering topic in greater detail and provides a sample project.

    I hope this helps. Let me know if there are problems with the implementation.

    Greetings,
    Ross
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
  3. DevCraft banner
  4. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 24 Feb 2011 Link to this post

    Thank you for the quick answer.

    I'm still having some issues however. Because I'm using the MVVM approach, my first idea was to create the filterdescriptors in the ViewModel. The problem with this approach is that the ColumnFilterDescriptor needs to be constructed with the column as parameter. Because my ViewModel doesn't know its View, it cannot get this column.

    Other approach: Create a behaviour that is attached to the grid. In this behaviour subscribe to the Grid.Columns.CollectionChangedEvent. If the Column is added to the grid, create the ColumnFilterDescriptor based on some parameters that are passed via Xaml to this behaviour. After the filterdescriptor is created and added to the grid, the QDSCV is created and bound to the itemssource of the grid. (see LoadData in snippet of first post). When all is executed, the filter icon is highlighted, but all data has been fetched and shown in the grid. The ColumnFilterDescriptor that was present in the FilterDescriptors collection of the grid, was not translated to a FilterDescriptor in the QDSCV.

    Other approach: Because the approach above loads all data without taking the FilterDescriptors into account, I tried to create the ColumnFilterDescriptor on the grid, and also a FilterDescriptor on the QDSCV. Initially everything is shown ok, but when I delete the column filter via the UI, the data is not reloaded. Probably because the FilterDescriptor created in the ViewModel is still active on the QDSCV.

    Other approach: After reading your post again I focused on following part:
    Now, when the ItemsSource of the grid is set to something (QDSCV in this case), the grid will check whether this class implements the IQueryableCollectionView interface. If it does - it will know that this is another Telerik class and will start synchronizing all the descriptors automatically.
    So I started thinking, maybe the order of the creation of the filterdescriptors on the grid and setting the itemssource is wrong. So now I explicitly set the ItemsSource first, than I set the property InitialFilterDescriptors to which the behaviour is listening. The behaviour now adds the ColumnFilterDescriptor to the FilterDescriptors collection of the grid. After this, I Load the data via the viewmodel. But again, the filterdescriptors don't seem to be translated to a filter on the QDSCV.


    Any help?

    Just to make sure, this is the scenario aimed for:
    A user opens a page and sees a gridview with some of its filtericons highlighted. The grid shows only the data according to the filters. Behind the screens, only the data visible for the user is fetched from the server. The grid is being paged. A user can easily change, add or remove filters.
    It seems to me this is a very common scenario, but I can't get it to work with a QDSCV and paging.
  5. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 24 Feb 2011 Link to this post

    Hello,

    After reading some other posts... Is it possible that the ColumnFilterDescriptors are only translated to FitlerDescriptors on the QDSCV when AutoLoad is set to true? If that is the case, I should have something like this:
    1. Create the QDSCV
    2. Set is to AutoLoad
    3. Set the ItemsSource of the Grid
    4. Add the ColumnFilterDescriptor
    5. Start loading the data

    If I'm correct, than step 4 will also create the filter in the QDSCV? But will step 2 not cause the QDSCV to load the unfiltered data? And if so, can the QDSCV load data in step 5 because it is already loading?
  6. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 25 Feb 2011 Link to this post

    Hello Sodi,

    Here is what I think you should do:

    1. Create the QDSCV.
    2. Set the ItemsSource of the Grid to the QDSCV in order to "link" them together.
    3. Add the ColumnFilterDescriptor to the Grid. The ColumnFilterDescriptor will be automatically transferred to the QDSCV, but no load will occur yet, since AutoLoad is still false.
    4. Set QDSCV.AutoLoad to true --> this will trigger an asynchronous load request to the server. Since the QDSCV already has the ColumnFilterDescriptor added, it will "bring back" the filtered data from the server.

    Try this and let me know if there are problems.

    Kind regards,
    Ross
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
  7. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 25 Feb 2011 Link to this post

    Hello,

    That is what I've done, but no FilterDescriptor was added to the QDSCV.
    public void LoadData()
            {
                    Query = _context.GetAllScheduleDayListDtoQuery();
                    ScheduleDayCollectionView = new QueryableDomainServiceCollectionView(_context, Query);
     
                    var fd = new FilterDescriptor { Member = "IsAlreadyCoupled", Value = true, Operator = FilterOperator.IsEqualTo };
                    InitialFilterDescriptors = new List<FilterDescriptor> { fd };    //this will trigger the behaviour attached to the grid to create and add the ColumnFilterDescriptor
     
                    ScheduleDayCollectionView.AutoLoad = true;
                    //when debugging, I notice that there are no FilterDescriptors set on the ScheduleDayCollectionView
            }


  8. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 25 Feb 2011 Link to this post

    Hello Sodi We,

    Unfortunately, I could not see where you add the ColumnFilterDescriptor to RadGridView after you have set the QDSCV as its ItemsSource. From the source code that you have pasted it seems that you are not following the steps I have described in my previous post.

    I will post the steps that you need to follow again:

    1. Create the QDSCV.
    2. Set the ItemsSource of RadGridView to the QDSCV in order to "link" them together.
    3. Add the ColumnFilterDescriptor toRadGridView . The ColumnFilterDescriptor will be automatically transferred to the QDSCV, but no load will occur yet, since AutoLoad is still false.
    4. Set QDSCV.AutoLoad to true --> this will trigger an asynchronous load request to the server. Since the QDSCV already has the ColumnFilterDescriptor added, it will "bring back" the filtered data from the server.

    I hope this helps.

    All the best,
    Ross
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
  9. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 25 Feb 2011 Link to this post

    Okay, I'll be a bit more specific.

    ViewModel:
    public void LoadData()
            {
                    Query = _context.GetAllScheduleDayListDtoQuery();
                    ScheduleDayCollectionView = new QueryableDomainServiceCollectionView<ScheduleDayListDto>(_context, Query);
     
                    var fd = new FilterDescriptor { Member = "IsAlreadyCoupled", Value = true, Operator = FilterOperator.IsEqualTo };
                    InitialFilterDescriptors = new List<FilterDescriptor> { fd };   
     
                    ScheduleDayCollectionView.AutoLoad = true;           
            }
     
            private QueryableDomainServiceCollectionView<ScheduleDayListDto> _scheduleDayCollectionView;
            public QueryableDomainServiceCollectionView<ScheduleDayListDto> ScheduleDayCollectionView
            {
                get { return _scheduleDayCollectionView; }
                set
                {
                    if (_scheduleDayCollectionView == value)
                        return;
                    _scheduleDayCollectionView = value;
                    NotifyPropertyChanged("ScheduleDayCollectionView");
                }
            }

            private List<FilterDescriptor> _initialFilterDescriptors;
            public List<FilterDescriptor> InitialFilterDescriptors
            {
                get { return _initialFilterDescriptors; }
                set
                {
                    if (_initialFilterDescriptors == value)
                        return;
                    _initialFilterDescriptors = value;
                    NotifyPropertyChanged("InitialFilterDescriptors");
                }
            } 


    View:
    <telerik:RadGridView  x:Name="scheduleList" Grid.Row="1"
                                      Style="{StaticResource GeneralRadGridViewStyle}"
                                      ItemsSource="{Binding ScheduleCollectionView}"
                                  SelectedItem="{Binding SelectedSchedule, Mode=TwoWay}"
                                      IsReadOnly="True" 
                                      AutoExpandGroups="True"
                                      EnableColumnVirtualization="True"
                                      IsBusy="{Binding IsBusy}"
                                      EnableRowVirtualization="True">
                <i:Interaction.Behaviors>
                    <a:GridViewFilterBehavior FilterDescriptors={Binding InitialFilterDescriptors} />
                </i:Interaction.Behaviors>
     
                <telerik:RadGridView.Columns>
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding ScheduleCode}" Width="Auto"/>
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding ScheduleType}" Width="Auto"/>
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding NrOfWeeks}" Width="Auto" />
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding HoursPerWeek}" Width="Auto" />
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding HoursPerDay}" Width="Auto" IsFilterable="False" IsGroupable="False" IsSortable="False"/>
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding DayShiftMonday}" Width="Auto" IsFilterable="False" IsGroupable="False" IsSortable="False"/>
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding Description}" Width="Auto" />
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding NrOfEmployees}" Width="Auto" />
                </telerik:RadGridView.Columns>
     
            </telerik:RadGridView>
     
            <telerik:RadDataPager Grid.Row="2" Margin="0, 0, 0, 1"
                                    Source="{Binding ScheduleCollectionView}"
                                    DisplayMode="All"
                                    IsTotalItemCountFixed="True"/>

    The behaviour:
    public class GridViewFilterBehavior : Behavior<RadGridView>
        {
     
            public static readonly DependencyProperty FilterDescriptorsProperty =
                DependencyProperty.Register("FilterDescriptors", typeof(List<Telerik.Windows.Data.FilterDescriptor>), typeof(GridViewFilterBehavior), new PropertyMetadata(null, FilterDescriptorsChanged));
     
     
            public List<Telerik.Windows.Data.FilterDescriptor> FilterDescriptors
            {
                get { return (List<Telerik.Windows.Data.FilterDescriptor>)GetValue(FilterDescriptorsProperty); }
                set { SetValue(FilterDescriptorsProperty, value); }
            }
     
            private RadGridView Grid
            {
                get
                {
                    return AssociatedObject as RadGridView;
                }
            }      
     
            private static void FilterDescriptorsChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
            {
                var b = o as GridViewFilterBehavior;
                foreach (var fd in b.FilterDescriptors)
                {
                    var col = b.Grid.Columns[fd.Member];
                    if (col != null)
                    {
                        var cfd = new ColumnFilterDescriptor((IDataFieldDescriptor)col);
                        cfd.DistinctFilter.DistinctValues.Add(fd.Value);
                        b.Grid.FilterDescriptors.Add(cfd);
                    }
                }
            }
        }

    When I open the page, the LoadData() method is called in the ViewModel.
    1. Create the QDSCV
    2. Because the ViewModel notifies whenever the QDSCV is set, the ItemsSource of the Grid is linked to the QDSCV.
    3. I then create and fill the property InitialFilterDescriptors. Because of the binding on the behaviour, I create ColumnFilterDescriptors on the appropriate column. (Okay, this mechanism could be better, because now I  expect the filter.Member to be the same as the ColumnName)
    4. We are back in the ViewModel and the QDSCV is set to AutoLoad. But when I set a breakpoint here, I notice that no FilterDescriptors have been added.

    I hope this makes it more clear.

    Kind regards,
    Sodi
  10. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 25 Feb 2011 Link to this post

    I also noticed, that the FilterDescriptorsChangedEvent in the Behaviour occurs before the Grid.Loaded event. Could this be the reason?
    If so, I'm not sure how to let the ViewModel wait for something that is happening in the View (Grid.Loaded). This seems to be opposed to the whole MVVM paradigm...
  11. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 25 Feb 2011 Link to this post

    Hello Sodi We,

    You can't bind RadGridView.FilterDescriptors to your view model property. RadGridView.FilterDescriptors is a collection property and is read-only. Collection properties don't have setters.

    Best wishes,
    Ross
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
  12. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 25 Feb 2011 Link to this post

    As you can see in the Behaviour code snippet, I'm not binding directly to the FilterDescriptors. Instead I'm looping over the bound collection and creating the ColumnFilterDescriptors in code.

    FYI, if I wait for the Grid.Loaded event and then do what I'm trying to explain, everything works correctly. So adding FilterDescriptors to the grid, when it is not loaded fully (=before the Grid.Loaded event is thrown) will not copy the FilterDescriptors to the QDSCV.
  13. Answer
    Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 25 Feb 2011 Link to this post

    Hello Sodi We,

    Then I think that you should do this after the Loaded event. It might not be 100% MVVM as you put it, but the addition of FilterDescriptors has to happen after the ItemsSource of the grid has been set to the QDSCV. I am afraid that there is not other way to do this.

    I really hope this helps. Thank you for your understanding.

    Kind regards,
    Ross
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
Back to Top
DevCraft banner