项目中遇到需要双滑块的情况,可以网上的基本都是单滑块的样式和例子,但是双滑块的很少,后来终于在网上找到一个大神的帖子,通过修改和完善终于符合自己的需求,可后来再查看代码的时候好像又看不懂了,现在重新整理记录下来,以备后续查看。
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
基本就这样,暂时满足自己的需求,时间长了回头过来写,有些啥意思自己都有点弄不明白了,凑合着看