WPF自定义控件之双滑块Slider

  今天写搜索界面,有许多值范围搜索的项,先是做了两个Textbox加两个Slider来实现选择起始->结束值的范围,后来发现这样用户操作性太不好,前台代码又很臃肿,干脆想办法写了个自定义的控件。首先来看下最终效果吧:

   


  具体的交互基本就是左边框是起始值,右边框是终止值,它们数据的是和两个滑块绑定的,会互相更新。左边的滑块是不能拖到右边滑块之外的,同理右边也不能到左边,如果输入的值超出(小于)上限值(下限),则会把值取为上限值(下限)。

  我的思路就是定义两个Slider,然后拼起来,哈哈!好吧,来看前台代码:

 1 <UserControl x:Class="FS.PresentationManagement.Controls.SilderArrange"

 2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 4              Name="UC_Arrange" Loaded="UC_Arrange_Loaded">

 5     <StackPanel Orientation="Horizontal" Height="{Binding ElementName=UC_Arrange,Path=SilderHeight}" >

 6         <TextBox Text="{Binding ElementName=SL_Bat1,Path=Value}" KeyUp="TextBox_KeyUp1" Width="35" Margin="0,3" BorderBrush="CornflowerBlue" />

 7         <Canvas Width="{Binding ElementName=UC_Arrange,Path=SilderWidth}" Margin="0,0,5,0">

 8             <Slider Name="SL_Bat1"

 9                 Value="{Binding ElementName=UC_Arrange,Path=StartValue}"

10                 Minimum="{Binding ElementName=UC_Arrange,Path=Minimum}"

11                 Maximum="{Binding ElementName=UC_Arrange,Path=Maximum}"

12                 SelectionStart="{Binding ElementName=UC_Arrange,Path=StartValue}"

13                 SelectionEnd="{Binding ElementName=UC_Arrange,Path=EndValue}"

14                 Width="{Binding ElementName=UC_Arrange,Path=SilderWidth}"

15                 TickFrequency="{Binding ElementName=UC_Arrange,Path=SliderTickFrequency}"

16                 FocusVisualStyle="{x:Null}"

17                 CacheMode="BitmapCache"

18                 IsSelectionRangeEnabled="True"

19                 TickPlacement="BottomRight"

20                 IsSnapToTickEnabled="True"

21                 VerticalAlignment="Center"

22                 Margin="2"

23                 ValueChanged="SL_Bat1_ValueChanged">

24                 <Slider.Clip>

25                     <RectangleGeometry Rect="{Binding ElementName=UC_Arrange,Path=StartRect}" />

26                 </Slider.Clip>

27             </Slider>

28             <Slider Name="SL_Bat2" 

29                 Value="{Binding ElementName=UC_Arrange,Path=EndValue}" 

30                 Minimum="{Binding ElementName=UC_Arrange,Path=Minimum}" 

31                 Maximum="{Binding ElementName=UC_Arrange,Path=Maximum}" 

32                 SelectionStart="{Binding ElementName=UC_Arrange,Path=StartValue}" 

33                 SelectionEnd="{Binding ElementName=UC_Arrange,Path=EndValue}" 

34                 Width="{Binding ElementName=UC_Arrange,Path=SilderWidth}"

35                 TickFrequency="{Binding ElementName=UC_Arrange,Path=SliderTickFrequency}"

36                 FocusVisualStyle="{x:Null}"

37                 CacheMode="BitmapCache"

38                 IsSelectionRangeEnabled="True"                

39                 TickPlacement="BottomRight"

40                 IsSnapToTickEnabled="True"

41                 VerticalAlignment="Center"

42                 Margin="2"

43                 ValueChanged="SL_Bat2_ValueChanged">

44                 <Slider.Clip>

45                     <RectangleGeometry Rect="{Binding ElementName=UC_Arrange,Path=EndRect}" />

46                 </Slider.Clip>                

47             </Slider>

48         </Canvas>

49         <TextBox Text="{Binding ElementName=SL_Bat2,Path=Value}" KeyUp="TextBox_KeyUp2" Width="35" Margin="0,3" BorderBrush="CornflowerBlue"/>        

50     </StackPanel>

