基于WPF自定义实现颜色拾取器

下面我将为你展示如何在WPF中自定义实现一个功能完整的颜色拾取器(Color Picker)。这个颜色拾取器将包含颜色选择区域、亮度滑块、RGB/HSV数值输入和十六进制颜色代码输入等功能。

图片[1]_基于WPF自定义实现颜色拾取器_知途无界

1. 基本结构设计

首先创建一个名为ColorPicker.xaml的用户控件:

<UserControl x:Class="YourNamespace.ColorPicker"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="350">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <!-- 主要颜色选择区域 -->
        <Border Grid.Row="0" Background="White" BorderBrush="Gray" BorderThickness="1" Margin="10">
            <Grid>
                <!-- 色彩选择面板 -->
                <Canvas x:Name="ColorPanel" Background="White" Margin="0,0,0,20" MouseLeftButtonDown="ColorPanel_MouseLeftButtonDown" MouseMove="ColorPanel_MouseMove" MouseLeftButtonUp="ColorPanel_MouseLeftButtonUp">
                    <!-- 渐变色将在代码中生成 -->
                </Canvas>
                
                <!-- 亮度滑块 -->
                <Border x:Name="BrightnessSlider" VerticalAlignment="Bottom" Height="20" Margin="0,0,0,0" 
                        Background="Gray" MouseLeftButtonDown="BrightnessSlider_MouseLeftButtonDown" 
                        MouseMove="BrightnessSlider_MouseMove" MouseLeftButtonUp="BrightnessSlider_MouseLeftButtonUp"/>
                
                <!-- 当前选中颜色的小方块 -->
                <Border x:Name="SelectedColorPreview" Width="30" Height="30" VerticalAlignment="Top" HorizontalAlignment="Right" 
                        Margin="0,10,10,0" BorderBrush="Black" BorderThickness="1" Background="White"/>
            </Grid>
        </Border>
        
        <!-- 颜色值显示和输入区域 -->
        <StackPanel Grid.Row="1" Orientation="Vertical" Margin="10,0,10,10">
            <!-- RGB 数值输入 -->
            <GroupBox Header="RGB 值" Margin="0,5">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="30"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="30"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="30"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    
                    <TextBlock Text="R:" VerticalAlignment="Center" Margin="5"/>
                    <TextBox x:Name="RgbR" Width="40" Margin="5" Grid.Column="1" TextChanged="Rgb_ValueChanged"/>
                    <TextBlock Text="G:" VerticalAlignment="Center" Margin="5" Grid.Column="2"/>
                    <TextBox x:Name="RgbG" Width="40" Margin="5" Grid.Column="3" TextChanged="Rgb_ValueChanged"/>
                    <TextBlock Text="B:" VerticalAlignment="Center" Margin="5" Grid.Column="4"/>
                    <TextBox x:Name="RgbB" Width="40" Margin="5" Grid.Column="5" TextChanged="Rgb_ValueChanged"/>
                </Grid>
            </GroupBox>
            
            <!-- 十六进制颜色代码 -->
            <GroupBox Header="十六进制颜色代码" Margin="0,5">
                <TextBox x:Name="HexColor" Width="100" Margin="5" TextChanged="HexColor_TextChanged"/>
            </GroupBox>
            
            <!-- 当前选中的颜色值显示 -->
            <Border Height="30" Margin="0,10,0,0" BorderBrush="Gray" BorderThickness="1">
                <Border.Background>
                    <SolidColorBrush x:Name="CurrentColor" Color="White"/>
                </Border.Background>
            </Border>
        </StackPanel>
    </Grid>
</UserControl>

2. 后台代码实现

