Binding properties of a composite objects to HighBinding and LowBinding in RangeBarSerires

7 posts, 1 answers
  1. Alex
    Alex avatar
    14 posts
    Member since:
    Nov 2013

    Posted 16 Aug 2014 Link to this post

    In my application I am dealing a lot with objects that have a concept of a range, for that reason I abstracted the logic related to ranges into a separate class. Below is the skeleton of the class, though in my application this class does much more (ex. checks for overlapping, containment, equality, uniqueness,  etc).

    public class Range : INotifyPropertyChanged
        {
            private int high;
            private int low;
     
            public Range(int low, int high)
            {
                this.low = Low;
                this.high = High;
            }
     
            public event PropertyChangedEventHandler PropertyChanged;
     
            public int High
            {
                get
                {
                    return this.high;
                }
     
                set
                {
                    this.high = value;
                    this.OnPropertyChanged();
                }
            }
     
            public int Low
            {
                get
                {
                    return this.low;
                }
     
                set
                {
                    this.low = value;
                    this.OnPropertyChanged();
                }
            }
     
            [NotifyPropertyChangedInvocator]
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }

    My business logic classes include this Range class and rely on this class to perform range related operations, for example:

    public class HeartRateZone : INotifyPropertyChanged
        {
            private string name;
            private bool isSelected;
     
            private Range range;
     
            public HeartRateZone(string name, Range range)
            {
                this.Range = range;
                Name = name;
            }
     
            public event PropertyChangedEventHandler PropertyChanged;
     
            public string Name
            {
                get
                {
                    return this.name;
                }
     
                set
                {
                    this.name = value;
                    this.OnPropertyChanged();
                }
            }
     
            public bool IsSelected
            {
                get
                {
                    return this.isSelected;
                }
     
                set
                {
                    this.isSelected = value;
                    this.OnPropertyChanged();
                }
            }
     
            public Range Range
            {
                get
                {
                    return this.range;
                }
     
                set
                {
                    this.range = value;
                    this.OnPropertyChanged();
                }
            }
     
            [NotifyPropertyChangedInvocator]
            private void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }

    I would like to chart my business objects using RangeBarSeries, and my expectation was that I could bind HighBinding and LowBinding properties of the RangeBarSeries to the corresponding High and Low properties of my Range class (see below) embedded in my business logic classes. 
    Note: DataPointsCollection in the example below, is of type ObservableCollection<HeartRateZones>, where HeartRateZone contains Range class.
    <telerikChart:RangeBarSeries x:Name="BarSeries"
                   CategoryBinding="Name"                        
                   ItemsSource="{Binding ElementName=HeartRateZoneChartControl, Path=DataPointsCollection}"
                   Canvas.ZIndex="2"
                   HighBinding="Range.High"
                   LowBinding="Range.Low">

    Unfortunately, I am getting this exception:

    {System.ArgumentException: Could not find property 'Range.High' for type 'ZonesVisualization.Controls.HeartRateZone'
       at Telerik.Windows.DynamicHelper.CreatePropertyValueGetter(Type type, String propertyName)
       at Telerik.Windows.Controls.PropertyNameDataPointBinding.GetValue(Object instance)
       at Telerik.Windows.Controls.RangeSeriesBaseDataSource.InitializeBinding(DataPointBindingEntry binding)
       at Telerik.Windows.Controls.ChartSeriesDataSource.GenerateDataPoint(Object dataItem, Int32 index)
       at Telerik.Windows.Controls.ChartSeriesDataSource.BindCore()
       at Telerik.Windows.Controls.ChartSeriesDataSource.Bind()
       at Telerik.Windows.Controls.ChartSeriesDataSource.Rebind(Boolean itemsSourceChanged, IEnumerable newSource)
       at Telerik.Windows.Controls.ChartSeriesDataSource.set_ItemsSource(IEnumerable value)
       at Telerik.Windows.Controls.ChartSeries.OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.RaisePropertyChangeNotifications(DependencyProperty dp, Object oldValue, Object newValue)
       at System.Windows.DependencyObject.UpdateEffectiveValue(DependencyProperty property, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, ValueOperation operation)
       at System.Windows.Dependen

    Attaching a project that reproduces this issue (renamed zip to jpg).
    Thanks.
    Alex.
  2. Rosy Topchiyska
    Admin
    Rosy Topchiyska avatar
    551 posts

    Posted 19 Aug 2014 Link to this post

    Hello Alex,

    Thank you for contacting us.

    Unfortunately, the PropertyNameDataPointBinding binding, used when binding the chart series to business objects, does not work with nested properties. You will have to move the High and Low properties directly to the HeartRateZone class. I have modified your project to demonstrate how you can do this.

    I hope this helps. Please, let us know if you have further questions.

    Regards,
    Rosy Topchiyska
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  3. DevCraft banner
  4. Alex
    Alex avatar
    14 posts
    Member since:
    Nov 2013

    Posted 24 Aug 2014 in reply to Rosy Topchiyska Link to this post

    Rosy, 
    Thanks for your response, though I have to say that it is not what I hoped for. This is a non-starter for me. I spent a lot of time and effort in isolating the Range functionality into a separate class and also built several User Controls that take range as the input, now I have to break this encapsulation, which I am not willing to do.
    BTW, Syncfusion's HiLo Chart supports this behavior out of the box - something for your dev team to consider.
    Thanks
    Alex.
  5. Ivaylo Gergov
    Admin
    Ivaylo Gergov avatar
    661 posts

    Posted 26 Aug 2014 Link to this post

    Hello Alex,

    Thank you for your feedback.

    I reviewed your scenario and I would like to suggest how to achieve this. As the PropertyNameDataPointBinding class is public, you can create a class which derives from it and override its GetValue(object instance) method. In this method you can simply assign the Range value to the instance object and execute the base method. Here's an example:
    public class RangeDataPointBinding : PropertyNameDataPointBinding
    {
        public override object GetValue(object instance)
        {
            instance = (instance as HeartRateZone).Range;
     
            return base.GetValue(instance);
        }
    }

    For your convenience, I have also attached a sample project to demonstrate this approach.

    Please, let me know if this suits your needs.

    Regards,
    Ivaylo Gergov
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  6. Alex
    Alex avatar
    14 posts
    Member since:
    Nov 2013

    Posted 27 Aug 2014 in reply to Ivaylo Gergov Link to this post

    Ivaylo, thank you so much for your suggestion - I am almost up and running.

    After implementing the approach you provided, I am now facing a similar but different issue, where I need to reference a nested class inside my PointTemplate (see below: trying to bind Text="{Binding Path=HeartRateRange.UpperRange}"
    Is there a way to use a nested class inside a PointTemplate?
    Thanks 
    Alex.

    <telerikChart:RangeBarSeries.PointTemplate>
                        <DataTemplate>
                            <Border
                                BorderBrush="{Binding IsSelected, Converter={StaticResource BoolToAccentColorConverter}}"
                                BorderThickness="2">
                                <Grid Background="{StaticResource PhoneForegroundBrush}">
                                    <Canvas VerticalAlignment="Top">
                                        <TextBlock
                                                Text="{Binding Path=HeartRateRange.Value.UpperRange, Mode=TwoWay}"
                                                Canvas.Left="10"
                                                Canvas.Top="-28"
                                                Foreground="{StaticResource AnnotationsPointForeground}"/>
                                    </Canvas>
                                    <Canvas VerticalAlignment="Bottom">
                                        <TextBlock
                                                Text="{Binding Path=HeartRateRange.Value.LowerRange, Mode=TwoWay}"
                                                Canvas.Left="7"/>
                                    </Canvas>
                                </Grid>
                            </Border>
                        </DataTemplate>
  7. Answer
    Ves
    Admin
    Ves avatar
    2879 posts

    Posted 01 Sep 2014 Link to this post

    Hi Alex,

    You can do this. The DataContext would be the DataPoint object. It contains the information needed to display the corresponding item. In this case it is a RangeDataPoint object. It holds High and Low values as well as a reference to the original data item, through the DataItem property. So, in the path you would need to start with DataItem and then add the path. E.g. if the series items source is a collection of HeartRateRange objects, then your binding might look like this:

    <TextBlock
        Text="{Binding Path=DataItem.HeartRateRange.Value.UpperRange}"
        Canvas.Left="10"
        Canvas.Top="-28"
        Foreground="{StaticResource AnnotationsPointForeground}"/>

    As a side note -- it seems you might get rid of that Mode=TwoWay.

    Best regards,
    Ves
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  8. Alex
    Alex avatar
    14 posts
    Member since:
    Nov 2013

    Posted 07 Sep 2014 in reply to Ves Link to this post

    Thanks so much for your help. This is exactly what I was looking for.
Back to Top
DevCraft banner