WPF中自定义双滑块Slider

本文介绍如何在WPF中创建一个自定义的MultiRangeSlider控件,该控件包含多个滑块,用于展示范围数据。通过使用StackPanel和Canvas容器,以及自定义Thumb样式,实现了滑块和矩形的交互。每个滑块对应一个矩形,矩形的宽度根据数据动态调整。此外,文章还展示了如何进行数据绑定和事件处理,以实现滑块的拖动和更新矩形宽度的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目中遇到需要双滑块的情况,可以网上的基本都是单滑块的样式和例子,但是双滑块的很少,后来终于在网上找到一个大神的帖子,通过修改和完善终于符合自己的需求,可后来再查看代码的时候好像又看不懂了,现在重新整理记录下来,以备后续查看。

WPF中的Slider,看他的模板那就很复杂,粗略的想MultiRangeSlider不就是几个滑块(Thumb),加几个矩形么,一个滑块对应两个矩形,滑块移动的时候,不就两边的矩形的宽度的变化么,矩形我们只想关注宽度变化,不想再去调整他的其实位置,用什么容器来装矩形呢,StackPanel,里面的对象总是首尾相连的嘛,可是要使Thumb能够水平移动,在StackPanel中显示不合适,那就放到Canvas中,然后把这两个容器使用Grid叠在一起,Canvas在上,就是下面这个样子

<Grid>
    <StackPanel Margin="15,0,15,0"
                x:Name="RangeContainer"
                Orientation="Horizontal">

    </StackPanel>
    <Canvas x:Name="ThumbContainer">
    </Canvas>
</Grid>

为了滑块好看,给滑块做个样式:

  <Style TargetType="local:ThumbEx">
            <Setter Property="Width" Value="30"></Setter>
            <Setter Property="Height" Value="100"></Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Thumb">
                        <Grid>
                            <StackPanel>
                                <Rectangle SnapsToDevicePixels="True"                           
                                           Height="55"
                                           RadiusX="1"
                                           RadiusY="1"
                                           StrokeThickness="10"
                                           Stroke="#FF158BCF"
                                           Fill="#FF158BCF"></Rectangle>
                            </StackPanel>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

而我自己的项目中,最终想实现的目标是这样的:

所以代码更改后就是这样。

<UserControl.Background>
        <ImageBrush ImageSource="1.png?x-oss-process=image/watermark,g_center,image_YXJ0aWNsZS9wdWJsaWMvd2F0ZXJtYXJrLnBuZz94LW9zcy1wcm9jZXNzPWltYWdlL3Jlc2l6ZSxQXzQwCg==,t_20"  Stretch="Uniform"/>
    </UserControl.Background>
    <UserControl.Resources>
        <Style TargetType="local:ThumbEx">
            <Setter Property="Width" Value="30"></Setter>
            <Setter Property="Height" Value="100"></Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Thumb">
                        <Grid>
                            <StackPanel>
                                <Rectangle SnapsToDevicePixels="True"                           
                                           Height="55"
                                           RadiusX="1"
                                           RadiusY="1"
                                           StrokeThickness="10"
                                           Stroke="#FF158BCF"
                                           Fill="#FF158BCF"></Rectangle>
                            </StackPanel>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid >
        <StackPanel Margin="15,0,15,0"                    
                    x:Name="RangeContainer"
                    Orientation="Horizontal">
        </StackPanel>
        <Canvas x:Name="ThumbContainer">
        </Canvas>
    </Grid>

为了多次调用方便,我们使用UserControl来自定义这个控件,当然得支持各种数据Binding啊,对于这个控件来说,我不需要知道外面是什么样的数据结构,我只需要知道我要展现多滑块需要哪些数据就行,所以得有一个描述Range的数据结构

public class RangeItem
{
    #region 字段
    private double _from;
    private double _to;
    private string _name;
    private double _maxDuration;
    private bool _isStatic;
    private double _duration;

    #endregion

    #region 属性
    public double From
    {
        get
        {
            return _from;
        }
        set
        {
            _from = value;
        }
    }

    public double To
    {
        get
        {
            return _to;
        }
        set
        {
            _to = value;
        }
    }

    /// <summary>
    /// 是否静止
    /// </summary>
    public bool IsStatic
    {
        get
        {
            return _isStatic;
        }
        set
        {
            _isStatic = value;
        }
    }

    public double Duration
    {
        get
        {
            return _duration;
        }
        set
        {
            _duration = value;
        }
    }

    public double MaxDuration
    {
        get
        {
            return _maxDuration;
        }
        set
        {
            _maxDuration = value;
        }
    }

    #endregion
}

重要的属性有From(起始值),To(结束值),MaxDuration(总长),

根据这一个数据,我们就能生成一个矩形。整个Slider的宽度是固定的,所以就可以根据(To-From)/MaxDuration*Slider长度,就能计算出这个矩形的宽度,直接加入StackPanel就行。

矩形加进去了,现在加滑块,因为滑块是在Canvas中的,所以他需要确切知道Canvas.Left附加属性,这个Left不就左边矩形的宽度么。在把滑块和左右两边的矩形关联起来,因为矩形的拖动事件需要实时去改变两边的矩形的宽度。

        private static double m_ThumbWidth;

        public void SetLeft(double length)
        {
            length = length / 100 * m_ThumbWidth - 15;
            Canvas.SetLeft(_thumbExs[0], length);
        }

        public void SetRight(double length)
        {
            length = length / 100 * m_ThumbWidth + 15;
            Canvas.SetRight(_thumbExs[1], length);
        }

要跟后端的数据绑定,也就是三个矩形的宽度关联

MultiSlider x:Name="mSlider" RangeItems="{Binding Items}"

下方就是加入滑块两端,也就是三个矩形的宽度

            #region 初始化滑块
            mSlider.ControlWidth = mSlider.Width;
            Items.Add(new RangeItem()
            {
                From = 0,
                To = 0,
                IsStatic = true,
                MaxDuration = 100
            });

            Items.Add(new RangeItem()
            {
                From = 0,
                To = 100,
                IsStatic = false,
                MaxDuration = 100
            });

            Items.Add(new RangeItem()
            {
                From = 100,
                To = 100,
                IsStatic = false,
                MaxDuration = 100
            });
            this.DataContext = this;
            #endregion

同时需要这段代码:

        #region 多滑块slider
        private ObservableCollection<RangeItem> _items = new ObservableCollection<RangeItem>();

        public ObservableCollection<RangeItem> Items
        {
            get
            {
                return _items;
            }
            set
            {
                _items = value;
            }
        }
        #endregion

基本就这样,暂时满足自己的需求,时间长了回头过来写,有些啥意思自己都有点弄不明白了,凑合着看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值