通用 Windows (UWP) 范围滑块

Posted

技术标签:

【中文标题】通用 Windows (UWP) 范围滑块【英文标题】:Universal Windows (UWP) Range Slider 【发布时间】:2016-08-01 10:47:57 【问题描述】:

我想在 UWP 中创建 范围滑块。我没有找到任何例子。只有单个滑块,但我希望它像 。

有人知道我该怎么做吗?请帮帮我。

【问题讨论】:

【参考方案1】:

要在 UWP 中创建范围滑块,我们可以创建自定义控件或使用UserControl。这里我以UserControl为例:

首先,我在我的项目中添加一个名为“MyRangeSlider”的 UserControl。

在 XAML 中:

<UserControl x:Class="UWP.MyRangeSlider"
             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:local="using:UWP"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             d:DesignHeight="300"
             d:DesignWidth="400"
             mc:Ignorable="d">

    <Grid Height="32" Margin="8,0">
        <Grid.Resources>
            <Style TargetType="Thumb">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Thumb">
                            <Ellipse Width="32"
                                     Height="32"
                                     Fill="White"
                                     RenderTransformOrigin="0.5 0.5"
                                     Stroke="Gray"
                                     StrokeThickness="1">
                                <Ellipse.RenderTransform>
                                    <TranslateTransform X="-16" />
                                </Ellipse.RenderTransform>
                            </Ellipse>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Grid.Resources>
        <Rectangle Height="16"
                   Margin="8,0"
                   Fill="#FFD5D5D5"
                   RadiusX="5"
                   RadiusY="5" />
        <Canvas x:Name="ContainerCanvas" Margin="8,0" SizeChanged="ContainerCanvas_SizeChanged">
            <Thumb x:Name="MinThumb" DragCompleted="MinThumb_DragCompleted" DragDelta="MinThumb_DragDelta" />
            <Thumb x:Name="MaxThumb" DragCompleted="MaxThumb_DragCompleted" DragDelta="MaxThumb_DragDelta" />
            <Rectangle x:Name="ActiveRectangle"
                       Canvas.Top="8"
                       Height="16"
                       Canvas.ZIndex="-1"
                       Fill="#FF69A0CC" />
        </Canvas>
    </Grid>
</UserControl>

在其代码隐藏中:

