WPF自定义Slider样式采坑记录

0x01. 概要

WPF 自带的拖动条控件是 Slider , 其默认样式为:

这种风格一般很难和实际的APP匹配, UI肯定会给一种自己的APP风格的拖动条. 最简单的莫过于修改滑块图案, 滑轨颜色等等. 如:

0x02. Slider组成

根据微软官方的文档, 一个Slider如下组成:

从上图我们可以看出, Slider的简单组成为: Track 和 TickBar, 其中Track包括:

  • Thumb : 滑块
  • RepeatButton : 重复的按钮, 即滑轨. 分为两段, 增量部分和减量部分.

TickBar为刻度标尺, 可选.

0x03. 自定义Slider风格

和其他WPF控件的自定义一样, 可以在 Window 标签的 Resource 中直接定义 Style , 也可以在 Slider 标签内自定义.

首先自定义 Thumb 部分, 把 Thumb 改为一个灰色边儿的白色圆形:

    
    
    
    
    
        
            
                
                    
                
            
        
    

其次是定义 RepeatButton , 分为 IncreaseRepeatButtonDecreaseRepeatButton , 这里我们将两种 Button 定义为一样的颜色和形状:

    
    
    
    
    
        
            
                
            
        
    


    
    
    
        
            
                
            
        
    

接下来是组合上面的 ThumbRepeatButton :

    
        
            
                
                    
                        
                            
                        
                        
                            
                        
                        
                            
                        
                    
                
            
        
    

在 Track内部包括了 IncreaseRepeatButton , Thumb , DecreaseRepeatButton . 组合好以后应用到 Slider中:

    
    

然后运行项目, 你会发现 Slider 已经和上面的图一样了:

但是, 当你拖动滑块的时候会发现拖不动!

这是为什么? 官网给出了 Slider 的自定义说明文档: Slider Styles and Templates | Microsoft Docs , 根据文档说明代码, 你会发现组合 Track的代码:


      
        
      
      
        
      
      
        
      
    

经过一步步删除代码发现, 当删除 x:Name=PART_Track 这部分的时候, 滑块就无法滑动!

所以自定义时一定要给 Track 添加 x:Name=PART_Track , 保证滑块可以滑动!!!

所以自定义时一定要给 Track 添加 x:Name=PART_Track , 保证滑块可以滑动!!!

所以自定义时一定要给 Track 添加 x:Name=PART_Track , 保证滑块可以滑动!!!

这是个很不起眼的坑, 请多加小心.

为什么没有这个名字就无法滑动呢?

我们打开 Slider 的源码查看:

namespace System.Windows.Controls
{
  /// Represents a control that lets the user select from a range of values by moving a  control along a .
  [Localizability(LocalizationCategory.Ignore)]
  [DefaultEvent("ValueChanged")]
  [DefaultProperty("Value")]
  [TemplatePart(Name = "PART_Track", Type = typeof (Track))]
  [TemplatePart(Name = "PART_SelectionRange", Type = typeof (FrameworkElement))]
  public class Slider : RangeBase
  {
    //...其他代码
    private const string TrackName = "PART_Track";
    private const string SelectionRangeElementName = "PART_SelectionRange";
    //...其他代码

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();
      this.SelectionRangeElement = this.GetTemplateChild("PART_SelectionRange") as FrameworkElement;
      this.Track = this.GetTemplateChild("PART_Track") as Track;
      if (this._autoToolTip == null)
        return;
      this._autoToolTip.PlacementTarget = this.Track != null ? (UIElement) this.Track.Thumb : (UIElement) null;
    }
  }

在其源码内部的是直接硬编码了 PART_Track 这个名称的, 所以如果不写名字或者修改名字都会导致无法滑动.