This is a migrated thread and some comments may be shown as answers.

Custom Grouping by Range

7 Answers 151 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Tom
Top achievements
Rank 1
Tom asked on 25 Jun 2010, 03:27 PM
Hello Everybody

I would like to be able to create custom groupings that allow users to group by a range of values rather than by a specific value of a particular member.  For example, if there is a "severity" column that has integer values from 0-199, I'd like to be able to create 2 groups of severity 0-99 and 100-199 instead of the default of 200 groups, one for severity 1, one for severity 2, etc.

I've hunted through the docs and it looks like I may be able to extend from Telerik.Windows.Data.GroupDescriptor to achieve my goal, but the documentation on the subject is not very helpful.  Can anyone explain how the GroupDescriptor class creates a grouping, or even better, provide code examples of how its done?

Thanks,
Tom

7 Answers, 1 is accepted

Sort by
0
Pavel Pavlov
Telerik team
answered on 28 Jun 2010, 09:34 AM
Hello Tom,

I am afraid extending the GroupDescriptor would not be in much help here. I would suggest to expose a Range property to your business object and use it in the GroupMemberPath of the Severity column.

This is the only solution I can suggest  so far.

Regards,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Tom
Top achievements
Rank 1
answered on 28 Jun 2010, 04:46 PM
Hi Pavel

I'm afraid my situation is a bit more complicated because my columns are autogenerated from a DataTable.  I've tried doing something like the example below to no avail.  I'm pretty new to WPF and the RadControls, so I'm not too sure what is going on.  When I run it, the Severity column is no longer groupable at all.  Is it possible to do it with generated columns, or am I going to have to manually define them?

        public MainWindow() 
        { 
            InitializeComponent(); 
            this.GridView.ItemsSource = GetDataTable().DefaultView; 
        } 
 
        private void GridView_AutoGeneratingColumn(object sender, Telerik.Windows.Controls.GridViewAutoGeneratingColumnEventArgs e) 
        { 
            if (e.Column.Header.ToString().Equals("SeverityRange")) 
                e.Column.IsVisible = false
            else if (e.Column.Header.ToString().Equals("Severity")) 
            { 
                GridViewDataColumn col = e.Column as GridViewDataColumn; 
                col.GroupMemberPath = "SeverityRange"
                col.IsGroupable = true
            } 
        } 

Thanks,
Tom
0
Pavel Pavlov
Telerik team
answered on 01 Jul 2010, 12:59 PM
Hello Tom,

Please ensure your "Severity" type implements the IEquatable interface.  This might be the reason for the column not being groupable.

Greetings,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Tom
Top achievements
Rank 1
answered on 01 Jul 2010, 08:30 PM
The Severity property was just an int, so it already was IEquatable.  I went back and reworked the Range class so it implemented IEquatable (before I had just overridden Object.Equals()), and I was then able to group.  I can now drag the severity column to the group panel to create groups based on a range, but there is still a problem.  The group headers display the correct range, but they are being duplicated.  Each row is put in its own group, even if some rows fall into the same range.  I've attached a picture illustrating the problem.  Any ideas?


0
Pavel Pavlov
Telerik team
answered on 06 Jul 2010, 01:03 PM
Hello Tom,

I was trying to find out what the trouble may be but I will need the code of your business objects. Can you plese paste some c# . I will try to built in into a sample app and see if I get the same behavior here.

Regards,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Tom
Top achievements
Rank 1
answered on 06 Jul 2010, 02:48 PM
I've created a sample that displays my problem.  It should be pretty easy to follow.  The Alarm class is just 3 basic fields (severity, description and date) and a Range property which is calculated when the severity property is set.  If you want, I can submit the whole thing as a project, but I figured it was small enough to just paste here.  My grid definition is a simple: <telerik:RadGridView x:Name="GridView" ColumnWidth="*" AutoGeneratingColumn="GridView_AutoGeneratingColumn"/>

Here is the Grid's code behind:
    public partial class MainWindow : Window 
    { 
        public MainWindow() 
        { 
            InitializeComponent(); 
            this.GridView.ItemsSource = Alarm.MakeDataTable().DefaultView; 
        } 
 
        void GridView_AutoGeneratingColumn(object sender, Telerik.Windows.Controls.GridViewAutoGeneratingColumnEventArgs e) 
        { 
            if (e.Column.Header.ToString().Equals("Range")) 
                e.Column.IsVisible = false
            else if (e.Column.Header.ToString().Equals("Severity")) 
            { 
                GridViewDataColumn col = e.Column as GridViewDataColumn; 
                col.GroupMemberPath = "Range"
                col.IsGroupable = true
            } 
        } 
    } 


