Custom scatter plot - PointTemplate

2 posts, 0 answers
  1. EM
    EM avatar
    21 posts
    Member since:
    Oct 2006

    Posted 07 Jun 2013 Link to this post

    After reviewing the documentation, specifically http://www.telerik.com/help/silverlight/radchartview-styles-and-templates-customizing-scatter-points.html, I am exploring ways to scale a custom scatter point based on values of the underlying data.

    I know there is an old post in this forum relating to bubble plots in RadChartView (where the bubble is sized based on the underlying data) which is not currently supported.  This was 2012.

     
    Looking at the source code for the Candlestick object (as part of the Candlestick series) as a reference, clearly the Candlestick uses sizing (scale) information to determine how big it should be based on the underlying data object.  In particular, it uses the layout and size information in the LayoutSlot to draw itself accordingly which is exactly what I need to do.  So, clearly the Candlestick has the mechanisms to do this internally (and in fact, it would be very nice if the classes were not declared Internal so they could easily be inherited and the issue would be easily solved for me just using a custom Candlestick object to draw what I want.

    The scatter point template doesn't seem to get that information (the layout height and width is 0, only the X, Y coordinates are valid).

    What I'm trying to do here is to write a custom point template which, using the load event on the template, can get scale information.  I do this in code-behind instead of defining the template in XAML so bear with me.  The template simply is a grid object with a handler attached to its loaded event, which causes the handler to be called whenever the chart renders the custom point template the first time the series is rendered.  In the loaded event of the grid, we simply create content based on the underlying data point.  This is very similar to the concept in the documentation where color and shape is changed.  What I want to do however is SIZE the object based on the underlying data.  Sizing is trivial, however, the sizing should match the current scale of the chart so the dimensions match the axis data, so I need a way to scale the axis information to the pixel.   ChartView does provide a conversion API (http://www.telerik.com/help/silverlight/radchartview-features-conversion.html) but I haven't found a good way to hook that in.  


    What I have to date is this:

    public static DataTemplate CreatePointTemplate(
             [NotNull]RoutedEventHandler pointLoadedHandler,
             string selectedBooleanField = null,
             string notSelectedBooleanField = null
             )
         {
      
             var template = new DataTemplate();
             var gridFactory = new FrameworkElementFactory(typeof(Grid));
                gridFactory.AddHandler(Control.LoadedEvent, pointLoadedHandler);
                
             template.VisualTree = gridFactory;
      
             return template;
      
         }

    The handler code is:

    private void HandlePointCreated(object sender, RoutedEventArgs e)
           {
               var grid = (Grid)sender;
               var scatterDataPoint = (ScatterDataPoint)((FrameworkElement)sender).DataContext;
               var data = (HistoricalMarketDataPoint)scatterDataPoint.DataItem;
     
               var layoutSlot = scatterDataPoint.LayoutSlot;
               var bubble = new Ellipse();
               var scale = !Equals(layoutSlot.Height, 0.0) ? data.PriceAverage/layoutSlot.Height : 1.0;
               var scaleSize = new Size(scale*layoutSlot.Width, scale*layoutSlot.Height);
     
               bubble.Width = scaleSize.Width;
               bubble.Height = scaleSize.Height;
                
               var chartPointBrush = new SolidColorBrush(Colors.Red);
               bubble.Fill = chartPointBrush;
               grid.Children.Add(bubble);
     
           }

    The layout slot width and height are always 0 for a scatter point so I can't derive the scale from the layout box.   This is not true of the Candlestick object where it uses the layout information to size itself.

    My custom point will be rendered at the X and Y locations correctly on the chart, what I'm missing is the ratio of pixel/value for the chart to allow me to compute the scale of the object so it matches the axes.   

    For simplicity, I'm using linear axes on both X and Y.

    So what I'd like to do is to compute the scale value in the handler code above.   I can easily get a reference to the chart object in the handler if needed, but I have no idea where to fish the plot scale data from right now.

    Alternatively, I can use a CandlestickSeries instead of a ScatterSeries and accomplish the same thing but changing the rendering of the candlestick so what I need to make it do.  I haven't found of a way to do that via templates as the relevant overrides are all marked "internal".

    Thanks!

    E.


  2. EM
    EM avatar
    21 posts
    Member since:
    Oct 2006

    Posted 07 Jun 2013 Link to this post

    Well, as always when I post something, I discover in the next 30 minutes exactly what I'm looking for...
    Here's the current thought with a working example:

    The CandlestickSeries does have a point template member, and I was able to use the above technique to get the information I needed.

    The point template looks like this:

    <DataTemplate x:Key="PointTemplate">
         <Grid Loaded="HandlePointLoaded"/>
    </DataTemplate>

    The series definition:

    <telerik:RadCartesianChart x:Name="Chart" >
               <telerik:RadCartesianChart.HorizontalAxis  >
                   <telerik:CategoricalAxis
                       x:Name="HorizontalAxis"
                       LabelFitMode="Rotate"
                       Title="Date" >
                       <telerik:CategoricalAxis.LabelTemplate>
                           <DataTemplate>
                               <StackPanel>
                                   <TextBlock Text="{Binding Converter={StaticResource OaDateConverter}}" />
                               </StackPanel>
                           </DataTemplate>
                       </telerik:CategoricalAxis.LabelTemplate>
     
                   </telerik:CategoricalAxis>
               </telerik:RadCartesianChart.HorizontalAxis>
     
               <telerik:RadCartesianChart.VerticalAxis>
                   <telerik:LinearAxis Title="Price">
                       <telerik:LinearAxis.LabelTemplate>
                           <DataTemplate>
                               <TextBlock Text="{Binding Converter={StaticResource CostConverter}}" />
                           </DataTemplate>
                       </telerik:LinearAxis.LabelTemplate>
                   </telerik:LinearAxis>
               </telerik:RadCartesianChart.VerticalAxis>
     
     
               <telerik:RadCartesianChart.Series>
     
     
     
     
                   <telerik:CandlestickSeries
                 ItemsSource="{Binding TestSellSeries}"
                 CategoryBinding="XValue"
                 HighBinding="PriceHigh"
                 LowBinding="PriceLow"
                 CloseBinding="PriceAverage"
                 OpenBinding="PriceAverage"
                 PointTemplate="{StaticResource PointTemplate}"
               />
     
     
     
               </telerik:RadCartesianChart.Series>
           </telerik:RadCartesianChart>

    And finally the handler:

    private void HandlePointLoaded(object sender, RoutedEventArgs e)
           {
               var grid = (Grid)sender;
               var scatterDataPoint = (OhlcDataPoint)((FrameworkElement)sender).DataContext;
               var data = (HistoricalMarketDataPoint)scatterDataPoint.DataItem;
     
               var layoutSlot = scatterDataPoint.LayoutSlot;
     
               var bubble = new Ellipse();
               var scale = !Equals(layoutSlot.Height, 0.0) ? data.PriceAverage / layoutSlot.Width : 1.0;
               var scaleSize = new Size(scale * layoutSlot.Width, scale * layoutSlot.Height);
     
               bubble.Width = scaleSize.Width;
               bubble.Height = scaleSize.Height;
     
               var chartPointBrush = new SolidColorBrush(Colors.Red);
               bubble.Fill = chartPointBrush;
               grid.Children.Add(bubble);
           }


    I need to correctly scale the object but at least the layout information has size information and a base to work from in pixels.

    E.
  3. UI for WPF is Visual Studio 2017 Ready
Back to Top