基于WPF開發(fā)簡單的顏色選擇器
WPF 簡單實現(xiàn)顏色選擇器
- 框架使用
.NET4
; Visual Studio 2022
;
實現(xiàn)代碼
1)新增 xaml
代碼如下:
- 定義一個
WriteableBitmap
用于記錄顏色緩沖值。 - 在
XAML
中定義Canvas
設置背景為一張圖像。 - 在
Canvas
中添加Thumb
是一個可拖動的控件,用于實現(xiàn)交互中的拖動后獲取顏色。
<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邏輯
處理代碼如下:
- 首先嵌套循環(huán),用于在圖像的每個像素位置上進行操作色值。
height
和width
是圖像的高度和寬度。- 在外層循環(huán)中,變量
y
從 0 開始遞增,直到小于height
。 - 在內層循環(huán)中,變量
x
從 0 開始遞增,直到小于width
。 - 在每個像素位置上,通過計算
normalizedX
和normalizedY
,將x
和y
的值歸一化到 [0, 1] 范圍內。 - 使用
HSVToRGB
函數(shù)將歸一化后的normalizedX
、normalizedY
和 1(表示最大亮度)轉換為 RGB 值,并將結果存儲在r
、g
和b
變量中。 - 計算像素在圖像數(shù)據(jù)緩沖區(qū)中的偏移量
pixelOffset
,其中stride
是每行像素占用的字節(jié)數(shù)。 - 使用
Marshal.WriteByte
方法將 RGB 值寫入圖像數(shù)據(jù)緩沖區(qū)中的相應位置,同時設置 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]。r
、g
和b
是輸出參數(shù),用于存儲轉換后的 RGB 值。- 在函數(shù)內部,根據(jù) HSV 轉換公式進行計算。如果飽和度
s
為 0,則表示灰度色調,此時將 RGB 的三個分量都設置為亮度v
的值,并乘以 255 轉換為字節(jié)表示。 - 如果飽和度
s
不為 0,則根據(jù)色相h
的值確定所處的色相區(qū)間,并根據(jù)公式計算出對應的 RGB 值。具體步驟如下: - 將色相
h
乘以 6,得到一個擴展的色相值hue
。 - 取
hue
的整數(shù)部分作為索引i
,表示所處的色相區(qū)間。 - 計算
hue
的小數(shù)部分f
。 - 根據(jù)公式計算出對應的 RGB 值,其中
p
是亮度v
與飽和度s
的乘積,q
是亮度v
與飽和度s
以及f
的乘積,t
是亮度v
與飽和度s
以及(1.0 - f)
的乘積。 - 根據(jù)索引
i
的值,將計算得到的 RGB 值賦給輸出參數(shù)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()
方法來獲取更新后的區(qū)域顏色。
?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
代碼如下:
實現(xiàn)鼠標左鍵按下時,將 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
的坐標。
計算每行像素數(shù)據(jù)所占的字節(jié)數(shù)。
創(chuàng)建一個字節(jié)數(shù)組,用于存儲位圖的像素數(shù)據(jù)。
將位圖的像素數(shù)據(jù)復制到字節(jié)數(shù)組中。
計算要訪問的像素在字節(jié)數(shù)組中的索引位置。
Color.FromArgb
取其 Alpha
、紅色、綠色和藍色通道的值。在這段代碼中,它被用于構造一個 Color
對象,表示位圖中特定像素的顏色。
pixels[pixelIndex + 3]
表示字節(jié)數(shù)組中的第pixelIndex + 3
個元素,即 Alpha 通道的值。pixels[pixelIndex + 2]
表示字節(jié)數(shù)組中的第pixelIndex + 2
個元素,即紅色通道的值。pixels[pixelIndex + 1]
表示字節(jié)數(shù)組中的第pixelIndex + 1
個元素,即綠色通道的值。pixels[pixelIndex]
表示字節(jié)數(shù)組中的第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開發(fā)簡單的顏色選擇器的文章就介紹到這了,更多相關WPF顏色選擇器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C# HttpClient Post參數(shù)同時上傳文件的實現(xiàn)
這篇文章主要介紹了C# HttpClient Post參數(shù)同時上傳文件的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06