在进行项目开发时,经常需要对颜色进行选择和编辑。市面上有许多优秀的颜色选择器控件,但为了更好地满足特定需求,决定尝试自己实现一个。本文将介绍是如何创建一个基于MVVM模式的颜色编辑器,并解决在实现过程中遇到的一些技术难题。
这个项目是基于MVVM模式的,包含了两个用户控件:ColorEditor和BrushEditor。为了方便使用,它们被打包在一个单独的类库项目中。
在生成颜色选择器时,遇到了第一个难题。使用WriteableBitmap生成颜色样本非常慢,因此决定缓存100种色调样本(从0到1,以1%的增量)。通过在WriteableBitmap的社区内容部分找到的SimpleBitmap类,生成位图的过程变得更加简单。
接下来,需要重新模板化Hue和Alpha的滑块,这是之前没有做过的。在尝试了几个ControlTemplates之后,采用了KAXAML的Simple Styles风格,并对其进行了调整。Hue滑块的背景仅仅是一个带有LinearGradientBrush和7个GradientStops的Border。
Alpha滑块和选定颜色框的实现稍微有些棘手,必须承认并不完全理解为什么显示棋盘格图案的DrawingBrush能够工作。
令惊讶的是,实现RGBA/HSLA文本框实际上是最棘手的部分。问题在于,如果更新了R值,还需要更新H、S和L。如果更新了H,就需要更新R,如果更新了S,也需要更新R。这本身可能不是问题,但RGB到HSL的转换代码并不完美。例如,HSL(0.56, .60, .23)可能不会完全对应一个RGB值。因此,它会四舍五入RGB值。但也许这种四舍五入会稍微改变H值。所以改变了RGB值,这又稍微扭曲了H值。最后,简单地说,如果改变了H值,设置一个标志,更新RGB值,当它们尝试更新HSL值时,检查那个标志是否设置。如果设置了,就不要更新。事后看来很简单。
这些编辑器的使用就像使用任何其他UserControl一样。
XAML代码示例:
<Window x:Class="BrushEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ColorEditor="clr-namespace:Lovatts.ColorEditor;assembly=Lovatts.ColorEditor"
Title="MainWindow" Height="350" Width="525">
<ColorEditor:BrushEditor />
</Window>
也可以绑定/使用底层的ViewModels:
ColorEditor colorEditor = new ColorEditor();
colorEditor.ColorEditorViewModel.Color = Colors.Blue;
colorEditor.ColorEditorViewModel.RGB.A = 67;
Lovatts.ColorEditor.BrushEditor brushEditor = new Lovatts.ColorEditor.BrushEditor();
brushEditor.BrushEditorViewModel.BrushType = BrushTypes.Radial;
brushEditor.BrushEditorViewModel.Center = new Point(1, 0);
brushEditor.BrushEditorViewModel.GradientStops.Clear();
brushEditor.BrushEditorViewModel.GradientStops.Add(new GradientStopViewModel(Colors.Blue, 1));
brushEditor.BrushEditorViewModel.GradientStops.Add(new GradientStopViewModel(Colors.Red, 0));
为了方便起见,包含了Brush的序列化/反序列化方法。
string xml = brushEditor.BrushEditorViewModel.SerializeBrushToXml();
brushEditor.BrushEditorViewModel.DeserializeBrushFromXml(xml);
XML输出示例:
<RadialGradientBrush Center="1,0" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<RadialGradientBrush.GradientStops>
<GradientStop Color="#FFFF0000" Offset="0" />
<GradientStop Color="#FF0000FF" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
这是第一次在CodeProject上提交代码,大约花了一天时间编写。
在更改色调滑块时,有一个小问题,它会有时将调色板滑块拇指固定到一个RGB值。
十六进制代码文本框被自动设置为OverType(使用以下方法),发现这更加用户友好:
private void MakeHexTextBoxOverType() {
PropertyInfo textEditorProperty = typeof(TextBox).GetProperty("TextEditor", BindingFlags.NonPublic | BindingFlags.Instance);
object textEditor = textEditorProperty.GetValue(hexTextBox, null);
// set _OvertypeMode on the TextEditor
PropertyInfo overtypeModeProperty = textEditor.GetType().GetProperty("_OvertypeMode", BindingFlags.NonPublic | BindingFlags.Instance);
overtypeModeProperty.SetValue(textEditor, true, null);
}
2012年6月19日:发布文章。