WPF 簡單實作顔色選擇器
控件名:ColorPicker
作 者:WPFDevelopersOrg - 驚鏵
原文連結[1]:https://github.com/yanjinhuagood/ColorPickerSample
- 架構使用
;.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
- 在外層循環中,變量
從 0 開始遞增,直到小于y
。height
- 在内層循環中,變量
從 0 開始遞增,直到小于x
。width
- 在每個像素位置上,通過計算
和normalizedX
,将normalizedY
和x
的值歸一化到 [0, 1] 範圍内。y
- 使用
函數将歸一化後的HSVToRGB
、normalizedX
和 1(表示最大亮度)轉換為 RGB 值,并将結果存儲在normalizedY
、r
和g
變量中。b
- 計算像素在圖像資料緩沖區中的偏移量
,其中pixelOffset
是每行像素占用的位元組數。stride
- 使用
方法将 RGB 值寫入圖像資料緩沖區中的相應位置,同時設定 Alpha 通道為 0xFF(完全不透明)。Marshal.WriteByte
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
-
是色相值,取值範圍為 [0, 1]。h
-
是飽和度值,取值範圍為 [0, 1]。s
-
是亮度值,取值範圍為 [0, 1]。v
-
、r
和g
是輸出參數,用于存儲轉換後的 RGB 值。b
- 在函數内部,根據 HSV 轉換公式進行計算。如果飽和度
為 0,則表示灰階色調,此時将 RGB 的三個分量都設定為亮度s
的值,并乘以 255 轉換為位元組表示。v
- 如果飽和度
不為 0,則根據色相s
的值确定所處的色相區間,并根據公式計算出對應的 RGB 值。具體步驟如下:h
- 将色相
乘以 6,得到一個擴充的色相值h
。hue
- 取
的整數部分作為索引hue
,表示所處的色相區間。i
- 計算
的小數部分hue
。f
- 根據公式計算出對應的 RGB 值,其中
是亮度p
與飽和度v
的乘積,s
是亮度q
與飽和度v
以及s
的乘積,f
是亮度t
與飽和度v
以及s
的乘積。(1.0 - f)
- 根據索引
的值,将計算得到的 RGB 值賦給輸出參數i
、r
和g
。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);
}
5)新增
GetAreaColor
代碼如下:
-
控件的中心點坐标轉換為相對于Thumb
的坐标。Canvas
- 計算每行像素資料所占的位元組數。
- 建立一個位元組數組,用于存儲位圖的像素資料。
- 将位圖的像素資料複制到位元組數組中。
- 計算要通路的像素在位元組數組中的索引位置。
-
取其Color.FromArgb
、紅色、綠色和藍色通道的值。在這段代碼中,它被用于構造一個Alpha
對象,表示位圖中特定像素的顔色。Color
-
表示位元組數組中的第pixels[pixelIndex + 3]
個元素,即 Alpha 通道的值。pixelIndex + 3
-
表示位元組數組中的第pixels[pixelIndex + 2]
個元素,即紅色通道的值。pixelIndex + 2
-
表示位元組數組中的第pixels[pixelIndex + 1]
個元素,即綠色通道的值。pixelIndex + 1
-
表示位元組數組中的第pixels[pixelIndex]
個元素,即藍色通道的值。pixelIndex
void GetAreaColor(Point? thumbPosition = )
{
thumbPosition = thumbPosition == ? 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);
}
}
碼雲[2]
參考資料
[1]
原文連結: https://github.com/yanjinhuagood/ColorPickerSample
[2]
碼雲: https://gitee.com/yanjinhua/ColorPickerSample