1 新建wpf项目
2 新建wpf UserControl类库
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace CustomControl
{
[TemplatePart(Name = "PART_RedSlider", Type = typeof(RangeBase))]
[TemplatePart(Name = "PART_BlueSlider", Type = typeof(RangeBase))]
[TemplatePart(Name = "PART_GreenSlider", Type = typeof(RangeBase))]
[TemplatePart(Name = "PART_PreviewBrush", Type = typeof(SolidColorBrush))]
public class ColorPicker : Control
{
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public byte Red
{
get { return (byte)GetValue(RedProperty); }
set { SetValue(RedProperty, value); }
}
public byte Green
{
get { return (byte)GetValue(GreenProperty); }
set { SetValue(GreenProperty, value); }
}
public byte Blue
{
get { return (byte)GetValue(BlueProperty); }
set { SetValue(BlueProperty, value); }
}
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(Color), typeof(ColorPicker), new PropertyMetadata(Colors.Black, OnColorChanged));
public static readonly DependencyProperty RedProperty =
DependencyProperty.Register("Red", typeof(byte), typeof(ColorPicker), new PropertyMetadata(OnColorRGBChanged));
public static readonly DependencyProperty GreenProperty =
DependencyProperty.Register("Green", typeof(byte), typeof(ColorPicker), new PropertyMetadata(OnColorRGBChanged));
public static readonly DependencyProperty BlueProperty =
DependencyProperty.Register("Blue", typeof(byte), typeof(ColorPicker), new PropertyMetadata(OnColorRGBChanged));
private static void OnColorRGBChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ColorPicker uc = (ColorPicker)d;
Color color = uc.Color;
if (e.Property == RedProperty)
{
color.R = (byte)e.NewValue;
}
else if (e.Property == GreenProperty)
{
color.G = (byte)e.NewValue;
}
else if (e.Property == BlueProperty)
{
color.B = (byte)e.NewValue;
}
uc.Color = color;
}
private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ColorPicker uc = (ColorPicker)d;
Color color = (Color)e.NewValue;
uc.Red = color.R;
uc.Green = color.G;
uc.Blue = color.B;
Color oldColor = (Color)e.OldValue;
uc.PreviousColor = oldColor;
RoutedPropertyChangedEventArgs<Color> args = new RoutedPropertyChangedEventArgs<Color>(oldColor, color);
args.RoutedEvent = ColorChangedEvent;
uc.RaiseEvent(args);
}
public static readonly RoutedEvent ColorChangedEvent = EventManager.RegisterRoutedEvent("ColorChanged", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<Color>), typeof(ColorPickerUserControl));
public event RoutedPropertyChangedEventHandler<Color> ColorChanged
{
add { AddHandler(ColorChangedEvent, value); }
remove { RemoveHandler(ColorChangedEvent, value); }
}
Color? PreviousColor;
public ColorPicker()
{
CommandBinding binding = new CommandBinding(ApplicationCommands.Undo,
UndoCommand_Executed, UndoCommand_CanExecute);
this.CommandBindings.Add(binding);
}
private void UndoCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = PreviousColor.HasValue;
}
private void UndoCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
this.Color = (Color)PreviousColor;
}
static ColorPicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
RangeBase slider = GetTemplateChild("PART_RedSlider") as RangeBase;
if (slider != null)
{
Binding binding = new Binding("Red");
binding.Source = this;
binding.Mode = BindingMode.TwoWay;
slider.SetBinding(RangeBase.ValueProperty, binding);
}
slider = GetTemplateChild("PART_GreenSlider") as RangeBase;
if (slider != null)
{
Binding binding = new Binding("Green");
binding.Source = this;
binding.Mode = BindingMode.TwoWay;
slider.SetBinding(RangeBase.ValueProperty, binding);
}
slider = GetTemplateChild("PART_BlueSlider") as RangeBase;
if (slider != null)
{
Binding binding = new Binding("Blue");
binding.Source = this;
binding.Mode = BindingMode.TwoWay;
slider.SetBinding(RangeBase.ValueProperty, binding);
}
SolidColorBrush brush = GetTemplateChild("PART_PreviewBrush") as SolidColorBrush;
if (brush != null)
{
Binding binding = new Binding("Color");
binding.Source = brush;
binding.Mode = BindingMode.OneWayToSource;
this.SetBinding(ColorPicker.ColorProperty, binding);
}
}
}
}
3 新建Themes文件夹,新建generic.xaml资源字典
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl.Themes">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/CustomControl;component/Themes/ColorPicker.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
新建ColorPicker.xaml资源字典,注意在这里给每个Slider和SolidColorBrush都命名,并将其Value,Color属性绑定删掉,从而在代码中绑定
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:ColorPicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ColorPicker}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Slider Name="PART_RedSlider" Minimum="0" Maximum="255"
Margin="{TemplateBinding Padding}"></Slider>
<Slider Grid.Row="1" Name="PART_GreenSlider" Minimum="0" Maximum="255"
Margin="{TemplateBinding Padding}"></Slider>
<Slider Grid.Row="2" Name="PART_BlueSlider" Minimum="0" Maximum="255"
Margin="{TemplateBinding Padding}"></Slider>
<Rectangle Grid.Column="1" Grid.RowSpan="3"
Margin="{TemplateBinding Padding}"
Width="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<SolidColorBrush x:Name="PART_PreviewBrush"></SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
4 主程序中,有两个控件引用,一个使用默认模板,一个使用新的模板,因为数据绑定已经在代码中实现,所以,所有的模板只要关注布局就可以了
<Window x:Class="CustomControlApp.ColorPickerTwoWays"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CustomControlApp"
xmlns:lib="clr-namespace:CustomControl;assembly=CustomControl"
mc:Ignorable="d"
Title="ColorPickerTwoWays" Height="450" Width="800">
<Window.Resources>
<ControlTemplate x:Key="FancyColorPickerTemplate">
<Border Background="LightGoldenrodYellow"
BorderBrush="Black"
BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="{x:Type Slider}">
<Setter Property="Orientation" Value="Vertical"></Setter>
<Setter Property="TickPlacement" Value="TopLeft"></Setter>
<Setter Property="TickFrequency" Value="10"></Setter>
<Setter Property="Minimum" Value="0"></Setter>
<Setter Property="Maximum" Value="255"></Setter>
<Setter Property="Margin" Value="5"></Setter>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3"></Setter>
<Setter Property="FontSize" Value="10"></Setter>
</Style>
</Grid.Resources>
<Ellipse Grid.Column="0" Grid.RowSpan="2"
Margin="10" Height="120" Stroke="LightGray" StrokeThickness="5">
<Ellipse.Fill>
<SolidColorBrush Color="{Binding Path=Color,
RelativeSource={RelativeSource TemplatedParent}}"></SolidColorBrush>
</Ellipse.Fill>
</Ellipse>
<Slider Name="PART_RedSlider" Grid.Column="1"></Slider>
<TextBlock Grid.Row="1" Grid.Column="1">RED</TextBlock>
<Slider Name="PART_GreenSlider" Grid.Column="2"></Slider>
<TextBlock Grid.Row="1" Grid.Column="2">GREEN</TextBlock>
<Slider Name="PART_BlueSlider" Grid.Column="3"></Slider>
<TextBlock Grid.Row="1" Grid.Column="3">BLUE</TextBlock>
</Grid>
</Border>
</ControlTemplate>
</Window.Resources>
<StackPanel>
<lib:ColorPicker
Name="colorPicker1" Margin="2" Padding="3" Color="AliceBlue"></lib:ColorPicker>
<lib:ColorPicker
Name="colorPicker2" Template="{StaticResource FancyColorPickerTemplate}" Color="{Binding ElementName=colorPicker1,Path=Color,Mode=TwoWay}"
Margin="5,20,5,5" ></lib:ColorPicker>
</StackPanel>
</Window>