基于WPF开发简单的颜色选择器

 更新时间:2023年07月25日 16:51:45   作者:WPF开发者  
这篇文章主要介绍了如何基于WPF实现简单的颜色选择器,文中的示例代码讲解详细,对我们学习或工作有一定帮助,需要的小伙伴可以参考一下

 WPF 简单实现颜色选择器

  • 框架使用.NET4
  • Visual Studio 2022;

实现代码

1)新增 xaml 代码如下:

  • 定义一个WriteableBitmap用于记录颜色缓冲值。
  • XAML中定义Canvas设置背景为一张图像。
  • Canvas中添加Thumb 是一个可拖动的控件,用于实现交互中的拖动后获取颜色。
<Canvas x:Name="canvas" MouseLeftButtonDown="canvas_MouseLeftButtonDown">
                <Canvas.Background>
                    <ImageBrush ImageSource="{Binding Bitmap}" />
                </Canvas.Background>
                <Thumb
                    x:Name="thumb"
                    Canvas.Left="0"
                    Canvas.Top="0"
                    Width="20"
                    Height="20"
                    Background="Transparent"
                    BorderBrush="Black"
                    BorderThickness="2"
                    DragDelta="Thumb_DragDelta">
                    <Thumb.Template>
                        <ControlTemplate TargetType="Thumb">
                            <Border
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                CornerRadius="10"
                                SnapsToDevicePixels="True" />
                        </ControlTemplate>
                    </Thumb.Template>
                </Thumb>
            </Canvas>

2)新增 Loaded逻辑 处理代码如下:

  • 首先嵌套循环,用于在图像的每个像素位置上进行操作色值。
  • height 和 width 是图像的高度和宽度。
  • 在外层循环中,变量 y 从 0 开始递增,直到小于 height
  • 在内层循环中,变量 x 从 0 开始递增,直到小于 width
  • 在每个像素位置上,通过计算 normalizedX 和 normalizedY,将 x 和 y 的值归一化到 [0, 1] 范围内。
  • 使用 HSVToRGB 函数将归一化后的 normalizedXnormalizedY 和 1(表示最大亮度)转换为 RGB 值,并将结果存储在 rg 和 b 变量中。
  • 计算像素在图像数据缓冲区中的偏移量 pixelOffset,其中 stride 是每行像素占用的字节数。
  • 使用 Marshal.WriteByte 方法将 RGB 值写入图像数据缓冲区中的相应位置,同时设置 Alpha 通道为 0xFF(完全不透明)。
            IntPtr backBuffer = Bitmap.BackBuffer;
            int stride = Bitmap.BackBufferStride;
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    byte r, g, b;
                    double normalizedX = (double)x / (width - 1);
                    double normalizedY = (double)y / (height - 1);
                    HSVToRGB(normalizedX, normalizedY, 1, out r, out g, out b);
                    int pixelOffset = y * stride + x * 4;
                    Marshal.WriteByte(backBuffer, pixelOffset + 0, b);
                    Marshal.WriteByte(backBuffer, pixelOffset + 1, g);
                    Marshal.WriteByte(backBuffer, pixelOffset + 2, r);
                    Marshal.WriteByte(backBuffer, pixelOffset + 3, 0xFF);
                }
            }

3)新增 HSVToRGB 方法代码如下:

  • HSVToRGB 方法返回 r 、g、b
  • h 是色相值,取值范围为 [0, 1]。
  • s 是饱和度值,取值范围为 [0, 1]。
  • v 是亮度值,取值范围为 [0, 1]。
  • rg 和 b 是输出参数,用于存储转换后的 RGB 值。
  • 在函数内部,根据 HSV 转换公式进行计算。如果饱和度 s 为 0,则表示灰度色调,此时将 RGB 的三个分量都设置为亮度 v 的值,并乘以 255 转换为字节表示。
  • 如果饱和度 s 不为 0,则根据色相 h 的值确定所处的色相区间,并根据公式计算出对应的 RGB 值。具体步骤如下:
  • 将色相 h 乘以 6,得到一个扩展的色相值 hue
  • 取 hue 的整数部分作为索引 i,表示所处的色相区间。
  • 计算 hue 的小数部分 f
  • 根据公式计算出对应的 RGB 值,其中 p 是亮度 v 与饱和度 s 的乘积,q 是亮度 v 与饱和度 s 以及 f 的乘积,t 是亮度 v 与饱和度 s 以及 (1.0 - f) 的乘积。
  • 根据索引 i 的值,将计算得到的 RGB 值赋给输出参数 rg 和 b
