As I mentioned in my introduction to this series, I’m going over various aspects of how I implemented a control that allows a user to choose a DateTime range using a double slider control. In Part One, I covered the basics of what I did to the control templates, put the slider together and showed the IValueConverters I used to handle the conversions from doubles to DateTime and doubles to TimeSpans. In this article, I’ll be talking about what I did to keep the upper value and the lower value sliders from crossing each other. The source for the control and a sample project can be found at CodePlex
My first thought doing this was to play it simple and bind the lower slider’s Maximum to the upper slider’s current value and the and the upper sliders Minimum to the lower’s current value. While this seemed logical to me, when I implemented it, it put the two sliders on two totally separate scales and it failed miserably. Not only were the sliders able to cross, since the scales were constantly changed, while I would move the lower slider, the upper slider would be moving (and not changing value).
My second attempt was much better, but still didn’t work as expected. In this attempt, I subscribed to the ValueChanged event for each of the sliders. In these event handlers, I had a piece of code as shown in “Attempt 2: Fail” that was setting the slider controls values so they wouldn’t cross each other (for long) and then setting dependency properties (UpperValue and LowerValue) to those upper and lower values. This failed because I wasn’t setting the upper slider’s value to something greater than the lower slider’s value until after this code ran. To a user, this would be acceptable, you can’t actually see the values cross. However, I was using this to help zoom in and out of a charting control and once they crossed, even for a few milliseconds, the charting control would blow up on me. It was quite messy…line fragments, points, and data everywhere.
- private void LowerSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
- {
- UpperSlider.Value = Math.Max(UpperSlider.Value, LowerSlider.Value + oneSecond);
- LowerValue = new DateTime((long)LowerSlider.Value);
- UpperValue = new DateTime((long)UpperSlider.Value);
- }
So, finally, my 3rd attempt I hit gold. Why was I setting LowerValue and UpperValue be set by the sliders when I was also setting them in the ValueChanged events? So I changed my bindings in the sliders to OneWay bindings so I have complete control of the range values and the sliders can suggest was my new UpperValue and LowerValue should be, but I get the final decision in my event handlers. In my event handlers, I check if the slider values have passed each other and set the dependency properties to appropriate values that make sure that the UpperValue is greater than the LowerValue.
- private void UpperSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
- {
- LowerSlider.Value = Math.Max(Math.Min(UpperSlider.Value – oneSecond, LowerSlider.Value), Minimum.Ticks);
- var _upperValue = new DateTime((long)UpperSlider.Value);
- var _lowerValue = new DateTime((long)LowerSlider.Value);
- if (UpperValue > _lowerValue)
- {
- LowerValue = _lowerValue;
- UpperValue = _upperValue;
- }
- else
- {
- UpperValue = _upperValue;
- LowerValue = _lowerValue;
- }
- }
Pingback: Windows Client Developer Roundup 057 for 1/31/2011 - Pete Brown's 10rem.net
Pingback: Windows Client Developer Roundup 057 for 1/31/2011 · All About Computer
Thank you for the information, this article is very helpful at all