基于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#正则表达式(Regex类)用法实例总结

    C#正则表达式(Regex类)用法实例总结

    正则表达式的主要作用是验证字符串的值是否满足一定的规则,在页面输入数据验证方面的应用比较多,下面这篇文章主要给大家介绍了关于C#正则表达式(Regex类)用法的相关资料,需要的朋友可以参考下
    2022-08-08
  • C#使用zxing/zbar/thoughtworkQRcode解析二维码的示例代码

    C#使用zxing/zbar/thoughtworkQRcode解析二维码的示例代码

    zxing是谷歌开源的二维码库,zbar,thoughtworkQRcode也是开源的,三者之间比较各有优劣,本文将通过一个案例demo源码,带来认识学习下这三者的实际解码效果,感兴趣的可以了解一下
    2023-07-07
  • C#实现Log4Net日志分类和自动维护实例

    C#实现Log4Net日志分类和自动维护实例

    这篇文章主要介绍了C#实现Log4Net日志分类和自动维护,实例讲述了Log4Net日志分类和自动维护的实现方法,并提供了完整的实例供大家参考学习,需要的朋友可以参考下
    2014-10-10
  • C#实现五子棋游戏

    C#实现五子棋游戏

    这篇文章主要为大家详细介绍了C#实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • 详解C#中使用对象或集合的初始值设定项初始化的操作

    详解C#中使用对象或集合的初始值设定项初始化的操作

    这篇文章主要介绍了详解C#中使用对象或集合的初始值设定项初始化的操作,文中分别讲了对对象和字典的初始化,需要的朋友可以参考下
    2016-01-01
  • C#中对象状态模式教程示例

    C#中对象状态模式教程示例

    这篇文章主要为大家介绍了C#中对象状态模式的教程示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • C#使用GDI画圆的方法

    C#使用GDI画圆的方法

    这篇文章主要介绍了C#使用GDI画圆的方法,涉及C#使用GDI绘图的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-04-04
  • WinForm调用百度地图接口用法示例

    WinForm调用百度地图接口用法示例

    这篇文章主要介绍了WinForm调用百度地图接口用法,结合具体实例形式简单分析了WinForm WebBrower控件与前端百度接口交互的相关操作技巧,需要的朋友可以参考下
    2017-06-06
  • C# Winform实现石头剪刀布游戏

    C# Winform实现石头剪刀布游戏

    这篇文章主要为大家详细介绍了Winform实现石头剪刀布游戏,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • C#操作本地文件及保存文件到数据库的基本方法总结

    C#操作本地文件及保存文件到数据库的基本方法总结

    C#使用System.IO中的文件操作方法在Windows系统中处理本地文件相当顺手,这里我们还总结了在Oracle中保存文件的方法,嗯,接下来就来看看整理的C#操作本地文件及保存文件到数据库的基本方法总结
    2016-05-05

最新评论