GridView filtering with distinct values and converter

14 posts, 1 answers
  1. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 05 Apr 2012 Link to this post

    Hello,
    when we have a column in a GridView that uses a converter, the groupings are sorted correctly, but not the distinct values in the filter.
    As you can see in the attached screenshot, the Switzerland and Algeria countries are not in the right place. That's because, the ISO 3166-1 alpha-2 code for Switzerland is CH and for Algeria, it is DZ.
    The sorting should be made using the converted value.
    Patrick
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 05 Apr 2012 Link to this post

    Hi,

    The distinct values string representations that are displayed to the user are produced by taking the raw distinct value from the source collection and then applying the column’s DataMemberBinding IValueConverter and DataFormatString if any. For example, if you have specified a currency DataFormatString for your column, the user will see a nicely formatted $ 2.22 in the distinct values list. However, underneath this nicely formatted string the original Float value will be preserved and will be used for performing the actual filtering inside the data engine. One of the most common pitfalls is thinking that an IValueConverter defined on the DataMemberBinding of the column will affect the data operations in any way. It will not. The DataMemberBinding IValueConverter and the DataFormatString of the column are used for UI purposes only. They can only change the appearance of data into something more user-friendly such as $ 2.22, but they do not play any role in the data engine. Filtering is always performed with raw data values. 

    If you want to sort your distinct values in a different way, you can attach to the DistinctValuesLoading event of RadGridView and sort the distinct values before you give them to the column:

    private void OnRadGridViewDistinctValuesLoading(object sender, Telerik.Windows.Controls.GridView.GridViewDistinctValuesLoadingEventArgs e)
    {
        var distinctValues = ((Telerik.Windows.Controls.RadGridView)sender).GetDistinctValues(e.Column, false);
            distinctValues = distinctValues.OrderBy(<<sort them here somehow>>)
            e.ItemsSource = distinctValues;
    }

    I hope this helps.
    Kind regards,
    Ross
    the Telerik team
    Sharpen your .NET Ninja skills! Attend Q1 webinar week and get a chance to win a license! Book your seat now >>
  3. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 05 Apr 2012 Link to this post

    Ross,
    I understand what you're saying, but sorting and displaying the distinct values is a UI thing.
    In addition, the values in the grouping headers are sorted correctly, why not the distinct values?
    Patrick
  4. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 05 Apr 2012 Link to this post

    Hello,

    I will try to explain this again.

    The distinct values are sorted according to what their actual values are. The IValueConverter is not used when sorting the distinct values. The IValueConverter is not used for data operations and sorting is a data operation.

    Let me give you an example. Imagine that your source collection comes from Entity Framework (LINQ to Entities) or from LINQ to SQL and the sorting is done directly in SQL Server. You can't sort values in the SQL Server using an IValueConverter which does not exist there. The LINQ query will simply fail and be rejected.

    Please, use the DistinctValuesLoading event -- it allows you to sort you distinct values in any way that you like.

    Thank you.

    Greetings,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  5. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 05 Apr 2012 Link to this post

    Ross,
    I still understand what you say, and have said in your previous reply, but can you explain why the grouping headers are sorted correctly?
    Patrick
  6. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 05 Apr 2012 Link to this post

    Hi,

    The reason for this is that when the query provider is LINQ to Objects the GroupBy is executed over the converted values. I will see whether I can implement sorting of the raw distinct values according to their converted string values out-of-the-box and let you know about my findings.

    You have to have in mind though that if different distinct values map to one and the same converted value you will still see this string value several times, because the Distinct operation is performed on the raw data and not on the converted values. Here is a practical example:

    If you have these distinct values: 1, 2, 3 and they are converted by an IValueConverter to "Odd", "Even", "Odd" you will see this in the distinct values list:

    "Odd"
    "Even"
    "Odd"

    because the actual raw distinct values that sit behind these string are still 1, 2, 3 and they are different. This is by design and cannot be changed for the reasons I have explained before. The Data Engine can only work with data values -- it cannot work with UI-specific values which might be localized and so forth.

    But I guess that your case with the states will not be such and you will not have repeating distinct values.

    Now, until I try to find a solution for the sorting I can suggest two very easy solutions in case you are binding the grid to an in-memory collection.
     
    A. Use FilterMemberPath.

    1. Create a new read-only property on your business object that will return "Swtizerland" for the CH instance and Algeria for the DZ instance, i.e. you get the point. You may call it CountryName.
    2. Then set your column's FilterMemberPath to be CountryName and there you go -- your column will still be bound to the Country property, but it will be filtered on the CountryName property.

    I have prepared a sample project demonstrating this approach.

    B. Order the distinct values in the event handler like this:

    private void playersGrid_DistinctValuesLoading(object sender, Telerik.Windows.Controls.GridView.GridViewDistinctValuesLoadingEventArgs e)
    {
        if (e.Column.UniqueName == "Country")
        {
            var countryCodes = ((RadGridView)sender).GetDistinctValues(e.Column, true);
            e.ItemsSource = countryCodes.Cast<string>().OrderBy(countryCode => Country.GetCountryName(countryCode));
        }
    }

     
    I have attached a second sample project for this approach as well.

    I hope that one of these two approaches will meet your requirements.

    I hope this helps. All the best,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  7. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 05 Apr 2012 Link to this post

    Ross,
    thank you for your answer, that explains the difference between grouping and filtering.
    I'm waiting for more information about your findings.
    Patrick
  8. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 05 Apr 2012 Link to this post

    Hi,

    Did you check the two projects that I sent you? Is none of them working for your case? Both of them achieve what you want with just a few lines of code.

    I am afraid that if I change the sorting logic of the distinct values I will do two major things:

    1. This will be a huge breaking change for a behavior that has been such for the last 4 years and we have never received a request to change it. If I change this behavior I will break all of our existing customers which might lead to unforseen consequences.
    2. Performance of populating the distinct values will degrade significantly since now they will have to be sorted on a function that uses the IValueConverter and the DataFormatString.

    On the other hand, you can easily sort the distinct values like in the two sample projects I sent you. Why is this not an option?

    Kind regards,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  9. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 05 Apr 2012 Link to this post

    Ross,
    I don't say that the solutions don't work, but you said in your previous message:
    I will see whether I can implement sorting of the raw distinct values according to their converted string values out-of-the-box and let you know about my findings.
    So I can tell you that I'm waiting for more information.

    The only problem with both your solutions is that they must be copied into each GridView that has a column with a country. Adding to that the fact that there are some other columns that also have the same pattern (a code and a converter), this will be a lot of work to correct all of them. In addition, in the case you have a solution right in the box, this will avoid me doing all the changes and removing them all if they are no more useful.
    So I can live for the moment with this limitation, awaiting for a better solution.

    Now, if you change the sorting logic, you will not make a breaking change, because the values will be sorted in a more natural way: they are sorted by their displayed value, not by a non-visible property.
    The performance problem is another one, but there are solutions to it: keep a cache of original and converted values, do the sort on the converted values and keep the original values after the sort.

    A last note about the DistinctValuesLoading: if the event exists on the column, in addition to the whole grid, it would be possible to define a custom column class that does the job automatically (I've already done it to display automatically the flag and the name of the country). Unfortunately, it would be complicated with do it with only the event on the GridView: when can I attach an event handler? And I must each time check if the column is the right one, which has a performance penalty.

    Patrick
  10. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 05 Apr 2012 Link to this post

    Hi,

    I understand your point. I have an idea that I hope will make everyone happy.

    I can introduce a new enumeration property on the GridViewDataColumnClass called DistinctValueSortMode which will have two possible values -- Raw (the current default one) and Display (the mode that you need).

    I will leave Raw as the default one so I don't break anyone and you will change the mode to Display on the columns that you need.

    Another limitation is that the Display sort mode will only be possible if the underlying LINQ provider is LINQ to Objects provider, i.e. EnumerableQuery. If it is System.Data.Objects.ELinq.ObjectQueryProvider (Entity Framework) or System.Data.Linq.Table<TEntity> (LINQ to SQL) this will not be possible. The reason for this is that I will be calling a Telerik-specific Func inside the Lambda that I supply to the OrderBy clause when sorting the distinct values. This Func is the one that accepts a raw value and returns the converted string.

    How does this sound? Will this solve your problems?

    Greetings,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  11. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 05 Apr 2012 Link to this post

    Ross,
    for me, and perhaps for others, it will resolve this kind of problem. I will be able to set this property on the custom column constructor, so there will be no problem to have the change made globally.
    Patrick
  12. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 05 Apr 2012 Link to this post

    Hi,

    I have another great idea. You mentioned that you have derived from the column, right? If that is so then...

    If I make our SortDistinctValues method protected virtual and add another new method called SortDistinctValuesByDisplay you can simply do this and all will be set:

    public class MyColumn : GridViewDataColumn
    {
        protected override IQueryable SortDistinctValues(IQueryable source)
        {
            return base.SortDistinctValuesByDisplay(source);
        }
    }

    In this way, I will allow you to change the behavior by simply overriding a method, which I think is more elegant than polluting the column with yet another property.

    What do you thing about this approach? Isn't it better?

    All the best,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  13. Patrick
    Patrick avatar
    459 posts
    Member since:
    Aug 2012

    Posted 06 Apr 2012 Link to this post

    Ross,
    it is a great idea for me, but the problem I see is that other users must define a custom column to use this feature. So I think that the property way is the better to follow.
    Patrick
  14. Answer
    Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 06 Apr 2012 Link to this post

    Hi,

    I consulted my colleagues and we decided to implement this feature by following the "protected virtual" pattern for many different reasons.

    I have attached a sample project with unofficial binaries built on my machine that demonstrates what we will officially release with our "Next Internal Build" next week. You can use them only for testing. Once we release our next "Latest Internal Build" on Monday, you can upgrade to it "officially".

    I hope this will help your project.

    Regards,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Back to Top