private static void HSVToRGB(double h, double s, double v, out byte r, out byte g, out byte b)
        {
            if (s == 0)
            {
                r = g = b = (byte)(v * 255);
            }
            else
            {
                double hue = h * 6.0;
                int i = (int)Math.Floor(hue);
                double f = hue - i;
                double p = v * (1.0 - s);
                double q = v * (1.0 - (s * f));
                double t = v * (1.0 - (s * (1.0 - f)));
                switch (i)
                {
                    case 0:
                        r = (byte)(v * 255);
                        g = (byte)(t * 255);
                        b = (byte)(p * 255);
                        break;
                    case 1:
                        r = (byte)(q * 255);
                        g = (byte)(v * 255);
                        b = (byte)(p * 255);
                        break;
                    case 2:
                        r = (byte)(p * 255);
                        g = (byte)(v * 255);
                        b = (byte)(t * 255);
                        break;
                    case 3:
                        r = (byte)(p * 255);
                        g = (byte)(q * 255);
                        b = (byte)(v * 255);
                        break;
                    case 4:
                        r = (byte)(t * 255);
                        g = (byte)(p * 255);
                        b = (byte)(v * 255);
                        break;
                    default:
                        r = (byte)(v * 255);
                        g = (byte)(p * 255);
                        b = (byte)(q * 255);
                        break;
                }
            }
        }

4)新增 Thumb_DragDelta 代码如下:

  • 在事件处理程序中,首先获取拖动的 Thumb 控件,并计算出新的左侧和顶部位置。通过 Canvas.GetLeft(thumb) 和 Canvas.GetTop(thumb) 方法获取当前 Thumb 控件在 Canvas 中的左侧和顶部位置,然后将其与拖动的变化量 e.HorizontalChange 和 e.VerticalChange 相加,得到新的位置。
  • 计算 Canvas 的右侧和底部边界。通过 canvas.ActualWidth - thumb.ActualWidth 和 canvas.ActualHeight - thumb.ActualHeight 计算出 Canvas 的右侧和底部边界位置。
  • 对新的左侧和顶部位置进行边界检查。如果新的左侧位置小于 0,则将其设置为 0,以保证 Thumb 控件不会超出 Canvas 的左侧边界。如果新的左侧位置大于 Canvas 的右侧边界位置 canvasRight,则将其设置为 canvasRight,以确保 Thumb 控件不会超出 Canvas 的右侧边界。类似地,对新的顶部位置进行边界检查。
  • 通过 Canvas.SetLeft(thumb, newLeft) 和 Canvas.SetTop(thumb, newTop) 将 Thumb 控件的位置更新为新的左侧和顶部位置。
  • 调用 GetAreaColor() 方法来获取更新后的区域颜色。
 private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var thumb = (Thumb)sender;
            double newLeft = Canvas.GetLeft(thumb) + e.HorizontalChange;
            double newTop = Canvas.GetTop(thumb) + e.VerticalChange;
            double canvasRight = canvas.ActualWidth - thumb.ActualWidth;
            double canvasBottom = canvas.ActualHeight - thumb.ActualHeight;
            if (newLeft < 0)
                newLeft = 0;
            else if (newLeft > canvasRight)
                newLeft = canvasRight;
            if (newTop < 0)
                newTop = 0;
            else if (newTop > canvasBottom)
                newTop = canvasBottom;
            Canvas.SetLeft(thumb, newLeft);
            Canvas.SetTop(thumb, newTop);
            GetAreaColor();
        }

5)新增 canvas_MouseLeftButtonDown 代码如下:

实现鼠标左键按下时,将 Thumb 控件移动到鼠标点击位置,并进行边界限制。同时,还获取了鼠标点击位置的颜色信息

 private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var canvasPosition = e.GetPosition(canvas);
            double newLeft = canvasPosition.X - thumb.ActualWidth / 2;
            double newTop = canvasPosition.Y - thumb.ActualHeight / 2;
            double canvasRight = canvas.ActualWidth - thumb.ActualWidth;
            double canvasBottom = canvas.ActualHeight - thumb.ActualHeight;
            if (newLeft < 0)
                newLeft = 0;
            else if (newLeft > canvasRight)
                newLeft = canvasRight;
            if (newTop < 0)
                newTop = 0;
            else if (newTop > canvasBottom)
                newTop = canvasBottom;
            Canvas.SetLeft(thumb, newLeft);
            Canvas.SetTop(thumb, newTop);
            var thumbPosition = e.GetPosition(canvas);
            GetAreaColor(thumbPosition);
        }

6)新增 GetAreaColor 代码如下:

Thumb 控件的中心点坐标转换为相对于 Canvas 的坐标。

计算每行像素数据所占的字节数。

创建一个字节数组,用于存储位图的像素数据。

将位图的像素数据复制到字节数组中。

计算要访问的像素在字节数组中的索引位置。

Color.FromArgb取其 Alpha、红色、绿色和蓝色通道的值。在这段代码中,它被用于构造一个 Color 对象,表示位图中特定像素的颜色。

  • pixels[pixelIndex + 3] 表示字节数组中的第 pixelIndex + 3 个元素,即 Alpha 通道的值。
  • pixels[pixelIndex + 2] 表示字节数组中的第 pixelIndex + 2 个元素,即红色通道的值。
  • pixels[pixelIndex + 1] 表示字节数组中的第 pixelIndex + 1 个元素,即绿色通道的值。
  • pixels[pixelIndex] 表示字节数组中的第 pixelIndex 个元素,即蓝色通道的值。
 void GetAreaColor(Point? thumbPosition = null)
        {
            thumbPosition = thumbPosition == null ? thumbPosition = thumb.TranslatePoint(new Point(thumb.ActualWidth / 2, thumb.ActualHeight / 2), canvas) : thumbPosition;
            int xCoordinate = (int)thumbPosition?.X;
            int yCoordinate = (int)thumbPosition?.Y;
            if (xCoordinate >= 0 && xCoordinate < Bitmap.PixelWidth && yCoordinate >= 0 && yCoordinate < Bitmap.PixelHeight)
            {
                int stride = Bitmap.PixelWidth * (Bitmap.Format.BitsPerPixel / 8);
                byte[] pixels = new byte[Bitmap.PixelHeight * stride];
                Bitmap.CopyPixels(new Int32Rect(0, 0, Bitmap.PixelWidth, Bitmap.PixelHeight), pixels, stride, 0);
                int pixelIndex = (yCoordinate * stride) + (xCoordinate * (Bitmap.Format.BitsPerPixel / 8));
                Color color = Color.FromArgb(pixels[pixelIndex + 3], pixels[pixelIndex + 2], pixels[pixelIndex + 1], pixels[pixelIndex]);
                MyBtn.Background = new SolidColorBrush(color);
            }
        }

效果图

到此这篇关于基于WPF开发简单的颜色选择器的文章就介绍到这了,更多相关WPF颜色选择器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C# 从枚举值获取对应的文本描述详解

    C# 从枚举值获取对应的文本描述详解

    这篇文章主要介绍了C# 从枚举值获取对应的文本描述详解的相关资料,需要的朋友可以参考下
    2017-01-01
  • C#五类运算符使用表达式树进行操作

    C#五类运算符使用表达式树进行操作

    这篇文章介绍了C#五类运算符使用表达式树进行操作,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-01-01
  • C#控制台带参数程序源码编写实例讲解

    C#控制台带参数程序源码编写实例讲解

    像ipconfig /all 这样的CMD命令想必大家都知道,但是很多童鞋可能不知道怎么写这样的控制台带参数的程序,需要的朋友可以了解下
    2012-12-12
  • C# 解决datagridview控件显示大量数据拖拉卡顿问题

    C# 解决datagridview控件显示大量数据拖拉卡顿问题

    这篇文章主要介绍了C# 解决datagridview控件显示大量数据拖拉卡顿问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • C#绘制饼状图和柱状图的方法

    C#绘制饼状图和柱状图的方法

    这篇文章主要为大家详细介绍了C#绘制饼状图和柱状图的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • C#实现字符串转换成字节数组的简单实现方法

    C#实现字符串转换成字节数组的简单实现方法

    这篇文章主要介绍了C#实现字符串转换成字节数组的简单实现方法,仅一行代码即可搞定,非常简单实用,需要的朋友可以参考下
    2015-05-05
  • 通俗易懂的C#之反射教程

    通俗易懂的C#之反射教程

    这篇文章主要介绍了通俗易懂的C#之反射教程,本文深入分析了反射内部的原理,剖析了内部属性、方法的运作,并用通俗易懂的语言阐述,需要的朋友可以参考下
    2015-01-01
  • C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法

    C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法

    这篇文章主要介绍了C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法,本文分别给出了使用微软语言包、手动编码实现两种实现方式,需要的朋友可以参考下
    2015-01-01
  • C#简单生成随机密码的方法示例

    C#简单生成随机密码的方法示例

    这篇文章主要介绍了C#简单生成随机密码的方法,结合具体实例形式分析了C#生成随机密码操作的前台界面与后台处理技巧,需要的朋友可以参考下
    2017-06-06
  • C#实现最完整的文件和目录操作类实例

    C#实现最完整的文件和目录操作类实例

    这篇文章主要介绍了C#实现最完整的文件和目录操作类,涉及C#针对文件与目录的创建、获取、检测、删除等常用操作技巧,非常具有实用价值,需要的朋友可以参考下
    2015-05-05

最新评论