public sealed partial class MyRangeSlider : UserControl

    public double Minimum
    
        get  return (double)GetValue(MinimumProperty); 
        set  SetValue(MinimumProperty, value); 
    

    public double Maximum
    
        get  return (double)GetValue(MaximumProperty); 
        set  SetValue(MaximumProperty, value); 
    

    public double RangeMin
    
        get  return (double)GetValue(RangeMinProperty); 
        set  SetValue(RangeMinProperty, value); 
    

    public double RangeMax
    
        get  return (double)GetValue(RangeMaxProperty); 
        set  SetValue(RangeMaxProperty, value); 
    

    public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(MyRangeSlider), new PropertyMetadata(0.0));

    public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(MyRangeSlider), new PropertyMetadata(1.0));

    public static readonly DependencyProperty RangeMinProperty = DependencyProperty.Register("RangeMin", typeof(double), typeof(MyRangeSlider), new PropertyMetadata(0.0, OnRangeMinPropertyChanged));

    public static readonly DependencyProperty RangeMaxProperty = DependencyProperty.Register("RangeMax", typeof(double), typeof(MyRangeSlider), new PropertyMetadata(1.0, OnRangeMaxPropertyChanged));

    public MyRangeSlider()
    
        this.InitializeComponent();
    

    private static void OnRangeMinPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        var slider = (MyRangeSlider)d;
        var newValue = (double)e.NewValue;

        if (newValue < slider.Minimum)
        
            slider.RangeMin = slider.Minimum;
        
        else if (newValue > slider.Maximum)
        
            slider.RangeMin = slider.Maximum;
        
        else
        
            slider.RangeMin = newValue;
        

        if (slider.RangeMin > slider.RangeMax)
        
            slider.RangeMax = slider.RangeMin;
        

        slider.UpdateMinThumb(slider.RangeMin);
    

    private static void OnRangeMaxPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        var slider = (MyRangeSlider)d;
        var newValue = (double)e.NewValue;

        if (newValue < slider.Minimum)
        
            slider.RangeMax = slider.Minimum;
        
        else if (newValue > slider.Maximum)
        
            slider.RangeMax = slider.Maximum;
        
        else
        
            slider.RangeMax = newValue;
        

        if (slider.RangeMax < slider.RangeMin)
        
            slider.RangeMin = slider.RangeMax;
        

        slider.UpdateMaxThumb(slider.RangeMax);
    

    public void UpdateMinThumb(double min, bool update = false)
    
        if (ContainerCanvas != null)
        
            if (update || !MinThumb.IsDragging)
            
                var relativeLeft = ((min - Minimum) / (Maximum - Minimum)) * ContainerCanvas.ActualWidth;

                Canvas.SetLeft(MinThumb, relativeLeft);
                Canvas.SetLeft(ActiveRectangle, relativeLeft);

                ActiveRectangle.Width = (RangeMax - min) / (Maximum - Minimum) * ContainerCanvas.ActualWidth;
            
        
    

    public void UpdateMaxThumb(double max, bool update = false)
    
        if (ContainerCanvas != null)
        
            if (update || !MaxThumb.IsDragging)
            
                var relativeRight = (max - Minimum) / (Maximum - Minimum) * ContainerCanvas.ActualWidth;

                Canvas.SetLeft(MaxThumb, relativeRight);

                ActiveRectangle.Width = (max - RangeMin) / (Maximum - Minimum) * ContainerCanvas.ActualWidth;
            
        
    

    private void ContainerCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
    
        var relativeLeft = ((RangeMin - Minimum) / (Maximum - Minimum)) * ContainerCanvas.ActualWidth;
        var relativeRight = (RangeMax - Minimum) / (Maximum - Minimum) * ContainerCanvas.ActualWidth;

        Canvas.SetLeft(MinThumb, relativeLeft);
        Canvas.SetLeft(ActiveRectangle, relativeLeft);
        Canvas.SetLeft(MaxThumb, relativeRight);

        ActiveRectangle.Width = (RangeMax - RangeMin) / (Maximum - Minimum) * ContainerCanvas.ActualWidth;
    

    private void MinThumb_DragDelta(object sender, DragDeltaEventArgs e)
    
        var min = DragThumb(MinThumb, 0, Canvas.GetLeft(MaxThumb), e.HorizontalChange);
        UpdateMinThumb(min, true);
        RangeMin = Math.Round(min);
    

    private void MaxThumb_DragDelta(object sender, DragDeltaEventArgs e)
    
        var max = DragThumb(MaxThumb, Canvas.GetLeft(MinThumb), ContainerCanvas.ActualWidth, e.HorizontalChange);
        UpdateMaxThumb(max, true);
        RangeMax = Math.Round(max);
    

    private double DragThumb(Thumb thumb, double min, double max, double offset)
    
        var currentPos = Canvas.GetLeft(thumb);
        var nextPos = currentPos + offset;

        nextPos = Math.Max(min, nextPos);
        nextPos = Math.Min(max, nextPos);

        return (Minimum + (nextPos / ContainerCanvas.ActualWidth) * (Maximum - Minimum));
    

    private void MinThumb_DragCompleted(object sender, DragCompletedEventArgs e)
    
        UpdateMinThumb(RangeMin);
        Canvas.SetZIndex(MinThumb, 10);
        Canvas.SetZIndex(MaxThumb, 0);
    

    private void MaxThumb_DragCompleted(object sender, DragCompletedEventArgs e)
    
        UpdateMaxThumb(RangeMax);
        Canvas.SetZIndex(MinThumb, 0);
        Canvas.SetZIndex(MaxThumb, 10);
    

然后我可以使用MyRangeSlider,如下所示:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="auto" />
    </Grid.ColumnDefinitions>
    <TextBox HorizontalAlignment="Center" FontSize="20" Text="Binding RangeMin, ElementName=RangeSlider, Mode=TwoWay" />
    <local:MyRangeSlider x:Name="RangeSlider"
                         Grid.Column="1"
                         Maximum="100"
                         Minimum="0"
                         RangeMax="80"
                         RangeMin="20" />
    <TextBox Grid.Column="2"
             HorizontalAlignment="Center"
             FontSize="20"
             Text="Binding RangeMax,
                            ElementName=RangeSlider,
                            Mode=TwoWay" />
</Grid>

它看起来像: 这是一个简单的示例,您可以对其进行编辑以满足您的要求。而如果要创建自定义控件,可以参考Building a custom control using XAML and C#。虽然这篇文章是针对Windows 8 XAML平台的,但是UWP也是一样的。

【讨论】:

以上是关于通用 Windows (UWP) 范围滑块的主要内容,如果未能解决你的问题,请参考以下文章

制作 | UWP :: 使用 Cmake 构建通用 Windows 应用程序

详解 UWP (通用 Windows 平台) 中的两种 HttpClient API

如何为通用 Windows 平台(UWP)应用程序创建 .appx 包?

如何使用通用 Windows 平台 (UWP) 应用程序执行进程命令(或类似命令)?

如何在 Windows 10 UWP 中正确实现语音识别

在Windows 10 C#UWP通用Windows应用程序中获取UserName