WPF: How to set dynamic range of numbers for vertical slider?

I am currently working with an MVVM WPF project that has a custom control used by multiple views but with a different range of values. Here's an example of what I need.

valuesenter image description hereenter image description here

As you can see, the control should react to different behavior depending on the values ​​I need to show in the slider (regardless of the number, this is just an example). One problem is that the project is designed using the MVVM design pattern, so "code behind" shouldn't be a good option (but if it solves the problem, that's okay for me), so I think it might be a bit harder to do it's in XAML.

The pictures I showed earlier did this in a static way by creating a grid next to the slider with a few text boxes, but it is impossible to hold onto this solution because I have to create a new one whenever I want. I decided to run the solution using a StackPanel or DockPanel with a height set ... but if you think this is not a very good option, if you want to show two values, because you need to specify the vertical alignment for each of these values, and it sounds not very good.

The only way I think is to do an override or something that allows the slider to show Ticks based on the slider's height ... is there a way to do something like this?

I just wrote a few lines of code, but nothing really matters because I don't know how to solve this problem. I don't need code, I need ideas.

+1


source to share


1 answer


We work together, so I'll leave the solution we found for this problem in case anyone is interested.

First, we created 2 classes.

1) A class that inherits from SLIDER with 2 dependency properties for the intermediate range (green numbers in the image) as follows:

class NumberedSlider : System.Windows.Controls.Slider
    {
        public static readonly DependencyProperty RangeMinProperty = DependencyProperty.Register(
                 "RangeMinSlider", typeof(int), typeof(NumberedTickBar), new FrameworkPropertyMetadata(0));

        public int RangeMin
        {
            get { return (int)base.GetValue(RangeMinProperty); }
            set { base.SetValue(RangeMinProperty, value); }
        }

        public static readonly DependencyProperty RangeMaxProperty = DependencyProperty.Register(
                  "RangeMaxSlider", typeof(int), typeof(NumberedTickBar), new FrameworkPropertyMetadata(0));

        public int RangeMax
        {
            get { return (int)base.GetValue(RangeMaxProperty); }
            set { base.SetValue(RangeMaxProperty, value); }
        }
    }

      

2) a class for the TickBar slider that will display numbers:

public class NumberedTickBar : TickBar
    {
        public static readonly DependencyProperty RangeMinProperty = DependencyProperty.Register(
            "RangeMin", typeof (int), typeof (NumberedTickBar), new FrameworkPropertyMetadata(0));

        public int RangeMin
        {
            get { return (int) base.GetValue(RangeMinProperty); }
            set { base.SetValue(RangeMinProperty, value); }
        }

        public static readonly DependencyProperty RangeMaxProperty = DependencyProperty.Register(
            "RangeMax", typeof (int), typeof (NumberedTickBar), new FrameworkPropertyMetadata(0));

        public int RangeMax
        {
            get { return (int) base.GetValue(RangeMaxProperty); }
            set { base.SetValue(RangeMaxProperty, value); }
        }

        protected override void OnRender(DrawingContext dc)
        {
            Size size = new Size(base.ActualWidth, base.ActualHeight);

            int tickCount;

            if ((this.Maximum - this.Minimum)%this.TickFrequency == 0)
                tickCount = (int) ((this.Maximum - this.Minimum)/this.TickFrequency);
            else
                tickCount = (int) ((this.Maximum - this.Minimum)/this.TickFrequency) + 1;

            double tickFrequencySize = (size.Height*this.TickFrequency/(this.Maximum - this.Minimum));

            // Draw each tick text
            for (int i = 0; i <= tickCount; i++)
            {
                int value = Convert.ToInt32(this.Minimum + this.TickFrequency*i);

                string text = Convert.ToString(value, 10);
                FormattedText formattedText;
                if (value >= this.RangeMin && value <= this.RangeMax)
                    formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
                                                      FlowDirection.LeftToRight, new Typeface("Arial Rounded MT Bold"), 16,
                                                      Brushes.Green);
                else
                    formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
                                                      FlowDirection.LeftToRight, new Typeface("Arial Rounded MT Bold"), 16,
                                                      Brushes.DarkBlue);

                dc.DrawText(formattedText, new Point(30, (tickFrequencySize*(tickCount - i)-6)));
            }
        }
    }

      

We then replaced the tickbar on the vertical slider control template, as shown in the following example:



Vertical slider with ticker captions

but also adding two new dependency property values:

 <common:NumberedTickBar   Margin="0,0,0,10" x:Name="TopTick" 
                                      SnapsToDevicePixels="True" Placement="Left" 
                                      Fill="{StaticResource GlyphBrush}" Width="4" 
                                      RangeMax="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=RangeMax}"
                                      RangeMin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=RangeMin}"/>

      

Finally, the xaml of the custom control has code like this:

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50*"/>
            <ColumnDefinition Width="50*"/>
        </Grid.ColumnDefinitions>
        <Grid>
            <common:NumberedSlider x:Name="SliderValue" MinHeight="180" Margin="5 15 5 15" Orientation="Vertical" 
                                   Maximum="89" 
                                   Minimum="77" 
                                   Value="{Binding BarValue, Mode=TwoWay}"  
                                   TickFrequency="1" 
                                   IsSnapToTickEnabled="True" 
                                   RangeMin="82" 
                                   RangeMax="85"
                                   IsEnabled="{Binding SliderEnabled}"/>
        </Grid>
        <Grid Grid.Column="1">
            <ProgressBar Orientation="Vertical" Width="70" 
                         Minimum="{Binding ElementName=SliderValue,Path=Minimum}" 
                         Maximum="{Binding ElementName=SliderValue,Path=Maximum}" 
                         Value="{Binding ElementName=SliderValue, Path=Value}" Margin="0 14" Background="Gray" MinHeight="200"
                         Foreground="{Binding ColorBar, Converter= {StaticResource setSteelPointLevelConverter}}"                          />
        </Grid>
    </Grid>

      

Hope this approach can help anyone.

+1


source







All Articles