and Here is the code for the Alarm/Range classes:
public class Alarm 
    { 
        private string description; 
        private DateTime date; 
        private int severity; 
        private Range range; 
        private int oneHundred = 100; 
 
        public string Description { get { return description; } set { description = value; } } 
        public DateTime Date { get { return date; } set { date = value; } } 
        public int Severity  
        { 
            get { return severity; } 
            set 
            { 
                severity = value; 
                int hundreds = severity / oneHundred; 
                range = new Range(hundreds * oneHundred, (hundreds + 1) * oneHundred - 1); 
            } 
        } 
        public Range Range 
        { 
            get { return range; } 
        } 
        #region Alarm Generation 
 
        private static Random random = new Random(); 
 
        public static Alarm MakeRandomAlarm() 
        { 
            Alarm alarm = new Alarm(); 
            alarm.Date = DateTime.Now.AddDays(random.NextDouble() * 100); 
            alarm.Date = alarm.Date.AddMinutes(random.NextDouble() * 10000); 
            alarm.Severity = random.Next(1000); 
            alarm.Description = descriptions[random.Next(descriptions.Length)]; 
            return alarm; 
        } 
 
        public static DataTable MakeDataTable() 
        { 
            DataTable dt = new DataTable(); 
            dt.Columns.Add("Date"typeof(DateTime)); 
            dt.Columns.Add("Severity"typeof(int)); 
            dt.Columns.Add("Range"typeof(Range)); 
            dt.Columns.Add("Description"typeof(string)); 
             
            for(int i = 0; i < 5; i++) 
            { 
                Alarm alarm = MakeRandomAlarm(); 
 
                //I'm hard coding the same severity for each alarm for now  
                //so they should all end up in the same group 
                alarm.Severity = 5;      
                dt.Rows.Add(alarm.Date, alarm.Severity, alarm.Range, alarm.Description); 
            } 
            return dt; 
        } 
 
        private static string[] descriptions = new string[]  
        { 
            "Coolant level is low"
            "Belt Speed of Pump1 is normal"
            "The PSI in Tank1 is normal"
            "Level guage is normal"
            "Compressor gauge is reading normal"
            "Core humidity is normal"
            "Belt1 on the Box LIne is slow"
            "Belt Speed of Pump1 is low"
            "The PSI in Tank1 is low"
        }; 
        #endregion 
    } 
 
    public class Range : IComparable<Range>, IEquatable<Range> 
    { 
        private int low;          //the lower bound, inclusive 
        private int high;         //the upper bound, inclusive 
 
        public int Low { get { return low; } set { low = value; } } 
        public int High { get { return high; } set { high = value; } } 
 
        public Range(int low, int high) 
        { 
            this.Low = low; 
            this.High = high; 
        } 
 
        /// <summary> 
        /// Compares two ranges. 
        /// The range with the lower value of the "Low" member is considered less than (aka will return -1) 
        /// If two ranges have the same value for "Low", then the range with the lower "High" value is considered less than the other 
        /// If both the high and low values are the same, then the two ranges are equal. 
        /// </summary> 
        /// <param name="other"></param> 
        /// <returns></returns> 
        int IComparable<Range>.CompareTo(Range other) 
        { 
            if (Equals(other)) 
                return 0; 
            else if (Low < other.Low) 
                return -1; 
            else if (other.Low < Low) 
                return 1; 
            else if (other.High < High)     //if the low values are equal, consider the range with the lower high value to be less 
                return 1; 
            else 
                return -1; 
        } 
 
        public override bool Equals(object obj) 
        { 
            if (obj is Range) 
            { 
                Range range = (Range)obj; 
                bool sameLow = range.Low == Low; 
                bool sameHigh = range.High == High; 
                return sameLow && sameHigh; 
            } 
             
            return false
        } 
 
        bool IEquatable<Range>.Equals(Range range) 
        { 
            bool sameLow = range.Low == Low; 
            bool sameHigh = range.High == High; 
            return sameLow && sameHigh; 
        } 
 
        public override string ToString() 
        { 
            return Low + " - " + High; 
        } 
    } 

Thanks,
TOm
0
Accepted
Pavel Pavlov
Telerik team
answered on 09 Jul 2010, 10:24 AM
Hi Tom,

Thanks for sending the code. I found what is missing. When overriding Equals , you need to override the GetHashCode() as well .

Two objects that  appear as equal should return the same  hash code.

*To avoid such complexity you can also  make the Range property a string. This will automatically solve all the equality related issues.

Greetings,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
Tags
GridView
Asked by
Tom
Top achievements
Rank 1
Answers by
Pavel Pavlov
Telerik team
Tom
Top achievements
Rank 1
Share this question
or