views:

2844

answers:

2

Hi,

I am working on a silverlight application that pulls in objects from a web service and dynamically populates a chart (lineseries) based on object attributes. The problem is that I have too many datapoints (dots) on the lineseries and I was wondering if there is a way to remove them.

I used Setter to collapse the visibility of the datapoints but I loose the automatic tooltip (attribute value) I got when the datapoints were visible.. Is there a way to regain the tooltip without seeing the datapoints.

Thanks Ron

PS: the XAML just defines the chart, everything else is done in cs

A: 

You can either style the data points so that they aren't as large and in the way so that you can still have the tooltips for all the data points, or you can filter down your data points to a smaller collection and not have tooltips (because you got rid of the data points) - you can't really have it both ways.. maybe you should try a line style graph instead?

SmartyP
Thanks... but the options 2 and 3 are nt really possible. I am relatively new to silverlight and was wondering if there was a way to modify the LineDataPoint template to make the data point transparent (if possible). It might be a longshot but, if this is possible how to I make sure that my code behind uses the template defined in page.xaml??
+1  A: 

I had this exact problem a few months back.

For me, outliners were initially not important. So I decided to go ahead and use LINQ to pare down the list. It should be easy enough to provide a .Where(...) clause, or use lambdas, to select a subset of the list - every N entries, or every-other entry in the list.

yourSeries.ItemsSource = blah.Where(x => x... /* insert expressions here to filter a little */)

If outliners are important, you may need to write a simple algorithm that filters your list's view down somewhat.

Here's some simple code (not pretty however) that I had to write for a graph. Sorry for not simplifying the code much, comment if you need detail, or the data object.

I was measuring the size of control assemblies over time, and correlating that with build dates/times/and check-ins.

For this app, I wanted to show changes in the data, as I had many redundant points. Those were the inflection points of interest.

Imagine having 800 builds, and related data - but really only say 300 interesting data points. That's a lot less data, and improves the display.

I explain the code a little below.

        private void ParseData(string xml)
        {
        XDocument data = XDocument.Parse(xml);
        _data = new Dictionary<string, List<ControlAssembly>>();

        foreach (XElement dataSet in data.Descendants("data"))
        {
            string set = dataSet.Attribute("set").Value;
            long lastSize = 0;
            int matchingSizeCount = 0;
            foreach (XElement build in dataSet.Descendants("build"))
            {
                ControlAssembly ca = ControlAssembly.Parse(build);
                if (ca != null)
                {
                    List<ControlAssembly> list;

                    if (!_data.TryGetValue(set, out list))
                    {
                        _data[set] = new List<ControlAssembly>();
                        list = _data[set];
                    }

                    bool add = true;
                    if (ca.SizeInKilobytes == lastSize)
                    {
                        matchingSizeCount++;
                        if (matchingSizeCount > 1 && !ca.IsKnownReleaseWeek)
                        {
                            // cut down on the displayed data points
                            add = false;
                        }
                    }
                    else
                    {
                        matchingSizeCount = 0;
                        lastSize = ca.SizeInKilobytes;
                    }

                    if (add)
                    {
                        _data[set].Add(ca);
                    }
                }
            }
        }
    }

The _data is my set of data for assemblies, which I eventually use to setup the series:

        ParseData(SampleData.LargeDataSet);
        _xapSeries = new Dictionary<string, LineSeries>();
        foreach (string assembly in _data.Keys)
        {
            LineSeries series = new LineSeries();
            series.Title = assembly.Replace(".dll", "");
            series.IndependentValueBinding = new Binding("BuildDateTime");
            series.DependentValueBinding = new Binding("CompressedSize");
            series.MarkerHeight = 12;
            series.MarkerWidth = 12;
            series.ItemsSource = _data[assembly].ToList();
            _xapSeries[assembly] = series;

            if (assembly != "Total")
            {
                CompressedSizes.Series.Add(series);
            }
        }

And, finally, if you do want to customize DataPoint templates, it can be done, but it is not trivial in my opinion.

