WPF自定义控件开发实例 - ColorPicker

开发环境:Win10 + VS2017 + .Net4.5

这个 ColorPicker 是<<WPF编程宝典:使用C#2012和.NET4.5>>这本书中的例子.这里我记录一下,主要是为了加深印象,防止以后要开发自定义控件的时候忘记一些基本的步骤,可以随时来查一下.
调到博文的最后可以先查看一下效果图.

  1. 建立ColorPicker类继承自 Control

    为什么继承自 Control,Control类继承自UIElement -> FrameworkElement -> ColorPicker,同时 Control 类提供了 Template 功能.

  2. 通知WPF,将为控件提供新的样式.
    方法是在静态构造函数中调用 OverrideMetadata()方法.

    static ColorPicker()
     {
         DefaultStyleKeyProperty.OverrideMetadata(
             typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker)));
     }
    
  3. 编写控件逻辑,添加一些必要的属性|事件|方法 ...

  4. 重写 OnApplyTemplate 方法,为模板中的元素添加数据绑定或者关联事件处理程序

  5. ColorPicker类的完整代码:

    using System;
     using System.Windows;
     using System.Windows.Controls;
     using System.Windows.Controls.Primitives;
     using System.Windows.Data;
     using System.Windows.Media;
    

    namespace Demo.DIYControl.Controls.DeepInDIY
    {
    [TemplatePart(Name = RedSliderName,Type = typeof(RangeBase))]
    [TemplatePart(Name = GreenSliderName, Type = typeof(RangeBase))]
    [TemplatePart(Name = BlueSliderName, Type = typeof(RangeBase))]
    [TemplatePart(Name = PreviewBrushName, Type = typeof(SolidColorBrush))]
    public class ColorPicker:Control
    {
    private const string RedSliderName = "PART_RedSlider";
    private const string GreenSliderName = "PART_GreenSlider";
    private const string BlueSliderName = "PART_BlueSlider";
    private const string PreviewBrushName = "PART_PreviewBrush";

         private Brush _initializeBorderBrush;
         
    
         public const byte RGBMaxValue = 255;
    
         public byte Red
         {
             get { return (byte)GetValue(RedProperty); }
             set { SetValue(RedProperty, value); }
         }
         // Using a DependencyProperty as the backing store for Red.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty RedProperty =
             DependencyProperty.Register(
                 nameof(Red), typeof(byte), typeof(ColorPicker), 
                 new FrameworkPropertyMetadata(OnColorRGBChanged));
    
         public byte Green
         {
             get { return (byte)GetValue(GreenProperty); }
             set { SetValue(GreenProperty, value); }
         }
         // Using a DependencyProperty as the backing store for Green.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty GreenProperty =
             DependencyProperty.Register(
                 nameof(Green), typeof(byte), typeof(ColorPicker), 
                 new FrameworkPropertyMetadata(OnColorRGBChanged));
    
         public byte Blue
         {
             get { return (byte)GetValue(BlueProperty); }
             set { SetValue(BlueProperty, value); }
         }
         // Using a DependencyProperty as the backing store for Blue.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty BlueProperty =
             DependencyProperty.Register(
                 nameof(Blue), typeof(byte), typeof(ColorPicker), 
                 new FrameworkPropertyMetadata(OnColorRGBChanged));
    
         public Color Color
         {
             get { return (Color)GetValue(ColorProperty); }
             set { SetValue(ColorProperty, value); }
         }
         // Using a DependencyProperty as the backing store for Color.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty ColorProperty =
             DependencyProperty.Register(nameof(Color), typeof(Color), 
                 typeof(ColorPicker), new FrameworkPropertyMetadata(Colors.Black,OnColorChanged));
    
         public CornerRadius CornerRadius
         {
             get { return (CornerRadius)GetValue(CornerRadiusProperty); }
             set { SetValue(CornerRadiusProperty, value); }
         }
         // Using a DependencyProperty as the backing store for CornerRadius.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty CornerRadiusProperty =
             DependencyProperty.Register(&quot;CornerRadius&quot;, typeof(CornerRadius), typeof(ColorPicker), 
                 new FrameworkPropertyMetadata(default(CornerRadius)));
    
         public bool UseDynamicBorder
         {
             get { return (bool)GetValue(UseDynamicBorderProperty); }
             set { SetValue(UseDynamicBorderProperty, value); }
         }
         // Using a DependencyProperty as the backing store for UseDynamicBorder.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty UseDynamicBorderProperty =
             DependencyProperty.Register(
                 &quot;UseDynamicBorder&quot;, typeof(bool), typeof(ColorPicker), 
                 new FrameworkPropertyMetadata(true, OnUseDynamicBorderChanged));
    
    
         public static readonly RoutedEvent ColorChangedEvent = EventManager.RegisterRoutedEvent(
             &quot;ColorChangedEvent&quot;, RoutingStrategy.Bubble,
             typeof(RoutedPropertyChangedEventHandler&lt;Color&gt;), typeof(ColorPicker));
         public event RoutedPropertyChangedEventHandler&lt;Color&gt; ColorChanged
         {
             add { AddHandler(ColorChangedEvent, value); }
             remove { RemoveHandler(ColorChangedEvent, value); }
         }
    
         static ColorPicker()
         {
             DefaultStyleKeyProperty.OverrideMetadata(
                 typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker)));
         }
    
         public ColorPicker()
         {
             Loaded += (sender, args) =&gt;
             {
                 _initializeBorderBrush = BorderBrush;   //save initial borderbrush
                 BorderBrush = UseDynamicBorder? new SolidColorBrush(Color) : BorderBrush;
             };
         }
    
    
         private static void OnColorRGBChanged(DependencyObject sender,
             DependencyPropertyChangedEventArgs args)
         {
             ColorPicker colorPicker = sender as ColorPicker;
             colorPicker = colorPicker ?? throw new ArgumentException();
    
             Color color = colorPicker.Color;
             if (args.Property == RedProperty)
                 color.R = (byte)args.NewValue;
             else if (args.Property == GreenProperty)
                 color.G = (byte)args.NewValue;
             else if (args.Property == BlueProperty)
                 color.B = (byte)args.NewValue;
    
             colorPicker.Color = color;
         }
    
         private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
             ColorPicker colorPicker = d as ColorPicker;
             d = d ?? throw new ArgumentException();
             colorPicker.Red = colorPicker.Color.R;
             colorPicker.Green = colorPicker.Color.G;
             colorPicker.Blue = colorPicker.Color.B;
    
             //set border color
             if (colorPicker.UseDynamicBorder)
                 colorPicker.BorderBrush = new SolidColorBrush(colorPicker.Color);
    
             colorPicker.RaiseEvent(
                 new RoutedEventArgs(ColorChangedEvent, e.NewValue));
         }
    
         private static void OnUseDynamicBorderChanged(
             DependencyObject d, DependencyPropertyChangedEventArgs args)
         {
             ColorPicker colorPicker = d as ColorPicker;
             colorPicker = colorPicker ?? throw new ArgumentException();
    
             colorPicker.BorderBrush = (bool)args.NewValue ? new SolidColorBrush(colorPicker.Color) : colorPicker._initializeBorderBrush;
         }
    
         public override void OnApplyTemplate()
         {
             base.OnApplyTemplate();
    
             RangeBase slider = GetTemplateChild(&quot;PART_RedSlider&quot;) as RangeBase;
             if (slider != null)
             {
                 Binding binding = new Binding()
                 {
                     Path = new PropertyPath(&quot;Red&quot;),
                     Source = this,
                     Mode = BindingMode.TwoWay
                 };
                 slider.SetBinding(RangeBase.ValueProperty, binding);
                 slider.Maximum = RGBMaxValue;
             }
    
             slider = GetTemplateChild(&quot;PART_GreenSlider&quot;) as RangeBase;
             if (slider != null)
             {
                 Binding binding = new Binding()
                 {
                     Path = new PropertyPath(nameof(Green)),
                     Source = this,
                     Mode = BindingMode.TwoWay
                 };
                 slider.SetBinding(RangeBase.ValueProperty, binding);
                 slider.Maximum = RGBMaxValue;
             }
    
             slider = GetTemplateChild(&quot;PART_BlueSlider&quot;) as RangeBase;
             if (slider != null)
             {
                 Binding binding = new Binding()
                 {
                     Path = new PropertyPath(nameof(Blue)),
                     Source = this,
                     Mode = BindingMode.TwoWay
                 };
                 slider.SetBinding(RangeBase.ValueProperty, binding);
                 slider.Maximum = RGBMaxValue;
             }
    
             SolidColorBrush brush = GetTemplateChild(&quot;PART_PreviewBrush&quot;) as SolidColorBrush;
             if (brush != null)
             {
                 Binding bd = new Binding
                 {
                     Path = new PropertyPath(nameof(brush.Color)),
                     Source = brush,
                     Mode = BindingMode.OneWayToSource
                 };
                 SetBinding(ColorPicker.ColorProperty, bd);
             }
         }
    
     }
    

    }

  6. 差不多就是这样一个简单的ColorPicker就出来了.另外增加一个UseDynamicBorder,来控制控件边框的颜色是否动态改变.
    看一下效果图

  </div>
posted @ 2019-04-14 18:38  Laggage  阅读(954)  评论(0编辑  收藏  举报