51 </UserControl>

  没错,我这里用的是Canvas,让两个Slider重叠,然后通过Clip剪裁,然后各个控件的值基本都是绑定到了后台的依赖属性,剪裁的矩形范围也不例外,后台会根据滑块的位置实时更新前台界面,让它们看起来很“和谐”。考虑到剪裁可能产生的效率问题,我把两个Slider的CacheMode都设置成了“BitmapCache”,也就是说会用GPU来计算界面元素。FocusVisualStyle="{x:Null}"这个设置则是让XP的机器显示Slider的时候不会出现很丑的虚线框。
  至于其它设置,都是常规的设置了,这些绑定到后台的除了剪裁的依赖属性设置的是private外,其它都是public,也就是说,使用的时候可以自己再给这些属性赋值。

  来看下后台代码吧:

大段的后台代码
  1 using System;

  2 using System.Windows;

  3 using System.Windows.Controls;

  4 using System.Windows.Input;

  5 

  6 namespace FS.PresentationManagement.Controls

  7 {

  8     /// <summary>

  9     /// 双滑块Slider 

 10     /// By lekko

 11     /// </summary>

 12     public partial class SilderArrange : UserControl

 13     {

 14         #region 私有变量

 15 

 16         private static int _width = 150;  // 拖动条初始宽度

 17         private static int _height = 30;  // 高度

 18         private static int _min = 0;      // 最小值

 19         private static int _max = 100;    // 最大值

 20         private static int _freq = 10;    // 出现刻度的间距

 21 

 22         #endregion

 23 

 24         // 构造函数

 25         public SilderArrange()

 26         {

 27             InitializeComponent();            

 28         }        

 29 

 30         #region 私有属性

 31 

 32         /// <summary>

 33         /// 裁剪矩阵(头)

 34         /// </summary>

 35         private Rect StartRect

 36         {

 37             get { return (Rect)GetValue(StartRectProperty); }

 38             set { SetValue(StartRectProperty, value); }

 39         }

 40         private static readonly DependencyProperty StartRectProperty =

 41             DependencyProperty.Register("StartRect", typeof(Rect), typeof(SilderArrange));

 42 

 43         /// <summary>

 44         /// 裁剪矩阵(尾)

 45         /// </summary>

 46         private Rect EndRect

 47         {

 48             get { return (Rect)GetValue(EndRectProperty); }

 49             set { SetValue(EndRectProperty, value); }

 50         }

 51         private static readonly DependencyProperty EndRectProperty =

 52             DependencyProperty.Register("EndRect", typeof(Rect), typeof(SilderArrange));

 53 

 54         #endregion

 55 

 56         #region 公开依赖属性

 57 

 58         /// <summary>

 59         /// 刻度间距,默认为10

 60         /// </summary>

 61         public int SliderTickFrequency

 62         {

 63             get { return (int)GetValue(SliderTickFrequencyProperty); }

 64             set { SetValue(SliderTickFrequencyProperty, value); }

 65         }

 66         public static readonly DependencyProperty SliderTickFrequencyProperty =

 67             DependencyProperty.Register("SliderTickFrequency", typeof(int), typeof(SilderArrange), new PropertyMetadata(_freq));

 68 

 69         /// <summary>

 70         /// 控件高度,默认为30

 71         /// </summary>

 72         public int SilderHeight

 73         {

 74             get { return (int)GetValue(SilderHeightProperty); }

 75             set { SetValue(SilderHeightProperty, value); }

 76         }

 77         public static readonly DependencyProperty SilderHeightProperty =

 78             DependencyProperty.Register("SilderHeight", typeof(int), typeof(SilderArrange), new PropertyMetadata(_height));

 79 

 80         /// <summary>

 81         /// 拖动条宽度,默认为150

 82         /// </summary>

 83         public int SilderWidth

 84         {

 85             get { return (int)GetValue(SilderWidthProperty); }

 86             set { SetValue(SilderWidthProperty, value); }

 87         }

 88         public static readonly DependencyProperty SilderWidthProperty =

 89             DependencyProperty.Register("SilderWidth", typeof(int), typeof(SilderArrange), new PropertyMetadata(_width));

 90 

 91         /// <summary>

 92         /// 最小值,默认为0

 93         /// </summary>

 94         public int Minimum

 95         {

 96             get { return (int)GetValue(MinimumProperty); }

 97             set { SetValue(MinimumProperty, value); }

 98         }

 99         public static readonly DependencyProperty MinimumProperty =

100             DependencyProperty.Register("Minimum", typeof(int), typeof(SilderArrange), new PropertyMetadata(_min));

101 

102         /// <summary>

103         /// 最大值,默认为100

104         /// </summary>

105         public int Maximum

106         {

107             get { return (int)GetValue(MaximumProperty); }

108             set { SetValue(MaximumProperty, value); }

109         }

110         public static readonly DependencyProperty MaximumProperty =

111             DependencyProperty.Register("Maximum", typeof(int), typeof(SilderArrange), new PropertyMetadata(_max));

112 

113         /// <summary>

114         /// 选中开始值,默认为0

115         /// </summary>

116         public int StartValue

117         {

118             get { return (int)GetValue(StartValueProperty); }

119             set { SetValue(StartValueProperty, value); }

120         }

121         public static readonly DependencyProperty StartValueProperty =

122             DependencyProperty.Register("StartValue", typeof(int), typeof(SilderArrange));

123 

124         /// <summary>

125         /// 选中结束值,默认为100

126         /// </summary>

127         public int EndValue

128         {

129             get { return (int)GetValue(EndValueProperty); }

130             set { SetValue(EndValueProperty, value); }

131         }

132         public static readonly DependencyProperty EndValueProperty =

133             DependencyProperty.Register("EndValue", typeof(int), typeof(SilderArrange), new PropertyMetadata(_max));

134 

135         #endregion

136 

137         #region 前台交互

138 

139         /// <summary>

140         /// 对两个拖动条进行裁剪

141         /// </summary>

142         private void ClipSilder()

143         {

144             int selectedValue = EndValue - StartValue;

145             int totalValue = Maximum - Minimum;

146             double sliderClipWidth = SilderWidth * (StartValue - Minimum + selectedValue / 2) / totalValue;

147             // 对第一个拖动条进行裁剪

148             StartRect = new Rect(0, 0, sliderClipWidth, SilderHeight);

149             // 对第二个拖动条进行裁剪

150             EndRect = new Rect(sliderClipWidth, 0, SilderWidth, SilderHeight);

151         }        

152 

153         /// <summary>

154         /// 初始化裁剪

155         /// </summary>

156         private void UC_Arrange_Loaded(object sender, RoutedEventArgs e)

157         {

158             ClipSilder();

159         }

160 

161         private void SL_Bat1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

162         {

163             if (e.NewValue > EndValue)    // 检查值范围

164                 StartValue = EndValue;    // 超出,重设为最大值

165             ClipSilder();

166         }

167 

168         private void SL_Bat2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

169         {

170             if (e.NewValue < StartValue)

171                 EndValue = StartValue;

172             ClipSilder();

173         }

174 

175         private void TextBox_KeyUp1(object sender, System.Windows.Input.KeyEventArgs e)

176         {

177             try

178             {               

179                 if (e.Key == Key.Enter)    // 按回车时确认输入

180                     StartValue = Convert.ToInt32(((TextBox)sender).Text);

181             }

182             catch

183             {

184             }

185         }

186 

187         private void TextBox_KeyUp2(object sender, KeyEventArgs e)

188         {

189             try

190             {

191                 if (e.Key == Key.Enter)

192                     EndValue = Convert.ToInt32(((TextBox)sender).Text);

193             }

194             catch

195             {

196             }

197         }       

198 

199         #endregion        

200         

201     }

202 }

  这样,在需要使用到这个控件的地方,先在前台代码加上程序集:xmlns:us="clr-namespace:FS.PresentationManagement.Controls",其中“FS.PresentationManagement.Controls”是刚才自定义的控件的命名空间,然后:

<us:SilderArrange x:Name="SLA_V" SilderWidth="250" Minimum="-50" Maximum="200" />

  就可以了,是不是很方便,呵呵,其实还有不少地方可以改进,不过最少能用了。

转载请注明原址:http://www.cnblogs.com/lekko/archive/2012/07/23/2604257.html

你可能感兴趣的:(slider)