演练:实现颜色编辑器
使用 适用于 Visual Studio 的 WPF 设计器的扩展性模型,可以在设计时,在“属性”窗口中为属性创建自定义值编辑器。 编辑器可以是内联编辑器(允许您在“属性”窗口中直接编辑值),也可以是扩展编辑器(允许您提供用来在“属性”窗口外部编辑属性的其他 UI)。 为了演示如何创建扩展编辑器,本演练提供了为控件的 Background 属性创建颜色编辑器的分步过程。这个扩展编辑器是在“属性”窗口中从内联编辑器打开的。
在本演练中,您将执行下列任务:
创建 WPF 自定义控件项目。
创建一个将充当扩展编辑器的用户控件。
创建一个可用来在“属性”窗口中编辑属性值并用来打开扩展编辑器的内联编辑器。
创建一个继承自 ExtendedPropertyValueEditor 的类,该类用来将编辑器连接到要为其提供自定义编辑的类。
创建一个用来实现 IProvideAttributeTable 接口的类,以注册新的扩展编辑器。
在设计时测试扩展编辑器。
系统必备
您需要以下组件来完成本演练:
- Visual Studio 2010.
创建自定义控件
第一步是为自定义控件创建项目。 该控件是一个带有少量设计时代码的简单按钮,该按钮使用 GetIsInDesignMode 方法来实现设计时行为。
创建自定义控件
使用 Visual C# 创建一个名为 CustomControlLibrary 的新 WPF 自定义控件库项目。
CustomControl1 的代码在“代码编辑器”中打开。
在 CustomControl1 的代码编辑器中,用下面的代码替换 CustomControlLibrary 命名空间中的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace CustomControlLibrary { public class CustomControl1 : Button { public CustomControl1() { if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) { Background = Brushes.Red; } } } }
将项目的输出路径设置为“bin\”。
生成解决方案。
为扩展编辑器创建用户控件
在上一个过程中创建的控件是自定义颜色编辑器将要附加到的控件。 在该过程中,将创建另一个要充当扩展编辑器的用户控件。
创建将充当扩展编辑器的用户控件
使用 Visual C# 向解决方案中添加一个名为 CustomControlLibrary.Design 的新 WPF 自定义控件库项目。
CustomControl1 的代码在“代码编辑器”中打开。
在**“解决方案资源管理器”**中,将 CustomControl1 文件从 CustomControlLibrary.Design 项目中删除。
添加对以下 WPF 设计器程序集的引用。
Microsoft.Windows.Design.Extensibility
Microsoft.Windows.Design.Interaction
添加对 CustomControlLibrary 项目的引用。
将项目的输出路径设置为“.. \CustomControlLibrary\bin\”。 这样可使控件的程序集与元数据程序集位于同一文件夹中,从而可为设计器启用元数据发现。
向 CustomControlLibrary.Design 项目中添加一个名为 ColorsList 的新类。
在 ColorsList 的代码编辑器中,用下面的代码替换自动生成的代码。
using System; using System.Linq; using System.Collections.Generic; using System.Text; using System.Windows.Media; using System.Reflection; using System.Collections.ObjectModel; namespace CustomControlLibrary.Design { public class ColorsList : ObservableCollection<Color> { public ColorsList() { Type type = typeof(Colors); foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Static)) { if (propertyInfo.PropertyType == typeof(Color)) { Add((Color)propertyInfo.GetValue(null, null)); } } } } }
向 CustomControlLibrary.Design 项目中添加一个名为 ColorsListControl 的新用户控件 (WPF)。
ColorsListControl.xaml 的代码将在设计器中打开。
在 ColorsListControl.xaml 的 XAML 视图中,用下面的 XAML 替换自动生成的 XAML。
<UserControl x:Class="CustomControlLibrary.Design.ColorsListControl" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:Local="clr-namespace:CustomControlLibrary.Design" xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design.Interaction" Height="184" Width="260" Background="White"> <UserControl.Resources> <Local:ColorsList x:Key="colors"/> <Style TargetType="{x:Type Button}"> <EventSetter Event="Click" Handler="ItemsControl_Click"/> </Style> </UserControl.Resources> <ItemsControl ItemsSource="{Binding Source={StaticResource colors}}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <ItemsControl.Template> <ControlTemplate TargetType="ItemsControl"> <Border CornerRadius="5" > <WrapPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"> <ScrollViewer> <ItemsPresenter/> </ScrollViewer> </WrapPanel> </Border> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Button Tag="{Binding}" Command="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowInlineEditor}"> <Button.Template> <ControlTemplate> <Border Width="30" Height="30" BorderBrush="Black" BorderThickness="1" CornerRadius="5"> <Rectangle Width="22" Height="22" ToolTip="{Binding}"> <Rectangle.Fill> <SolidColorBrush Color="{Binding}"/> </Rectangle.Fill> </Rectangle> </Border> </ControlTemplate> </Button.Template> </Button> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </UserControl>
在**“解决方案资源管理器”**中,展开 ColorsListControl.xaml 并打开 ColorsListControl.xaml.cs。
在 ColorsListControl 的代码编辑器中,用下面的代码替换自动生成的代码。
using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace CustomControlLibrary.Design { public partial class ColorsListControl : System.Windows.Controls.UserControl { public ColorsListControl() { InitializeComponent(); } public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorsListControl), new FrameworkPropertyMetadata(null)); public Color SelectedColor { get { return (Color)base.GetValue(SelectedColorProperty); } set { base.SetValue(SelectedColorProperty, value); } } public static readonly DependencyProperty SelectedBrushProperty = DependencyProperty.Register("SelectedBrush", typeof(SolidColorBrush), typeof(ColorsListControl), new FrameworkPropertyMetadata(null)); public SolidColorBrush SelectedBrush { get { return (SolidColorBrush)base.GetValue(SelectedBrushProperty); } set { base.SetValue(SelectedBrushProperty, value); } } private void ItemsControl_Click(object sender, RoutedEventArgs e) { SelectedColor = (Color)((Button)sender).Tag; SelectedBrush = new SolidColorBrush(SelectedColor); } } }
生成解决方案。
在设计器中重新加载 ColorsListControl.xaml,此时应当会在设计视图中看到扩展编辑器 UI。
为颜色编辑器创建模板
颜色编辑器的内联编辑器不如扩展编辑器复杂,可以用 XAML 数据模板创建。 还将为扩展编辑器创建一个数据模板,指定要使用在上一个过程中创建的用户控件。
为颜色编辑器创建模板
向 CustomControlLibrary.Design 项目中添加一个名为 EditorResources 的新类。
在 EditorResources 的代码编辑器中,用下面的代码替换自动生成的代码。
namespace ExtendedEditorNamespace { using System; using System.Collections.Generic; using System.Text; using System.Windows; public partial class EditorResources : ResourceDictionary { public EditorResources() : base() { InitializeComponent(); } } }
在**“项目”菜单中,单击“添加资源字典”**。
将该文件命名为 EditorResources.xaml 并单击**“添加”**。
EditorResources.xaml 的代码将在设计器中打开。
在 EditorResources.xaml 的 XAML 视图中,用下面的 XAML 替换自动生成的 XAML。
<ResourceDictionary xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design.Interaction" xmlns:Local="clr-namespace:CustomControlLibrary.Design" xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore" xmlns:sys="clr-namespace:System;assembly=mscorlib" x:Class="CustomControlLibrary.Design.EditorResources"> <DataTemplate x:Key="BrushExtendedEditorTemplate"> <Local:ColorsListControl SelectedBrush="{Binding Value, Mode=TwoWay}"/> </DataTemplate> <DataTemplate x:Key="BrushInlineEditorTemplate"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBox Grid.Column="0" Text="{Binding StringValue}"/> <PropertyEditing:EditModeSwitchButton Grid.Column="1"/> </Grid> </DataTemplate> </ResourceDictionary>
生成解决方案。
封装模板并注册编辑器
繁琐的工作已经完成了。 既然已经为颜色编辑器创建了扩展编辑器和内联编辑器,可以创建一个类来封装它们并向自定义控件注册它们了。
封装并注册编辑器
向 CustomControlLibrary.Design 项目中添加一个名为 BrushExtendedEditor 的新类。
在 BrushExtendedEditor 的代码编辑器中,用下面的代码替换自动生成的代码。
using System; using System.Collections.Generic; using System.Text; using Microsoft.Windows.Design.PropertyEditing; using System.Windows; namespace CustomControlLibrary.Design { public class BrushExtendedEditor : ExtendedPropertyValueEditor { private EditorResources res = new EditorResources(); public BrushExtendedEditor() { this.ExtendedEditorTemplate = res["BrushExtendedEditorTemplate"] as DataTemplate; this.InlineEditorTemplate = res["BrushInlineEditorTemplate"] as DataTemplate; } } }
向 CustomControlLibrary.Design 项目中添加一个名为 Metadata 的新类。
在 Metadata 的代码编辑器中,用下面的代码替换自动生成的代码。
using System; using System.Collections.Generic; using System.Text; using Microsoft.Windows.Design.Metadata; using System.ComponentModel; using Microsoft.Windows.Design.PropertyEditing; using System.Windows.Media; using System.Windows.Controls; using System.Windows; using CustomControlLibrary; // The ProvideMetadata assembly-level attribute indicates to designers // that this assembly contains a class that provides an attribute table. [assembly: ProvideMetadata(typeof(CustomControlLibrary.Design.Metadata))] namespace CustomControlLibrary.Design { // Container for any general design-time metadata to initialize. // Designers look for a type in the design-time assembly that // implements IProvideAttributeTable. If found, designers instantiate // this class and access its AttributeTable property automatically. internal class Metadata : IProvideAttributeTable { // Accessed by the designer to register any design-time metadata. public AttributeTable AttributeTable { get { AttributeTableBuilder builder = new AttributeTableBuilder(); builder.AddCustomAttributes (typeof(CustomControl1), "Background", PropertyValueEditor.CreateEditorAttribute( typeof(BrushExtendedEditor))); return builder.CreateTable(); } } } }
生成解决方案。
测试颜色编辑器
您刚才完成了颜色编辑器的创建。 现在剩下的工作就是对其进行测试。 为了测试颜色编辑器,将向解决方案中添加一个 WPF 应用程序项目,并添加您的自定义控件, 然后在“属性”窗口中更改 Background 属性。您将在操作中看到新编辑器。
测试颜色编辑器
使用 Visual C# 向解决方案中添加一个名为 DemoApplication 的新 WPF 应用程序项目。
MainWindow.xaml 将在 WPF 设计器中打开。
添加对 CustomControlLibrary 项目的引用。
在 MainWindow.xaml 的 XAML 视图中,用下面的 XAML 替换自动生成的 XAML。 此 XAML 将添加对 CustomControlLibrary 命名空间的引用并添加 CustomControl1 自定义控件。 该按钮将出现在设计视图中而且将具有红色背景,这表明该控件处于设计模式下。 如果该按钮未出现,则可能需要单击设计器顶部的信息栏以重新加载视图。
<Window x:Class="DemoApplication.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" xmlns:my="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary"> <Grid> <my:CustomControl1 Margin="30,30,30,30" Name="customControl11"></my:CustomControl1> </Grid> </Window>
在设计器视图中,选择该控件。
在**“属性”窗口中,单击“Background”**属性旁边的下拉按钮。 将显示一个可视化颜色编辑器,而不是默认的颜色列表。
从编辑器中选择一种颜色。 自定义控件的背景色将更改为所选颜色。