今天写搜索界面,有许多值范围搜索的项,先是做了两个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