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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM