所谓的潇洒

导航

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>
View Code

  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;
        }
    }
}
View Code

  使用控件

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;
        }
    }
}
View Code

  默认外观

自定义控件下添加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>
View Code

  样式资源

<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>
View Code

  使用控件和样式

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;
        }
    }
}
View Code

自定义装饰元素

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();
            }
        }
    }
}
View Code

 

posted on 2023-02-28 17:07  所谓的潇洒  阅读(22)  评论(0编辑  收藏  举报