对应的后台代码文件ColorPicker.xaml.cs

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace YourNamespace
{
    public partial class ColorPicker : UserControl
    {
        // 当前选中的颜色
        private Color _currentColor = Colors.White;
        
        // 用于绘制色彩面板的渐变矩形集合
        private Rectangle[,] _colorRectangles;
        
        // 是否正在拖拽
        private bool _isDragging = false;
        private bool _isDraggingBrightness = false;
        
        // 拖拽的目标元素
        private FrameworkElement _dragTarget = null;

        public ColorPicker()
        {
            InitializeComponent();
            InitializeColorPanel();
            UpdateColorDisplay();
        }

        // 初始化色彩选择面板
        private void InitializeColorPanel()
        {
            ColorPanel.Children.Clear();
            
            int width = 200;
            int height = 150;
            ColorPanel.Width = width;
            ColorPanel.Height = height;
            
            // 创建一个彩色渐变网格
            _colorRectangles = new Rectangle[20, 15];
            double rectWidth = width / 20.0;
            double rectHeight = height / 15.0;
            
            for (int i = 0; i < 20; i++)
            {
                for (int j = 0; j < 15; j++)
                {
                    Rectangle rect = new Rectangle();
                    rect.Width = rectWidth;
                    rect.Height = rectHeight;
                    rect.Margin = new Thickness(i * rectWidth, j * rectHeight, 0, 0);
                    
                    // 生成HSL颜色并转换为RGB
                    double hue = (i / 19.0) * 360.0;
                    double saturation = 1.0;
                    double lightness = 1.0 - (j / 14.0);
                    
                    Color color = HslToRgb(hue, saturation, lightness);
                    rect.Fill = new SolidColorBrush(color);
                    
                    ColorPanel.Children.Add(rect);
                    _colorRectangles[i, j] = rect;
                }
            }
            
            // 创建亮度滑块背景
            CreateBrightnessSliderBackground();
        }

        // 创建亮度滑块背景
        private void CreateBrightnessSliderBackground()
        {
            // 移除旧的亮度滑块背景
            var oldChildren = BrightnessSlider.Children;
            oldChildren.Clear();
            
            // 创建一个从当前颜色到黑色的渐变
            GradientStopCollection stops = new GradientStopCollection();
            
            // 从当前颜色(不透明)到当前颜色(透明)再到黑色
            stops.Add(new GradientStop(_currentColor, 0.0));
            stops.Add(new GradientStop(_currentColor, 0.7));
            stops.Add(new GradientStop(Colors.Black, 1.0));
            
            LinearGradientBrush brush = new LinearGradientBrush(stops, 0.0);
            BrightnessSlider.Background = brush;
        }

        // HSL转RGB
        private Color HslToRgb(double h, double s, double l)
        {
            double r, g, b;

            if (s == 0)
            {
                r = g = b = l; // 灰度
            }
            else
            {
                double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
                double p = 2 * l - q;
                r = HueToRgb(p, q, h / 360.0 + 1.0 / 3.0);
                g = HueToRgb(p, q, h / 360.0);
                b = HueToRgb(p, q, h / 360.0 - 1.0 / 3.0);
            }

            return Color.FromRgb(
                (byte)(r * 255),
                (byte)(g * 255),
                (byte)(b * 255));
        }

        private double HueToRgb(double p, double q, double t)
        {
            if (t < 0) t += 1;
            if (t > 1) t -= 1;
            if (t < 1.0 / 6.0) return p + (q - p) * 6 * t;
            if (t < 1.0 / 2.0) return q;
            if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6;
            return p;
        }

        // 从RGB获取HSL
        private void RgbToHsl(byte r, byte g, byte b, out double h, out double s, out double l)
        {
            double red = r / 255.0;
            double green = g / 255.0;
            double blue = b / 255.0;

            double max = Math.Max(red, Math.Max(green, blue));
            double min = Math.Min(red, Math.Min(green, blue));

            l = (max + min) / 2;

            if (max == min)
            {
                h = s = 0; // 灰度
            }
            else
            {
                double d = max - min;
                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

                if (max == red)
                {
                    h = (green - blue) / d + (green < blue ? 6 : 0);
                }
                else if (max == green)
                {
                    h = (blue - red) / d + 2;
                }
                else
                {
                    h = (red - green) / d + 4;
                }
                h /= 6;
            }
        }

        // 获取指定坐标的颜色
        private Color GetColorFromPosition(Point position)
        {
            // 获取相对于ColorPanel的坐标
            Point relativePos = new Point(position.X - ColorPanel.Margin.Left, position.Y - ColorPanel.Margin.Top);
            
            // 确保坐标在面板范围内
            if (relativePos.X < 0 || relativePos.Y < 0 || 
                relativePos.X > ColorPanel.ActualWidth || relativePos.Y > ColorPanel.ActualHeight - 20)
            {
                return _currentColor;
            }
            
            // 计算最近的矩形块
            int col = (int)(relativePos.X / (ColorPanel.ActualWidth / 20.0));
            int row = (int)(relativePos.Y / (ColorPanel.ActualHeight / 15.0));
            
            col = Math.Max(0, Math.Min(19, col));
            row = Math.Max(0, Math.Min(14, row));
            
            if (_colorRectangles != null && _colorRectangles[col, row] != null)
            {
                var brush = _colorRectangles[col, row].Fill as SolidColorBrush;
                if (brush != null)
                {
                    return brush.Color;
                }
            }
            
            return _currentColor;
        }

        // 更新亮度
        private Color UpdateBrightness(Color color, double brightness)
        {
            // brightness: 0.0 (黑) 到 1.0 (原色)
            if (brightness >= 1.0)
                return color;
            if (brightness <= 0.0)
                return Colors.Black;
                
            byte r = (byte)(color.R * brightness);
            byte g = (byte)(color.G * brightness);
            byte b = (byte)(color.B * brightness);
            
            return Color.FromRgb(r, g, b);
        }

        // 更新颜色显示
        private void UpdateColorDisplay()
        {
            // 更新当前颜色预览
            SelectedColorPreview.Background = new SolidColorBrush(_currentColor);
            
            // 更新十六进制颜色代码
            HexColor.Text = ColorToHex(_currentColor);
            
            // 更新RGB输入框
            RgbR.Text = ((int)_currentColor.R).ToString();
            RgbG.Text = ((int)_currentColor.G).ToString();
            RgbB.Text = ((int)_currentColor.B).ToString();
            
            // 更新当前颜色显示区域
            CurrentColor.Color = _currentColor;
            
            // 更新亮度滑块
            CreateBrightnessSliderBackground();
        }

        // 颜色转十六进制
        private string ColorToHex(Color color)
        {
            return "#" + color.R.ToString("X2") + color.G.ToString("X2") + color.B.ToString("X2");
        }

        // 十六进制转颜色
        private Color HexToColor(string hex)
        {
            try
            {
                // 移除#号
                if (hex.StartsWith("#"))
                    hex = hex.Substring(1);
                
                if (hex.Length == 6)
                {
                    byte r = Byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
                    byte g = Byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
                    byte b = Byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
                    return Color.FromRgb(r, g, b);
                }
            }
            catch
            {
                // 如果转换失败,返回当前颜色
            }
            
            return _currentColor;
        }

        // 颜色值改变事件处理
        private void Rgb_ValueChanged(object sender, TextChangedEventArgs e)
        {
            if (!_isDragging)
            {
                try
                {
                    byte r = byte.Parse(RgbR.Text);
                    byte g = byte.Parse(RgbG.Text);
                    byte b = byte.Parse(RgbB.Text);
                    
                    _currentColor = Color.FromRgb(r, g, b);
                    UpdateColorDisplay();
                    CreateBrightnessSliderBackground();
                }
                catch
                {
                    // 忽略无效输入
                }
            }
        }

        private void HexColor_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (!_isDragging)
            {
                _currentColor = HexToColor(HexColor.Text);
                UpdateColorDisplay();
                CreateBrightnessSliderBackground();
            }
        }

        // 色彩面板鼠标事件
        private void ColorPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                _isDragging = true;
                _dragTarget = sender as FrameworkElement;
                ColorPanel.CaptureMouse();
                UpdateColorFromMouse(e.GetPosition(ColorPanel));
            }
        }

        private void ColorPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isDragging && e.ChangedButton == MouseButton.Left)
            {
                UpdateColorFromMouse(e.GetPosition(ColorPanel));
            }
        }

        private void ColorPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                _isDragging = false;
                if (_dragTarget != null)
                {
                    _dragTarget.ReleaseMouseCapture();
                }
                _dragTarget = null;
            }
        }

        // 亮度滑块鼠标事件
        private void BrightnessSlider_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                _isDraggingBrightness = true;
                _dragTarget = sender as FrameworkElement;
                BrightnessSlider.CaptureMouse();
                UpdateBrightnessFromMouse(e.GetPosition(BrightnessSlider));
            }
        }

        private void BrightnessSlider_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isDraggingBrightness && e.ChangedButton == MouseButton.Left)
            {
                UpdateBrightnessFromMouse(e.GetPosition(BrightnessSlider));
            }
        }

        private void BrightnessSlider_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                _isDraggingBrightness = false;
                if (_dragTarget != null)
                {
                    _dragTarget.ReleaseMouseCapture();
                }
                _dragTarget = null;
            }
        }

        // 从鼠标位置更新颜色
        private void UpdateColorFromMouse(Point position)
        {
            Color color = GetColorFromPosition(position);
            _currentColor = UpdateBrightness(color, 1.0); // 先获取原始色彩
            UpdateColorDisplay();
        }

        // 从鼠标位置更新亮度
        private void UpdateBrightnessFromMouse(Point position)
        {
            // 亮度滑块的位置决定亮度值
            double relativeY = position.Y;
            double sliderHeight = BrightnessSlider.ActualHeight;
            
            // 亮度从顶部(1.0)到底部(0.0)
            double brightness = 1.0 - (relativeY / sliderHeight);
            brightness = Math.Max(0.0, Math.Min(1.0, brightness));
            
            // 获取当前基础颜色(从色彩面板选择的颜色)
            // 我们需要从当前选中的颜色中提取HSL,然后保持H和S不变,只改变L
            double h, s, l;
            byte r = _currentColor.R;
            byte g = _currentColor.G;
            byte b = _currentColor.B;
            RgbToHsl(r, g, b, out h, out s, out l);
            
            // 使用新的亮度值
            l = brightness;
            
            // 将HSL转换回RGB
            Color newColor = HslToRgb(h * 360.0, s, l);
            
            _currentColor = newColor;
            UpdateColorDisplay();
        }

        // 公共属性:当前选中的颜色
        public Color SelectedColor
        {
            get { return _currentColor; }
            set 
            { 
                _currentColor = value;
                UpdateColorDisplay();
            }
        }

        // 颜色改变事件
        public event EventHandler<ColorChangedEventArgs> ColorChanged;

        // 触发颜色改变事件
        protected virtual void OnColorChanged(Color color)
        {
            ColorChanged?.Invoke(this, new ColorChangedEventArgs(color));
        }
    }

    // 颜色改变事件参数
    public class ColorChangedEventArgs : EventArgs
    {
        public Color Color { get; }

        public ColorChangedEventArgs(Color color)
        {
            Color = color;
        }
    }
}

