zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    EMQ X v4.3 正式发布:性能大幅提升,更好用的多语言扩展
    全新 EMQ X Cloud 物联网云平台重磅发布
    MQTT 和 CoAP 在 EMQ X 世界的一次「约会」
    Python MQTT 异步框架 —— HBMQTT
    EMQ X 团队:连接数亿关键 IoT 设备|EMQ 2021 全球招聘季
    让每一个来自社区的声音被听见、有回响:EMQ X 开源项目正式采用 RFC 流程
    Kuiper 1.1.2 正式发布
    Xamarin.Forms iOS打包上传AppStore,奇妙的崩溃 EXC_CRASH (SIGABRT)
    大数据系列修炼-Scala课程01
    文本摘要简述
  • 原文地址:https://www.cnblogs.com/lekko/p/2604257.html
Copyright © 2011-2022 走看看