WPF知识点备忘录——自定义元素
用户控件
XMAL代码
<UserControl x:Class="CustomControl.ColorPicker" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="colorPicker"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Slider Name="sliderRed" Minimum="0" Maximum="255" Value="{Binding ElementName=colorPicker,Path=Red}"/> <Slider Grid.Row="1" Name="sliderGreen" Minimum="0" Maximum="255" Value="{Binding ElementName=colorPicker,Path=Green}"/> <Slider Grid.Row="2" Name="sliderBlue" Minimum="0" Maximum="255" Value="{Binding ElementName=colorPicker,Path=Blue}"/> <Rectangle Grid.Column="1" Grid.RowSpan="3" Width="50" Stroke="Black" StrokeThickness="1"> <Rectangle.Fill > <SolidColorBrush Color="{Binding ElementName=colorPicker,Path=Color}"/> </Rectangle.Fill> </Rectangle> <Button Grid.Row="3" Command="Undo" CommandTarget="{Binding ElementName=colorPicker}">Undo</Button> </Grid> </UserControl>
CS代码
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.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace CustomControl { /// <summary> /// UserControl1.xaml 的交互逻辑 /// </summary> public partial class ColorPicker : UserControl { public static DependencyProperty ColorProperty; public static DependencyProperty RedProperty; public static DependencyProperty GreenProperty; public static DependencyProperty BlueProperty; public static readonly RoutedEvent ColorChangedEvent;//自定义路由事件 private Color? previousColor; 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 event RoutedPropertyChangedEventHandler<Color> ColorChanged { add { AddHandler(ColorChangedEvent,value); } remove { RemoveHandler(ColorChangedEvent, value); } } static ColorPicker() { ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPicker), new PropertyMetadata(Colors.Black, new PropertyChangedCallback(OnColorChanged))); RedProperty = DependencyProperty.Register("Red", typeof(byte), typeof(ColorPicker), new PropertyMetadata(new PropertyChangedCallback(OnRGBChanged))); GreenProperty = DependencyProperty.Register("Green", typeof(byte), typeof(ColorPicker), new PropertyMetadata(new PropertyChangedCallback(OnRGBChanged))); BlueProperty = DependencyProperty.Register("Blue", typeof(byte), typeof(ColorPicker), new PropertyMetadata(new PropertyChangedCallback(OnRGBChanged))); ColorChangedEvent = EventManager.RegisterRoutedEvent("ColorChanged",RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<Color>),typeof(ColorPicker)); CommandManager.RegisterClassCommandBinding(typeof(ColorPicker), new CommandBinding(ApplicationCommands.Undo, UndoCommand_Excuted, UndoCommand_CanExcuted)); } public ColorPicker() { InitializeComponent(); //SetUpCommands(); } private static void OnColorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { Color newColor = (Color)e.NewValue; Color oldColor = (Color)e.OldValue; ColorPicker colorPicker = (ColorPicker)sender; colorPicker.Red = newColor.R; colorPicker.Green = newColor.G; colorPicker.Blue = newColor.B; RoutedPropertyChangedEventArgs<Color> args = new RoutedPropertyChangedEventArgs<Color>(oldColor, newColor); args.RoutedEvent = ColorPicker.ColorChangedEvent; colorPicker.RaiseEvent(args); colorPicker.previousColor = oldColor; } /// <summary> /// OnColorChanged修改RGB值触发OnRGBChanged,而后者中修改Color不会仔触发回去, /// 因为在WPF中不允许重复进入属性变化回调函数 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void OnRGBChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ColorPicker colorPicker = (ColorPicker)sender; Color color = colorPicker.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; colorPicker.Color = color; } private static void UndoCommand_CanExcuted(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = ((ColorPicker)sender).previousColor.HasValue; } private static void UndoCommand_Excuted(object sender, ExecutedRoutedEventArgs e) { var colorPicker = (ColorPicker)sender; colorPicker.Color = (Color)colorPicker.previousColor; } } }
使用控件
xmlns:lib="clr-namespace:CustomControl;assembly=CustomControl" <lib:ColorPicker Name="colorPicker" Color="Beige" ColorChanged="ColorPicker_ColorChanged"></lib:ColorPicker>
无外观用户控件
CS代码
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.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace CustomControl { /// <summary> /// UserControl1.xaml 的交互逻辑 /// </summary> [TemplatePart(Name ="PART_RedSlider",Type =typeof(RangeBase))] [TemplatePart(Name = "PART_GreenSlider", Type = typeof(RangeBase))] [TemplatePart(Name = "PART_BlueSlider", Type = typeof(RangeBase))] public class NoFaceColorPicker : Control { public static DependencyProperty ColorProperty; public static DependencyProperty RedProperty; public static DependencyProperty GreenProperty; public static DependencyProperty BlueProperty; public static readonly RoutedEvent ColorChangedEvent; private Color? previousColor; 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 event RoutedPropertyChangedEventHandler<Color> ColorChanged { add { AddHandler(ColorChangedEvent, value); } remove { RemoveHandler(ColorChangedEvent, value); } } static NoFaceColorPicker() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NoFaceColorPicker), new FrameworkPropertyMetadata(typeof(NoFaceColorPicker))); ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(NoFaceColorPicker), new PropertyMetadata(Colors.Black, new PropertyChangedCallback(OnColorChanged))); RedProperty = DependencyProperty.Register("Red", typeof(byte), typeof(NoFaceColorPicker), new PropertyMetadata(new PropertyChangedCallback(OnRGBChanged))); GreenProperty = DependencyProperty.Register("Green", typeof(byte), typeof(NoFaceColorPicker), new PropertyMetadata(new PropertyChangedCallback(OnRGBChanged))); BlueProperty = DependencyProperty.Register("Blue", typeof(byte), typeof(NoFaceColorPicker), new PropertyMetadata(new PropertyChangedCallback(OnRGBChanged))); ColorChangedEvent = EventManager.RegisterRoutedEvent("ColorChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<Color>), typeof(NoFaceColorPicker)); CommandManager.RegisterClassCommandBinding(typeof(NoFaceColorPicker), new CommandBinding(ApplicationCommands.Undo, UndoCommand_Excuted, UndoCommand_CanExcuted)); } public NoFaceColorPicker() { } 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); } RangeBase greenSlider = GetTemplateChild("PART_GreenSlider") as RangeBase; if (greenSlider != null) { Binding binding = new Binding("Green"); binding.Source = this; binding.Mode = BindingMode.TwoWay; greenSlider.SetBinding(RangeBase.ValueProperty, binding); } RangeBase blueSlider = GetTemplateChild("PART_BlueSlider") as RangeBase; if (blueSlider != null) { Binding binding = new Binding("Blue"); binding.Source = this; binding.Mode = BindingMode.TwoWay; blueSlider.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(ColorProperty, binding); } } private static void OnColorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { Color newColor = (Color)e.NewValue; Color oldColor = (Color)e.OldValue; NoFaceColorPicker colorPicker = (NoFaceColorPicker)sender; colorPicker.Red = newColor.R; colorPicker.Green = newColor.G; colorPicker.Blue = newColor.B; RoutedPropertyChangedEventArgs<Color> args = new RoutedPropertyChangedEventArgs<Color>(oldColor, newColor); args.RoutedEvent = ColorChangedEvent; colorPicker.RaiseEvent(args); colorPicker.previousColor = oldColor; } private static void OnRGBChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { NoFaceColorPicker colorPicker = (NoFaceColorPicker)sender; Color color = colorPicker.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; colorPicker.Color = color; } private static void UndoCommand_CanExcuted(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = ((NoFaceColorPicker)sender).previousColor.HasValue; } private static void UndoCommand_Excuted(object sender, ExecutedRoutedEventArgs e) { var colorPicker = (NoFaceColorPicker)sender; colorPicker.Color = (Color)colorPicker.previousColor; } } }
默认外观
自定义控件下添加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"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/CustomControl;component/Themes/NoFaceColorPicker.xaml"/> <ResourceDictionary Source="/CustomControl;component/Themes/FlipPanel.xaml"/> <ResourceDictionary Source="/CustomControl;component/Themes/CustomWindow.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> 添加NoFaceColorPicker.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"> <Style TargetType="{x:Type local:NoFaceColorPicker}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:NoFaceColorPicker}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Slider Minimum="0" Maximum="255" Margin="{TemplateBinding Padding}" Name="PART_RedSlider"/> <Slider Grid.Row="1" Minimum="0" Maximum="255" Margin="{TemplateBinding Padding}" Name="PART_GreenSlider"/> <Slider Grid.Row="2" Minimum="0" Maximum="255" Margin="{TemplateBinding Padding}" Name="PART_BlueSlider"/> <Rectangle Grid.Column="1" Grid.RowSpan="3" Margin="{TemplateBinding Padding}" Width="50" Stroke="Black" StrokeThickness="1"> <Rectangle.Fill > <SolidColorBrush x:Name="PART_PreviewBrush"/> </Rectangle.Fill> </Rectangle> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
样式资源
<Application x:Class="CustomCtrlTest.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:CustomCtrlTest" xmlns:lib="clr-namespace:CustomControl;assembly=CustomControl" StartupUri="MainWindow.xaml"> <Application.Resources> <Style TargetType="lib:NoFaceColorPicker"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="lib:NoFaceColorPicker"> <StackPanel Orientation="Horizontal"> <Ellipse Stroke="Black" StrokeThickness="2" Width="100" Height="50" > <Ellipse.Fill> <SolidColorBrush x:Name="PART_PreviewBrush"/> </Ellipse.Fill> </Ellipse> <StackPanel > <Slider Orientation="Vertical" Margin="5,0,5,0" Minimum="0" Maximum="255" Height="80" Name="PART_RedSlider"/> <TextBlock Text="R" HorizontalAlignment="Center"/> </StackPanel> <StackPanel > <Slider Orientation="Vertical" Margin="5,0,5,0" Minimum="0" Maximum="255" Height="80" Name="PART_GreenSlider"/> <TextBlock Text="G" HorizontalAlignment="Center"/> </StackPanel> <StackPanel > <Slider Orientation="Vertical" Margin="5,0,5,0" Minimum="0" Maximum="255" Height="80" Name="PART_BlueSlider"/> <TextBlock Text="B" HorizontalAlignment="Center"/> </StackPanel> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </Application.Resources> </Application>
使用控件和样式
xmlns:lib="clr-namespace:CustomControl;assembly=CustomControl" <lib:NoFaceColorPicker Grid.Row="2" Name="noFaceColorPicker" ColorChanged="ColorPicker_ColorChanged"></lib:NoFaceColorPicker>
支持可视化状态
...
自定义绘图元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; namespace CustomControl { public class CustomDrawnElement : FrameworkElement { public static DependencyProperty BackGroundColorProperty; static CustomDrawnElement() { FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(Colors.Yellow); metadata.AffectsRender = true; BackGroundColorProperty = DependencyProperty.Register("BackGroundColor", typeof(Color), typeof(CustomDrawnElement), metadata); } public Color BackGroundColor { get { return (Color)GetValue(BackGroundColorProperty); } set { SetValue(BackGroundColorProperty, value); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); this.InvalidateVisual(); } protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); this.InvalidateVisual(); } protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); Rect bounds = new Rect(0, 0, base.ActualWidth, base.ActualHeight); drawingContext.DrawRectangle(GetForeGroundBrush(), null, bounds); } private Brush GetForeGroundBrush() { if (!IsMouseOver) return new SolidColorBrush(BackGroundColor); RadialGradientBrush brush = new RadialGradientBrush(Colors.White, Colors.Black); Point absoluteGradientOrigin = Mouse.GetPosition(this); Point relativeGradientOrigin = new Point( absoluteGradientOrigin.X / base.ActualWidth, absoluteGradientOrigin.Y / base.ActualHeight); brush.GradientOrigin = relativeGradientOrigin; brush.Center = relativeGradientOrigin; return brush; } } }
自定义装饰元素
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.Input; using System.Windows.Media; namespace CustomControl { public class CustomDrawnDecorator : Decorator { public static DependencyProperty DecoratorBackGroundColorProperty; static CustomDrawnDecorator() { FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(Colors.Yellow); metadata.AffectsRender = true; DecoratorBackGroundColorProperty = DependencyProperty.Register("DecoratorBackGroundColor", typeof(Color), typeof(CustomDrawnElement), metadata); } public Color DecoratorBackGroundColor { get { return (Color)GetValue(DecoratorBackGroundColorProperty); } set { SetValue(DecoratorBackGroundColorProperty, value); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); this.InvalidateVisual(); } protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); this.InvalidateVisual(); } protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); Rect bounds = new Rect(0, 0, base.ActualWidth, base.ActualHeight); drawingContext.DrawRectangle(GetForeGroundBrush(), null, bounds); } private Brush GetForeGroundBrush() { if (!IsMouseOver) return new SolidColorBrush(DecoratorBackGroundColor); RadialGradientBrush brush = new RadialGradientBrush(Colors.White, Colors.Black); Point absoluteGradientOrigin = Mouse.GetPosition(this); Point relativeGradientOrigin = new Point( absoluteGradientOrigin.X / base.ActualWidth, absoluteGradientOrigin.Y / base.ActualHeight); brush.GradientOrigin = relativeGradientOrigin; brush.Center = relativeGradientOrigin; return brush; } protected override Size MeasureOverride(Size constraint) { UIElement child = this.Child; if (child != null) { child.Measure(constraint); return child.DesiredSize; } else { return new Size(); } } } }