3. 在窗口中使用颜色拾取器

现在你可以在主窗口或其他地方使用这个颜色拾取器了。以下是如何在窗口中使用它的示例:

XAML (MainWindow.xaml)

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:YourNamespace"
        Title="颜色拾取器示例" Height="450" Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <local:ColorPicker x:Name="colorPicker" Grid.Row="0" Margin="10" ColorChanged="ColorPicker_ColorChanged"/>
        
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
            <TextBlock Text="当前选中颜色: " VerticalAlignment="Center" Margin="5"/>
            <Rectangle Width="50" Height="30" Margin="5" 
                       Fill="{Binding SelectedColor, ElementName=colorPicker}"/>
            <TextBlock Text="{Binding SelectedColor, ElementName=colorPicker, StringFormat='{}#{0:X2}{1:X2}{2:X2}'}" 
                       VerticalAlignment="Center" Margin="5"/>
        </StackPanel>
    </Grid>
</Window>

后台代码 (MainWindow.xaml.cs)

using System.Windows;

namespace YourNamespace
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void ColorPicker_ColorChanged(object sender, ColorChangedEventArgs e)
        {
            // 当颜色改变时触发
            var color = e.Color;
            // 可以在这里处理颜色改变的逻辑
            System.Diagnostics.Debug.WriteLine($"选中颜色: #{color.R:X2}{color.G:X2}{color.B:X2}");
        }
    }
}

4. 功能说明

这个自定义颜色拾取器具有以下功能:

  1. 色彩选择面板​:点击或拖拽选择基础颜色
  2. 亮度滑块​:调整所选颜色的亮度
  3. 实时预览​:右上角显示当前选中的颜色
  4. RGB数值输入​:可以直接输入RGB值来设置颜色
  5. 十六进制颜色代码​:支持输入或显示十六进制颜色代码
  6. 颜色改变事件​:当颜色改变时触发事件,方便在其他地方响应颜色变化

5. 扩展建议

如果想进一步增强这个颜色拾取器,可以考虑添加以下功能:

  1. HSV数值输入​:除了RGB,还可以添加色调、饱和度、亮度的数值输入
  2. 调色板​:提供常用颜色快速选择
  3. 最近使用颜色​:记录和显示最近选择的颜色
  4. 颜色历史​:保存颜色选择历史
  5. 更精确的色彩选择​:使用更复杂的算法生成色彩面板,提供更丰富的颜色选择

这个自定义颜色拾取器提供了一个良好的基础,可以根据具体需求进行扩展和定制。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞61 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容