views:

71

answers:

2

I have a simple silverlight toolkit Chart and a standard Slider in the same control. I want the Chart to be updated whenever the slider value changes. This oughta be simple. I tried binding to the .ValueChanged event on the slider but this appears to fire far too often (eg: multiple times while the slider is still in motion). I'm only interested in the value of the slider when it has come to a stop. The performance is terrible if I update as often as the event is fired. So I added a couple of handlers to the .MouseLeftButtonUp and .MouseLeftButtonDown events that should lock or unlock the logic for updating the chart. Unfortunately .MouseLeftButtonDown fails to fire (breakpoints within the handler don't get hit). So that route appears to be a non-starter as well.

I'm quite new to silverlight so I'm looking for constructive criticism of the approach as well as possible solutions. Cheers!

MainPage.xaml.cs:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Input;

namespace ControlledModelView.Media
{
    public partial class MainPage : UserControl
    {
        readonly Dictionary<KeyValuePair<int, int>, Dictionary<string, double>> data = new Dictionary<KeyValuePair<int, int>, Dictionary<string, double>>();
        readonly Chart chart = new Chart { VerticalAlignment = VerticalAlignment.Top };
        private static bool sliderInMotion = true;
        public MainPage()
        {
            InitializeComponent();
            var rng = new Random();
            for (var i = 0; i < 4; i++)
                for (var d = -1; d >= -31; d--)
                    data.Add(new KeyValuePair<int, int>(i, d), GetRandomData(rng));

            var slider = new Slider { LargeChange = 7, SmallChange = 1, Minimum = -31, Maximum = -1, Value = -1, VerticalAlignment = VerticalAlignment.Bottom };
            slider.ValueChanged += SliderValueChanged;
            slider.MouseLeftButtonUp += SliderRelease;
            slider.MouseLeftButtonDown += SliderLock;
            DrawLineChart(-1);
            LayoutRoot.Children.Add(chart);
            LayoutRoot.Children.Add(slider);
        }

        static void SliderLock(object sender, MouseButtonEventArgs e)
        {
            sliderInMotion = true;
        }

        static void SliderRelease(object sender, MouseButtonEventArgs e)
        {
            sliderInMotion = false;
        }

        void SliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if(!sliderInMotion)
                DrawLineChart((int)e.NewValue);
        }

        private void DrawLineChart(int day)
        {
            chart.Series.Clear();
            for (var seriesIndex = 0; seriesIndex < 4; seriesIndex++)
            {
                chart.Series.Add(new LineSeries
                {
                    ItemsSource = data[new KeyValuePair<int, int>(seriesIndex, day)],
                    DependentValuePath = "Value",
                    IndependentValuePath = "Key",
                    AnimationSequence = AnimationSequence.Simultaneous,
                    Name = "Line" + seriesIndex,
                    Title = "Line" + seriesIndex,
                    IsSelectionEnabled = false,
                    Visibility = Visibility.Visible,
                    IsEnabled = true
                });
            }
        }

        static Dictionary<string, double> GetRandomData(Random rng)
        {
            var data = new Dictionary<string, double>();
            for (var year = 1995; year < DateTime.Now.Year; year ++)
                data.Add(year.ToString(), 500000 * rng.NextDouble());
            return data;
        }
    }
}

MainPage.xaml:

<UserControl x:Class="ControlledModelView.Media.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot" />
</UserControl>
+2  A: 

This seems to be a prime example for the RX framework (IObservable), if you are able to use it. One of the standard samples lists filtering events, by coordinates (for MouseClick events in a region of interest). You could use that exactly for your sample.

See this question for some details. Get it here.

Benjamin Podszun
Cool, having a read now....
grenade
Rx can also throttle events so they can only fire, say, once every 150 ms. This saved the day on one of my projects.
Daniel Straight
+1  A: 

grenade,

I'm not a silverlight developer, and although there may be better ways to do what you're trying, that looks a perfectly acceptable solution (if it worked). It's quite clear and obvious what it's attempting to do.

Seeing as it doesn't work, my immediate reaction is to see if their is a LostFocus event that might work instead, although that doesn't quite get you where you want, as they then have to de-select the control. That thought process however makes me wondering if a different control might work better? e.g. a numeric up down where the user can type the number they wish directly, most aren't likely to keep clicking up so you'd get less updates.

But that's taking the whole "if you can't fix the problem, change the problem" route... sorta.

Ian