You have a lot to do, such as

  • Defining the proper type of point (I'm using LineDataPoints here)
  • Setup the style palette to select from your point styles, which include that custom template

This is a positive if you need to do a lot of customizing. You could, for instance, make the entire data point transparent.

Here is a custom template (sorry, very verbose) for a LineDataPoint (Silverlight Toolkit chart controls) that has a custom ToolTip binding, a bound color for the point, and other properties that relate back to the same data point from the filter code above.

I removed the visual states from this XAML to clean it up

        <ControlTemplate x:Key="CustomLineDataPointTemplate" TargetType="charting:LineDataPoint">
        <Grid x:Name="Root" Opacity="0" ToolTipService.ToolTip="{Binding DataPointTooltipText}">
            <Ellipse Opacity="0.4" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}"/>
            <Ellipse Opacity="0.4" RenderTransformOrigin="0.661,0.321">
                <Ellipse.Fill>
                    <RadialGradientBrush GradientOrigin="0.681,0.308">
                        <GradientStop Color="#00FFFFFF"/>
                        <GradientStop Color="#FF3D3A3A" Offset="1"/>
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse StrokeThickness="2" Stroke="{Binding DataPointBrush}" />
            <Ellipse x:Name="SelectionHighlight" Opacity="0" Fill="Red"/>
            <Ellipse x:Name="MouseOverHighlight" Opacity="0" Fill="White"/>
        </Grid>
    </ControlTemplate>

The custom style palette that will use these:

        <datavis:StylePalette x:Key="MyCustomStylePalette">
        <!--Blue-->
        <Style TargetType="charting:LineDataPoint">
            <Setter Property="Background"><Setter.Value><RadialGradientBrush><RadialGradientBrush.RelativeTransform><TransformGroup><ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2.09" ScaleY="1.819"/><TranslateTransform X="-0.425" Y="-0.486"/></TransformGroup></RadialGradientBrush.RelativeTransform><GradientStop Color="#FFB9D6F7"/><GradientStop Color="#FF284B70" Offset="1"/></RadialGradientBrush></Setter.Value></Setter>
            <Setter Property="Template" Value="{StaticResource CustomLineDataPointTemplate}" />
        </Style>
        <!--Red-->
        <Style TargetType="charting:LineDataPoint">
            <Setter Property="Background"><Setter.Value><RadialGradientBrush><RadialGradientBrush.RelativeTransform><TransformGroup><ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2.09" ScaleY="1.819"/><TranslateTransform X="-0.425" Y="-0.486"/></TransformGroup></RadialGradientBrush.RelativeTransform><GradientStop Color="#FFFBB7B5"/><GradientStop Color="#FF702828" Offset="1"/></RadialGradientBrush></Setter.Value></Setter>
            <Setter Property="Template" Value="{StaticResource CustomLineDataPointTemplate}" />
        </Style>
        <!-- Light Green -->
        <Style TargetType="Control">
            <Setter Property="Background"><Setter.Value><RadialGradientBrush><RadialGradientBrush.RelativeTransform><TransformGroup><ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2.09" ScaleY="1.819"/><TranslateTransform X="-0.425" Y="-0.486"/></TransformGroup></RadialGradientBrush.RelativeTransform><GradientStop Color="#FFB8C0AC"/><GradientStop Color="#FF5F7143" Offset="1"/></RadialGradientBrush></Setter.Value></Setter>
            <Setter Property="Template" Value="{StaticResource CustomLineDataPointTemplate}" />
        </Style>
        <!-- Yellow -->
        <Style TargetType="Control">
            <Setter Property="Background"><Setter.Value><RadialGradientBrush><RadialGradientBrush.RelativeTransform><TransformGroup><ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2.09" ScaleY="1.819"/><TranslateTransform X="-0.425" Y="-0.486"/></TransformGroup></RadialGradientBrush.RelativeTransform><GradientStop Color="#FFFDE79C"/><GradientStop Color="#FFF6BC0C" Offset="1"/></RadialGradientBrush></Setter.Value></Setter>
            <Setter Property="Template" Value="{StaticResource CustomLineDataPointTemplate}" />
        </Style>
        <!-- Indigo -->
        <Style TargetType="Control">
            <Setter Property="Background"><Setter.Value><RadialGradientBrush><RadialGradientBrush.RelativeTransform><TransformGroup><ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="2.09" ScaleY="1.819"/><TranslateTransform X="-0.425" Y="-0.486"/></TransformGroup></RadialGradientBrush.RelativeTransform><GradientStop Color="#FFA9A3BD"/><GradientStop Color="#FF382C6C" Offset="1"/></RadialGradientBrush></Setter.Value></Setter>
            <Setter Property="Template" Value="{StaticResource CustomLineDataPointTemplate}" />
        </Style>
    </datavis:StylePalette>

And the XAML that binds the style palette:

            <charting:Chart

            Title="Compressed control sizes over time" 
             StylePalette="{StaticResource MyCustomStylePalette}"

             x:Name="CompressedSizes" />

Hope this helps.

Jeff Wilcox