演练:创建菜单提供程序
本演练演示如何为 Windows Presentation Foundation (WPF) 自定义控件创建设计时菜单提供程序。 可以使用此快捷菜单项在自定义按钮控件上设置 Background 属性的值。 有关完整的代码清单,请参见 WPF Designer Extensibility Samples(WPF 设计器扩展性示例)站点上的“上下文菜单提供程序”示例。
在本演练中,您将执行下列任务:
创建一个 WPF 自定义控件库项目。
为设计时元数据创建一个单独的程序集。
实现菜单提供程序。
在设计时使用控件。
完成本演练后,您将知道如何为自定义控件创建菜单提供程序。
提示
显示的对话框和菜单命令可能会与“帮助”中的描述不同,具体取决于您现用的设置或版本。 若要更改设置,请在“工具”菜单上选择“导入和导出设置”。 有关更多信息,请参见 使用设置。
系统必备
您需要以下组件来完成本演练:
- Visual Studio 2010.
创建自定义控件
第一步是为自定义控件创建项目。 该控件是一个带有少量设计时代码的简单按钮,该按钮使用 GetIsInDesignMode 方法来实现设计时行为。
创建自定义控件
使用 Visual Basic 或 Visual C# 创建一个名为 CustomControlLibrary 的新 WPF 自定义控件库项目。
CustomControl1 的代码在“代码编辑器”中打开。
在**“解决方案资源管理器”中,将代码文件的名称更改为 ButtonWithDesignTime.cs 或 ButtonWithDesignTime.vb。 如果出现消息框,询问是否对此项目中的所有引用执行重命名操作,请单击“是”**。
在“代码编辑器”中打开 ButtonWithDesignTime.cs 或 ButtonWithDesignTime.vb。
用下面的代码替换自动生成的代码。 此代码从 Button 继承而来,当按钮出现在设计器中时,将显示文本“已激活设计模式”。
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Windows.Controls Imports System.Windows.Media Imports System.ComponentModel Public Class ButtonWithDesignTime Inherits Button Public Sub New() ' The GetIsInDesignMode check and the following design-time ' code are optional and shown only for demonstration. If DesignerProperties.GetIsInDesignMode(Me) Then Content = "Design mode active" End If End Sub End Class
using System; using System.Collections.Generic; using System.Text; using System.Windows.Controls; using System.Windows.Media; using System.ComponentModel; namespace CustomControlLibrary { public class ButtonWithDesignTime : Button { public ButtonWithDesignTime() { // The GetIsInDesignMode check and the following design-time // code are optional and shown only for demonstration. if (DesignerProperties.GetIsInDesignMode(this)) { Content = "Design mode active"; } } } }
将项目的输出路径设置为“bin\”。
生成解决方案。
创建设计时元数据程序集
设计时代码在特定元数据程序集中部署。 对于本演练,上下文菜单实现在一个名为 CustomControlLibrary.VisualStudio.Design 的程序集中部署。 有关更多信息,请参见提供设计时元数据。
创建设计时元数据程序集
使用 Visual Basic 或 Visual C# 为解决方案添加一个名为 CustomControlLibrary.VisualStudio.Design 的新类库项目。
将项目的输出路径设置为“.. \CustomControlLibrary\bin\”。 这样可使控件的程序集与元数据程序集位于同一文件夹中,从而可为设计器启用元数据发现。
添加对下列 WPF 程序集的引用。
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
添加对下列 WPF 设计器程序集的引用。
Microsoft.Windows.Design.Extensibility
Microsoft.Windows.Design.Interaction
添加对 CustomControlLibrary 项目的引用。
在**“解决方案资源管理器”中,将 Class1 代码文件的名称更改为 Metadata.cs 或 Metadata.vb。 如果出现消息框,询问是否对此项目中的所有引用执行重命名操作,请单击“是”**。
用下面的代码替换自动生成的代码。 此代码将创建一个将自定义设计时实现附加到 ButtonWithDesignTime 类的 AttributeTable。
Imports System Imports System.Collections.Generic Imports System.Text Imports System.ComponentModel Imports System.Windows.Media Imports System.Windows.Controls Imports System.Windows Imports CustomControlLibrary Imports Microsoft.Windows.Design.Features Imports Microsoft.Windows.Design.Metadata 'Imports CustomControlLibrary.VisualStudio.Design.Slid ' The ProvideMetadata assembly-level attribute indicates to designers ' that this assembly contains a class that provides an attribute table. <Assembly: ProvideMetadata(GetType(CustomControlLibrary.VisualStudio.Design.Metadata))> ' 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. Friend Class Metadata Implements IProvideAttributeTable ' Accessed by the designer to register any design-time metadata. Public ReadOnly Property AttributeTable() As AttributeTable _ Implements IProvideAttributeTable.AttributeTable Get Dim builder As New AttributeTableBuilder() ' Add the menu provider to the design-time metadata. builder.AddCustomAttributes(GetType(ButtonWithDesignTime), _ New FeatureAttribute(GetType(CustomContextMenuProvider))) Return builder.CreateTable() End Get End Property End Class
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Windows.Media; using System.Windows.Controls; using System.Windows; using CustomControlLibrary; using Microsoft.Windows.Design.Features; using Microsoft.Windows.Design.Metadata; using CustomControlLibrary.VisualStudio.Design; // The ProvideMetadata assembly-level attribute indicates to designers // that this assembly contains a class that provides an attribute table. [assembly: ProvideMetadata(typeof(CustomControlLibrary.VisualStudio.Design.Metadata))] namespace CustomControlLibrary.VisualStudio.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(); // Add the menu provider to the design-time metadata. builder.AddCustomAttributes( typeof(ButtonWithDesignTime), new FeatureAttribute(typeof(CustomContextMenuProvider))); return builder.CreateTable(); } } } }
保存解决方案。
实现菜单提供程序
菜单提供程序在名为 CustomContextMenuProvider 的类型中的实现。 通过提供的 MenuAction,可以在设计时设置控件的 Background 属性。
实现菜单提供程序
向 CustomControlLibrary.VisualStudio.Design 项目中添加一个名为 CustomContextMenuProvider 的新类。
在 CustomContextMenuProvider 的代码编辑器中,用下面的代码替换自动生成的代码。 此代码实现提供自定义 MenuAction 的 PrimarySelectionContextMenuProvider。
Imports System Imports System.Collections.Generic Imports System.Text Imports Microsoft.Windows.Design.Interaction Imports System.Windows Imports Microsoft.Windows.Design.Model Imports System.Windows.Controls Imports System.Windows.Media ' The CustomContextMenuProvider class provides two context menu items ' at design time. These are implemented with the MenuAction class. Class CustomContextMenuProvider Inherits PrimarySelectionContextMenuProvider Private setBackgroundToBlueMenuAction As MenuAction Private clearBackgroundMenuAction As MenuAction ' The provider's constructor sets up the MenuAction objects ' and the the MenuGroup which holds them. Public Sub New() ' Set up the MenuAction which sets the control's ' background to Blue. setBackgroundToBlueMenuAction = New MenuAction("Blue") setBackgroundToBlueMenuAction.Checkable = True AddHandler setBackgroundToBlueMenuAction.Execute, AddressOf SetBackgroundToBlue_Execute ' Set up the MenuAction which sets the control's ' background to its default value. clearBackgroundMenuAction = New MenuAction("Cleared") clearBackgroundMenuAction.Checkable = True AddHandler clearBackgroundMenuAction.Execute, AddressOf ClearBackground_Execute ' Set up the MenuGroup which holds the MenuAction items. Dim backgroundFlyoutGroup As New MenuGroup("SetBackgroundsGroup", "Set Background") ' If HasDropDown is false, the group appears inline, ' instead of as a flyout. Set to true. backgroundFlyoutGroup.HasDropDown = True backgroundFlyoutGroup.Items.Add(setBackgroundToBlueMenuAction) backgroundFlyoutGroup.Items.Add(clearBackgroundMenuAction) Me.Items.Add(backgroundFlyoutGroup) ' The UpdateItemStatus event is raised immediately before ' this provider shows its tabs, which provides the opportunity ' to set states. AddHandler UpdateItemStatus, AddressOf CustomContextMenuProvider_UpdateItemStatus End Sub ' The following method handles the UpdateItemStatus event. ' It sets the MenuAction states according to the state ' of the control's Background property. This method is ' called before the context menu is shown. Sub CustomContextMenuProvider_UpdateItemStatus( _ ByVal sender As Object, _ ByVal e As MenuActionEventArgs) ' Turn everything on, and then based on the value ' of the BackgroundProperty, selectively turn some off. clearBackgroundMenuAction.Checked = False clearBackgroundMenuAction.Enabled = True setBackgroundToBlueMenuAction.Checked = False setBackgroundToBlueMenuAction.Enabled = True ' Get a ModelItem which represents the selected control. Dim selectedControl As ModelItem = _ e.Selection.PrimarySelection ' Get the value of the Background property from the ModelItem. Dim backgroundProperty As ModelProperty = _ selectedControl.Properties("Background") ' Set the MenuAction items appropriately. If Not backgroundProperty.IsSet Then clearBackgroundMenuAction.Checked = True clearBackgroundMenuAction.Enabled = False ElseIf backgroundProperty.ComputedValue.Equals(Brushes.Blue) Then setBackgroundToBlueMenuAction.Checked = True setBackgroundToBlueMenuAction.Enabled = False End If End Sub ' The following method handles the Execute event. ' It sets the Background property to its default value. Sub ClearBackground_Execute( _ ByVal sender As Object, _ ByVal e As MenuActionEventArgs) Dim selectedControl As ModelItem = e.Selection.PrimarySelection selectedControl.Properties("Background").ClearValue() End Sub ' The following method handles the Execute event. ' It sets the Background property to Brushes.Blue. Sub SetBackgroundToBlue_Execute( _ ByVal sender As Object, _ ByVal e As MenuActionEventArgs) Dim selectedControl As ModelItem = e.Selection.PrimarySelection selectedControl.Properties("Background").SetValue(Brushes.Blue) End Sub End Class
using System; using System.Collections.Generic; using System.Text; using Microsoft.Windows.Design.Interaction; using System.Windows; using Microsoft.Windows.Design.Model; using System.Windows.Controls; using System.Windows.Media; namespace CustomControlLibrary.VisualStudio.Design { // The CustomContextMenuProvider class provides two context menu items // at design time. These are implemented with the MenuAction class. class CustomContextMenuProvider : PrimarySelectionContextMenuProvider { private MenuAction setBackgroundToBlueMenuAction; private MenuAction clearBackgroundMenuAction; // The provider's constructor sets up the MenuAction objects // and the the MenuGroup which holds them. public CustomContextMenuProvider() { // Set up the MenuAction which sets the control's // background to Blue. setBackgroundToBlueMenuAction = new MenuAction("Blue"); setBackgroundToBlueMenuAction.Checkable = true; setBackgroundToBlueMenuAction.Execute += new EventHandler<MenuActionEventArgs>(SetBackgroundToBlue_Execute); // Set up the MenuAction which sets the control's // background to its default value. clearBackgroundMenuAction = new MenuAction("Cleared"); clearBackgroundMenuAction.Checkable = true; clearBackgroundMenuAction.Execute += new EventHandler<MenuActionEventArgs>(ClearBackground_Execute); // Set up the MenuGroup which holds the MenuAction items. MenuGroup backgroundFlyoutGroup = new MenuGroup("SetBackgroundsGroup", "Set Background"); // If HasDropDown is false, the group appears inline, // instead of as a flyout. Set to true. backgroundFlyoutGroup.HasDropDown = true; backgroundFlyoutGroup.Items.Add(setBackgroundToBlueMenuAction); backgroundFlyoutGroup.Items.Add(clearBackgroundMenuAction); this.Items.Add(backgroundFlyoutGroup); // The UpdateItemStatus event is raised immediately before // this provider shows its tabs, which provides the opportunity // to set states. UpdateItemStatus += new EventHandler<MenuActionEventArgs>( CustomContextMenuProvider_UpdateItemStatus); } // The following method handles the UpdateItemStatus event. // It sets the MenuAction states according to the state // of the control's Background property. This method is // called before the context menu is shown. void CustomContextMenuProvider_UpdateItemStatus( object sender, MenuActionEventArgs e) { // Turn everything on, and then based on the value // of the BackgroundProperty, selectively turn some off. clearBackgroundMenuAction.Checked = false; clearBackgroundMenuAction.Enabled = true; setBackgroundToBlueMenuAction.Checked = false; setBackgroundToBlueMenuAction.Enabled = true; // Get a ModelItem which represents the selected control. ModelItem selectedControl = e.Selection.PrimarySelection; // Get the value of the Background property from the ModelItem. ModelProperty backgroundProperty = selectedControl.Properties["Background"]; // Set the MenuAction items appropriately. if (!backgroundProperty.IsSet) { clearBackgroundMenuAction.Checked = true; clearBackgroundMenuAction.Enabled = false; } else if (backgroundProperty.ComputedValue == Brushes.Blue) { setBackgroundToBlueMenuAction.Checked = true; setBackgroundToBlueMenuAction.Enabled = false; } } // The following method handles the Execute event. // It sets the Background property to its default value. void ClearBackground_Execute( object sender, MenuActionEventArgs e) { ModelItem selectedControl = e.Selection.PrimarySelection; selectedControl.Properties["Background"].ClearValue(); } // The following method handles the Execute event. // It sets the Background property to Brushes.Blue. void SetBackgroundToBlue_Execute( object sender, MenuActionEventArgs e) { ModelItem selectedControl = e.Selection.PrimarySelection; selectedControl.Properties["Background"].SetValue(Brushes.Blue); } } }
生成解决方案。
测试设计时实现
可以像使用任何其他 WPF 控件一样使用 ButtonWithDesignTime 控件。 WPF 设计器处理所有设计时对象的创建。
测试设计时实现
使用 Visual Basic 或 Visual C# 为解决方案添加一个名为 DemoApplication 的新 WPF 应用程序项目。
MainWindow.xaml 将在 WPF 设计器中打开。
添加对 CustomControlLibrary 项目的引用。
在 XAML 视图中,用以下 XAML 替换自动生成的 XAML。 此 XAML 将添加对 CustomControlLibrary 命名空间的引用并添加 ButtonWithDesignTime 自定义控件。 该按钮出现在“设计”视图中,并显示文本“已激活设计模式”,表明该按钮处于设计模式下。 如果该按钮未出现,可能需要单击设计器顶部的信息栏,以重新加载视图。
<Window x:Class="DemoApplication.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:cc="clr-namespace:CustomControlLibrary;assembly=CustomControlLibrary" Title="Window1" Height="300" Width="300"> <Grid> <cc:ButtonWithDesignTime Margin="30,30,30,30" Background="#FFD4D0C8"></cc:ButtonWithDesignTime> </Grid> </Window>
在“设计”视图中,单击 ButtonWithDesignTime 控件将其选中。
右击 ButtonWithDesignTime 控件,指向**“设置背景”,然后选择“蓝色”**。
该控件的背景即设置为蓝色。 在 XAML 视图中,将 Background 属性设置为菜单操作指定的值。
运行 DemoApplication 项目。
运行时,该按钮将具有您用快捷菜单设置的背景。
后续步骤
您可以向自定义控件中添加更多设计时功能。
向控件的设计时添加自定义装饰器。 有关更多信息,请参见演练:创建设计时装饰器。
为自定义类型创建编辑器,可在“属性”窗口中使用该编辑器。 有关更多信息,请参见如何:创建值编辑器。
请参见
参考
PrimarySelectionContextMenuProvider