原文链接:Silverlight Design Time Extensibility --By Justin Angel (Microsoft Silverlight Toolkit Program Manager)

嗨伙计们

只要有人谈到开发者与设计师在 Silverlight/WPF上协同工作时,他们就会谈论“设计,开发工作流程”这个
问题。即使您是您自己的设计师,这工作也始终是永远存在于当你在“设计师”和“开发”之间切换“帽子”的过程中。

我是一个使用工具创建用户界面的支持者。 我的生活让我不能理解为什么有人会选择非产能(non-productive)
和手写XAML的事情。

你能找出的一个情况就是当你使用(Expression Blend & Visual Studio WPF/Silverlight Designer)这类工
具进行工作时,如果使用正确,这些工具会对提高生产力起到巨大的推动作用。然而,这篇帖子不是关于如何使用
这类工具,而是关于如何帮助那些使用您的控件作为工具进行设计的人。本文是关于开发者着手去让设计师有更容
易的(设计)体验并减少摩擦。

控件提供商和开发者通常都想给自己的控件以更好的体验。然而,在这个问题上其缺乏大量的信息。我决定用
本文纠正这种情况。

本文也与那些在项目中有设计师一起工作的开发者有关。我建立的解决方案可在这里下载,SilverlightControls.zip

介绍:

首先,我们会考虑在设计时的Assemblies工作。

考虑下面这个类:

public class myControl : Control
{
    public string MyStringProperty { get; set;  }
}

现在,考虑下面这个设计时属性的类:

[Description("I am a control")]
public class myControl : Control
{
    [Description("I am a property")]
    public string MyStringProperty { get; set;  }
}

在设计时assemblies 工作的方式就是将设计时属性与实际的类进行分离。之后我们的类会是这个样子:

public class myControl : Control
{
    public string MyStringProperty { get; set;  }
}

并且在我们的设计时组装过程时,代码相当于:

AddCallback(typeof(myControl), builder =>
     builder.AddCustomAttributes(new DescriptionAttribute("I am a control 2")));

AddCallback(typeof(myControl), builder =>
    builder.AddCustomAttributes("MyStringProperty", new DescriptionAttribute("I am a property")));

在没有研究太多API的情况下,可以看到第一行会显示“myControl has the following DescriptionAttribute”,
第二行显示:“myControl.MyStringProperty has this DescriptionAttribute”.

我们从运行时代码中分离出设计时代码. 下面让我们看一下在BLEND中,它的样子:

 

这一做法有哪些优势?

1.将设计时代码与运行时代码解耦有助于创造更好的代码。 (分离的问题,POCO,单一的责任等)

2.基于工具修改设计时属性。这种做法允许支持不同的设计时属性,而这些属性基于它们运行的工具。

3.设计时变更无须重新编译运行时组件。

4.不是运行时组装的author可以添加设计时属性。

5.高级的设计时支持,并不需要我们使用GUIDs注册Visual Studio软件包。


参考架构

这里有一些步骤. 从本质上讲,我们要建立以下结构:

一点都不乱(译者注:我看有点乱,呵呵),是吧?简单地说,这仅仅是参考模型。

我们将创建3个新项目。

 *. Design.dll -将包含设计时属性,这些属性用于Visual Studio WPF/Silverlight设计器和Expression Blend。

*. VisualStudio.design.dll -将包含设计时的功能,仅用于Visual Studio。

*. Expression.design.dll -将包含设计时的功能,仅用于Expression blend。

这种命名约定是强制性的。

第一步是增加项目引用,我们会在该项目中添加设计时支持用于设计时组件:

添加对共享设计时dll的引用:

并添加Blend design-time 引用:

步骤:

1. 添加一个Silverlight 类库,该类库会包含我们的控件. 我们将它命名为:SilverlightControls.

2. 添回一个myControl 类.

public class myControl : Control
{
    public string MyStringProperty
    {

get { return GetValue(MyStringPropertyProperty) as string; }

set { SetValue(MyStringPropertyProperty, value); }
    }

public static readonly DependencyProperty MyStringPropertyProperty =
        DependencyProperty.Register("MyStringProperty",typeof(string), typeof(myControl), null);   
}

3. 创建共享运行时DLL. 因为控件被命名为“SilverlightControls” ,所以这里命名为“SilverlightControls.Design”.
这里选择类型为“WPF Custom Control library” 因为稍后会使用一些WPF 特性.如果喜欢,我们可以手工添加引用到普通类库项目中。

4. 在“SilverlightControls.Design” 项目中添加对“SilverlightControls” 项目的引用,  以及Microsoft design time 组件和
Silverlight System.Windows.dll.

(Silverlight 2 引用路径– 默认位于 c:"Program Files"Microsoft SDKs"Silverlight"v2.0"Reference Assemblies)

这是我们引用的组件:

5. 创建visual studio 设计时DLL.
   因为要添加的设计时用于“SilverlightControls” 所以这里命名为:“SilverlightControls.VisualStudio.Design”.

6. 在项目“SilverlightControls.VisualStudio.Design” 中添加对“SilverlightControls”的引用, 以及
Microsoft design time assemblies 和 Silverlight System.Windows.dll.

这里我们添加的引用:

7. 创建expression blend 设计时 DLL.
   因为要添加的设计时用于“SilverlightControls” 所以这里命名为“SilverlightControls.Expression.Design”.

8. 在项目“SilverlightControls.Expression.Design” 中添加对“SilverlightControls” 的引用,Microsoft

design time assemblies 和 Blend specific dlls。

这里我们添加的引用:

9. 我们需要在三个运行时组件项目中分别添加一个类,该类实现了IReigsterMetadata 接口.

我在这三个项目中使用下面模版.

public class MetadataRegistration : IRegisterMetadata
{
    private static AttributeTable _customAttributes;

private static bool _initialized;

public void Register()
    {
        if (!_initialized)
        {
            MetadataStore.AddAttributeTable(CustomAttributes);

_initialized = true;
        }
    }

public static AttributeTable CustomAttributes
    {
        get
        {
            if (_customAttributes == null)
            {
                _customAttributes = new CustomMetadataBuilder().CreateTable();
            }

return _customAttributes;
        }
    }

private class CustomMetadataBuilder : AttributeTableBuilder
    {
        public DeveloperMetadataBuilder()
        {
            // TODO: Add Design time code here!
        }

private void AddTypeAttributes(Type type, params Attribute[] attribs)
        {
            base.AddCallback(type, builder => builder.AddCustomAttributes(attribs));
        }

private void AddMemberAttributes(Type type, string memberName, params Attribute[] attribs)
        {
            base.AddCallback(type, builder => builder.AddCustomAttributes(memberName, attribs));
        }
    }
}

我不想深入研究这个类做了什么,以及IRegisterMetadata 和 AttributeTableBuilder 是什么, 您可以到
MSDN 上去查找. 我只想说,它们是extensibility framework 中的类,通过它们,我们可以对我们的类添加设
计时支持。并且,我也不打算深入分析我的 template 做了什么. 起码它确保 Metadata 仅被创建一次,并且提
供了简单的访问方法,这些方法我们会在本DEMO中到处使用。

好,这就是我们当前的项目结构:

注:我们在每个项目中都已有了一个metadata 类.

10. 安装设计时文件拷贝。
       设计时DLL文件须与运行时DLL文件放在相同的目录下.  
       我喜欢自动完成相关的*design.dll 文件拷贝. 你可以手工完成这一操作。

我使用的自动方式是添加一条 post-build action 到每个设计时项目中.

右击我们的项目文件 –> “属性” –> “生成事件”. 粘帖下面内容到Post Build Events:

copy "$(TargetPath)" "$(SolutionDir)"SilverlightControls"Bin"Debug"

在所有的三个设计时项目中重复这一步骤.

创建我们的测试项目

1. 我们使用BLEND创建一个新的项目来使用我们的DLL. 我们会使用该项目来预览我们的设计时改进。

2. 添加对“SilverlightControls.dll”的引用.

这时发生了什么?

我们有了两个设计时dll约束. 因为这两个约束会被工具自动加载到tools:

Design time DLLs 文件与 runtime DLL在一起.
   Design time DLLs 遵守命名协定.
   
<RuntimeName>.Design.dll – 共享设计时
<RuntimeName>.Expression.Design.dll – 用于expression blend 设计时
<RuntimeName?.VisualStudio.Design.dll – 用于Visual studio wpf/silverlight 设计器

现在我们有了这个基本架构,接下来就开始用一下吧.

注:原文作者在这里好像就做了一个章节,下面是后续的文章内容:属性讲解

DescriptionAttribute (Shared)

Description 是一个让我们在设计时工具中显示类似智能说明的方法.  该属性用于在类和属性之间进行工作。

当我们说某个属性是“Shared” 时,它意味着工作在两个工具之间并且须放在共享设计时 dll中.
   尽管当前 Visual studio 有只读的设计时界面, 一旦其获得一个可以编辑的设计界面,这些都会在两个工具间被支持.

那就让我们在SilverlightControls.Design.MetadataRegistration类型中DescriptionAttribute.

public CustomMetadataBuilder()
            {

AddTypeAttributes(typeof(myControl),
                    new DescriptionAttribute("I am a control"));

AddMemberAttributes(typeof(myControl),"MyStringProperty",
                    new DescriptionAttribute("I am a property"));
            }

现在编译我们的应用并在Expression Blend看一下这些描述。

DisplayNameAttribute (shared)

显示名称属性用于显示类型成员的(friendlier)名称.

AddMemberAttributes(typeof(myControl),"MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"));

下面是其在 Blend中的显示:

CategoryAttribute (shared)

每个属性都有一个默认分类. Blend 为我们的控件定义如下分类:

默认情况下,我们的属性添加到了“Miscellaneous” 分类. 如果想重新定义它,我们可让它在别处。

我们可能想让我们的属性添加到已有的分类中,比如“Common Properties”.

AddMemberAttributes(typeof(myControl),"MyStringProperty",
    new DescriptionAttribute("I am a property"),
    new DisplayNameAttribute("My String Property"),
    new CategoryAttribute("Common Properties"));

在Blend中,我们看到它被移动到了那个分类下:

我们可能想去创建我们自己在分类,该分类我们称之为“My Category”.

AddMemberAttributes(typeof(myControl),"MyStringProperty",
    new DescriptionAttribute("I am a property"),
    new DisplayNameAttribute("My String Property"),
    new CategoryAttribute("My Category"));

Blend 甚至可以在一个相同的自定义分类中进行属性分组(group properties).

让我们在控件中添加一个新的Integer 类型属性:

public class myControl : Control
{
    public string MyStringProperty
    {
        get { return GetValue(MyStringPropertyProperty) as string; }

set { SetValue(MyStringPropertyProperty, value); }
    }

public static readonly DependencyProperty MyStringPropertyProperty =
        DependencyProperty.Register("MyStringProperty",typeof(string), typeof(myControl), null);

public int MyIntProperty
    {
        get { return (int)GetValue(MyIntPropertyProperty) ; }
        set { SetValue(MyIntPropertyProperty, value); }
    }

public static readonly DependencyProperty MyIntPropertyProperty =
        DependencyProperty.Register("MyIntProperty", typeof(int), typeof(myControl), null);

}

我们将它添加到我们自定义的分类中.

AddMemberAttributes(typeof(myControl),"MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"),
        new CategoryAttribute("My Category"));

AddMemberAttributes(typeof(myControl), "MyIntProperty",
        new CategoryAttribute("My Category"));

我们会看到在‘My Category’中有两个属性:

BrowsableAttribute (shared)

Browsable 属性允许我们隐藏一些属性,这些属发现在Blend’的数据面板(Data pane)或Visual
Studio的属性窗口中不再有效.

    AddMemberAttributes(typeof(myControl),"MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"),
        new CategoryAttribute("My Category"));

AddMemberAttributes(typeof(myControl), "MyIntProperty",
        new CategoryAttribute("My Category"),
        BrowsableAttribute.No);

当我们在 Blend运行当前应用时,会看到在Data Pane中不在有该属性了:

我们能够使用 browsable 属性隐藏一些继承的属性以及我们自定义的属性.

EditorBrowsableAttribute (Blend)

我仅在Blend 设计时项目中标记了这个属性, 但这是出于我对Visual Studio Silverlight 设计器的猜测.
这可能在VS的一些流行特征中支持, 但我建议在使用之前先考虑它。Editor Browsable 高级状态可以让你在
Blend 中的一个分类下隐藏一些属性,除非用户喜欢看到这些。基本上,隐藏一下较少使用的属性可以让分类
显示更加清楚。

让我们打开expression blend metadata 项目中的元数据文件并添加下面设计时:

  AddMemberAttributes(typeof (myControl), "MyIntProperty",
            new EditorBrowsableAttribute(EditorBrowsableState.Advanced));

这是Blend 显示的分类:

我们会在该分类上看到一个小的“隐藏”箭头:

如果点击它, 会展开并显示该分类下的所有高级属性:

如果用户离开控件然后在选中它时,该分类显示将再次关闭:

那我们在这里假设直到你需要时我们在显示它.

在Silverlight Framework 属性里在什么地方应用该属性呢? 很多地方.

下面是在Silverlight的“Layout” 分类中:

然后我点击它:

我们明白了为什么要在同一分类下避免显示太多的属性.

TypeConverterAttribute (Shared)

指定用作此属性所绑定到的对象的转换器的类型。

起初, 它被用在从UI到后台模型的值转换操作. 它在Blend 2 SP1 在现在还无法使用.
如果能使用就好了, 因为它有一些不错的东西.

在Blend TypeConverters能做的一件事是支持标准值,我们会在操作中看到.

在我们的类似对象“MyObject”添加另一个属性:

public class myControl : Control
{
    public string MyStringProperty
    {
        …
    }

public int MyIntProperty
    {
        …
    }

public object MyObjectProperty
    {
        get { return (object)GetValue(MyObjectPropertyProperty); }

set { SetValue(MyObjectPropertyProperty, value); }
    }

public static readonly DependencyProperty MyObjectPropertyProperty =
        DependencyProperty.Register("MyObjectProperty", typeof(object), typeof(myControl), null);

}

这是该属性的默认设计时:

我们通过创建一个简单的TypeConverter开始:

public class myTypeConverter : TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        return new StandardValuesCollection(new string[] { "Hello" ,"World", "foo", "bar"});
    }
}

该类型converter 的工作就是对任何使用该converter的属性写入相应的(可能)值(以代码方式).

下面在共享设计时metadata中,我们加入了对该 TypeConverter的引用:

AddMemberAttributes(typeof(myControl), "MyObjectProperty",
        new CategoryAttribute("My Category"),
        new TypeConverterAttribute(typeof(myTypeConverter)));

然后我们在 Blend应用它:

我们得到了一系列可供选择的值(以代码方式).

同样,我们可以使用内置式的TypeConverters, 比如 Boolean Converter:

   AddMemberAttributes(typeof(myControl), "MyObjectProperty",
        new CategoryAttribute("My Category"),
        new TypeConverterAttribute(typeof(BooleanConverter)));

这是它在 Blend的效果:

某些TypeConverters 甚至提供了可视工具化的提示,用来设置当前属性.
   下面就是一个不错的例子,ExpandleTypeConverter:

    AddMemberAttributes(typeof(myControl), "MyObjectProperty",
        new CategoryAttribute("My Category"),
        new TypeConverterAttribute(typeof(ExpandableObjectConverter)));

这是它在 Blend的效果:

然后点击“new” 按钮会弹出一个“对象”对话框:

PropertyOrderAttribute (Shared)

Property order 属性可以让我们定义已存在的属性在某分类下的排序位置.

我们通过定义三个属性来加以说明, 为此我打算将MyIntProperty挪到 advanced 区域外. (在blend 设计时的元数据文件)

  AddMemberAttributes(typeof (myControl), "MyIntProperty",
          new EditorBrowsableAttribute(EditorBrowsableState.Advanced));

在取消 MyIntProperty隐藏之后, 下面是我们默认的属性排序:

它是基于字母排序方式的.

首先“MyIntProperty”, 第二“MyObjectProperty” ,然后第三 “My String Property”.

现在我们想让 MyStringProperty 排在第一的位置.

    AddMemberAttributes(typeof(myControl), "MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"),
        new CategoryAttribute("My Category"),
        new PropertyOrderAttribute(PropertyOrder.Early));

这是它在 Blend的效果:

我有点想看一下这个PropertyOrder 类了. 让我们仔细看一下它的 static 成员.

好的, 现在我想让MyIntProperty 显示在最后.

  AddMemberAttributes(typeof(myControl), "MyIntProperty",
        new CategoryAttribute("My Category"),
        new PropertyOrderAttribute(PropertyOrder.Late));

我们看到在Blend中该属性目前是最后一项了:

那如果我们将 MyObjectProperty 与“My String Property” 同时定义在PropertyOrder.Early?

   AddMemberAttributes(typeof(myControl), "MyObjectProperty",
        new CategoryAttribute("My Category"),
        new TypeConverterAttribute(typeof(ExpandableObjectConverter)),
        new PropertyOrderAttribute(PropertyOrder.Early));

它们会首先会 property order 排序,然后是基于字母排序:

因为“MyObjectProperty” 与“My String Property” 都有相同的property order, 它们会在内部通过字母进行排序.

现在,基于练习的原因, 我会让当前的Property Ordering 相同并在内部对这些属性进行手工排序.
我们尝试将“My String Property” 移到“MyObjectProperty”之前.

   AddMemberAttributes(typeof(myControl), "MyObjectProperty",
        new CategoryAttribute("My Category"),
        new TypeConverterAttribute(typeof(ExpandableObjectConverter)),
        new PropertyOrderAttribute(PropertyOrder.CreateAfter(PropertyOrder.Early)));

然后我们会看到MyObjectProperty 排到了“My String property”之后:

注意:PropertyOrder.CreateBefore/After 方法会返回一个PropertyOrder, 我们可以依据我们的喜好手工进行排序。

注:作者在此处又结束了一篇,下面是接下来的内容

NumberRangeAttribute, NumberIncrementAttribute, NumberFormatAttribute (Blend)

在Expression DLLs中这些属性很重要, 因此它们被用在了 Blend(当然项目)中.

让我们看一个我们在什么属性上声明使用了它们: Opacity

首先我们注意在此处有一个比例数值. Opacity 被显示为百分比格式.

接着我们看到有一个标识符‘%’跟在该数值后. 现在我们通过鼠标滚轮加/减当前数值.

   

如果我们 drag 鼠标并按 SHIFT 键会看到递增单位被加的更大:

   

或drag 鼠标并按CTRL + SHIFT  键会看到递增单位被加的更小:

   

(通过上面的演示)我们能看出两件事:

1. 有三种 increments/decrements 方式– Small, Large 和 normal

2. 存在0-100的数值范围

这两种情况我们之前都看到了,其就是基于这些属性实现的.

让我们看一下这些属性的构造方法:

NumberRangeAttribute 属性可以让我们指定最小 & 最大值的范围.

NumberIncrementAttribute 属性可以让我们指定small, default 和large 递增类型.

NumberFormatAttribute 属性可以让我们指写显示内容的格式, 数字精度,显示比率等.

在我们的blend metadata 文件中, 为MyIntProperty 添加下列属性:

将数值范围限制在1-100之间, 指定increments 为0.5, 1 和 5, 显示比率为 x10 以及格式为 “X%”.

我们将范围限制在1-100:

AddMemberAttributes(typeof(myControl), "MyIntProperty",
                    new NumberRangesAttribute(null, 1, 100, null, null));

分别添加 0.5, 1 和 5 increments 用于small, default 和 large increments:

AddMemberAttributes(typeof(myControl), "MyIntProperty",
                    new NumberRangesAttribute(null, 1, 100, null, null),
                    new NumberIncrementsAttribute(0.5, 1, 5));

最后添加‘X%’格式用于显示10的比率.

 AddMemberAttributes(typeof(myControl), "MyIntProperty",
                    new NumberRangesAttribute(null, 1, 100, null, null),
                    new NumberIncrementsAttribute(0.5, 1, 5),
                    new NumberFormatAttribute("0'%'", null, 10));

运行这个例子并采用普通 dragging :
 
 

然后drag 并按下SHIFT 键:

   

这是MyIntProperty实际初始值:

我们得到了我们想要的: 取值范围在0-100, 显示比率, 格式以及increments.

注:原文作者在此又结束了一章

ToolboxBrowsableAttribute (Shared)
 
    我们将几个控件编译在一起然后在Visual studio Silverlight 工具栏中添加它们或放在Blend库中.

如果我们打算隐藏这些控件中的一个时, 我们可以使用ToolboxBrowsableAttribute . 一个不错的
例子就是ComboBox 是可见的,但ComboBoxItem 被隐藏, 日期可见但DayButton 不可见等等.

在Visual Studio 2008 SP1, Blend 2 SP1这两个工具中,该属性仅是设计时属性.

下面我们创建另一个控件.

public class myOtherControl : Control
{
}

在Blend 里,我们会在asset gallery中看到一个新的控件:

在Visual studio 里,我们打开 “Choose Items” 对话框, 找到“SIlverlightControls.dll” 并点击.

现在,我们着手在这些对话框中隐藏myOtherControl。 我们在共享设计时元数据项目中添加 ToolboxBrowsableAttribute。

AddTypeAttributes(typeof(myOtherControl),
        new ToolboxBrowsableAttribute(false));

之后编辑并重启 blend, 我们看到在Blend 的Asset Gallery中不再出现该控件了:

然后在Visual studio 的“Choose Items” 对话框中也没再显示myOtherControl:

Inline Editors (Shared)

Editors 是一个在Visual Studio 属性窗口和 Blend Data Pane中的定制元素.

下面是一些已知的editors 例子:

我们在custom project 项目中定义我们的editors 以避免Silverlight dlls 的二义性引用(ambiguous references).

Naming convention and the very existence of this project are optional.

我们添加一些必要的设计时引用.

也要确保在 “SilverlightControls.Design.dll” 中对“SilverlightControls.Design.Editors.dll”的引用.

我们着手在Editors项目中添加一个ResourceDictionary,将其命名为“EditorDictionary”.

在EditorResources.xaml 里添加如下的DataTemplate:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<DataTemplate x:Key="ColorsComboBox">

<ComboBox Text="{Binding StringValue}" IsEditable="True">

<ComboBoxItem Background="Red">Red</ComboBoxItem>

<ComboBoxItem Background="Blue">Blue</ComboBoxItem>

<ComboBoxItem Background="Green">Green</ComboBoxItem>

<ComboBoxItem Background="Yellow">Yellow</ComboBoxItem>

<ComboBoxItem Background="Black">Black</ComboBoxItem>

</ComboBox>

</DataTemplate>

</ResourceDictionary>

我们得到了一个包括一些ComboBoxItems的 ComboBox。 唯一重要的就是通过设计工具确保字符值以或数值被绑定到.

下面我们添加了一些静态类来访问我们的Resource dictionary.

public static class EditorDictionary
{
    private static ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(new Uri("SilverlightControls.Design.Editos;component/EditorDictionary.xaml", UriKind.Relative));

public static DataTemplate ColorsComboBox
    {
        get
        {
            return dictionary["ColorsComboBox"] as DataTemplate;
        }
    }
}

这个类要做的就是加载Resource dictionary 并包含一个返回我们 DateTemplate的静态属性.

现在让我们看一个感兴趣的地方, 我们会创建一个显示DataTemplate的自定义inline editor.

public class CustomInlineEditor : PropertyValueEditor
{
    public CustomInlineEditor()
    {
        this.InlineEditorTemplate = EditorDictionary.ColorsComboBox;
    }
}

非常直接. PropertyValueEditor 一个有趣的地方 - 这个(我们用于设置DataTemplate)InlineEditorTemplate取自resource dictionary.

最后必须要说的是MyControl.MyStringProperty 有一个自定底 inline editor.

    AddMemberAttributes(typeof(myControl), "MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"),
        new CategoryAttribute("My Category"),
        new PropertyOrderAttribute(PropertyOrder.Early),
        PropertyValueEditor.CreateEditorAttribute(typeof(CustomInlineEditor)));

在Blend里的效果:

然后,我们选取“Green”.

在UI和XAML之间来回进行驱动的是ComboBox 到 StringValue的绑定.

我想从这个练习中得到的不应该是“rainbow colored ComboBoxes不错” 或“要将任何元素都做为一个inline editor”.

现在我们看到editor 的样子以及它是如何工作的, 我们应该熟悉3种类型的属性 editors.

Inline Property Editor :下面的“单行并有属性名称和编辑框” 样式.

Extended Property Editor:有内部的editor, 但也可根据用户操作弹出或占用一些区域(显示).

Dialog property editor – 一个具有触发对话框按钮的Inline property editor.

Expended Property Editor

正如上面所见, 一个expended property editor 就是一个inline editor,其基于用户操作来占用更大的屏幕区域.
    让我们看看如何创建它.
    我们着手在EditorResources.xaml文件中创建另一个 DataTemplate(InlineValueEditorTemplate):

  <DataTemplate x:Key="SimpleTextBox">

<StackPanel Orientation="Horizontal">

<TextBox Text="{Binding StringValue}" />

<PropertyEditing:EditModeSwitchButton />

</StackPanel>

</DataTemplate>

我们看到仅有一个绑定StringValue的TextBox。我们在前面的inline property editor中已看到过.

有趣的是这里有一个“EditModeSwitchButton”. 它内置一个extensibility 按钮,它可展开/收缩extended template
视图.这个按钮的作用就是链接到 extensibility 的PropertyValueEditorCommands,该命令负责显示或隐藏extended视图.

现在我们有了InlineValueEditorTemplate, 下面添加另外一个 DataTemplate(ExtendedValieEditorTemplate):

<DataTemplate x:Key="HelloWorldListBox">

<ListBox SelectedItem="{Binding StringValue}">

<ListBox.ItemsSource>

<x:Array Type="{x:Type System:String}">

<System:String>Hello</System:String>

<System:String>World</System:String>

<System:String>foo</System:String>

<System:String>bar</System:String>

</x:Array>

</ListBox.ItemsSource>

<ListBox.ItemTemplate>

<DataTemplate>

<TextBlock Text="{Binding}" />

</DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

</DataTemplate>

尽管这个DataTemplate 看着有点意思, 然而实际上并不是这样. 它仅是一组在Listbox中作为 TextBlock显示出来的字符串.
唯一有趣的是它是我们将这个ListBox 的SelectedItem 绑定到 StringValue.

下面, 我们会在EditorDictionary类中创建这两个强类型(命名)的属性:

public static DataTemplate SimpleTextBox
{
    get
    {
        return dictionary["SimpleTextBox"] as DataTemplate;
    }
}

public static DataTemplate HelloWorldListBox
{
    get
    {
        return dictionary["HelloWorldListBox"] as DataTemplate;
    }
}

最后,我们创建一个ExtendedValuePropertyEditor 来使用我们上面的两个新的 DataTemplates:

public class CustomExtendedEditor : ExtendedPropertyValueEditor
{
    public CustomExtendedEditor()
    {
        this.InlineEditorTemplate = EditorDictionary.SimpleTextBox;
        this.ExtendedEditorTemplate = EditorDictionary.HelloWorldListBox;
    }
}

还有要把CustomExtendedEditor绑定到我们的 MyStringProperty 上:

    AddMemberAttributes(typeof(myControl), "MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"),
        new CategoryAttribute("My Category"),
        new PropertyOrderAttribute(PropertyOrder.Early),
        PropertyValueEditor.CreateEditorAttribute(typeof(CustomExtendedEditor)));

这是在 Blend 中的效果:

   
   点击该按钮之后 ,我们就得到了这个ExtendedEditorTemplate 弹出框:

我们点击另一个按钮会看到ExtendedEditorTemplate 在data pane里占用了更多的空间:

选择这个列表项中的任一个值都会造成Textbox 和后面属性的更新:

    

并再点击按钮时会关闭该extended editor:

Dialog Property Editor

除了用 Extended template 显示 inline editor, 我们可能打算用弹出对话框方式来显示(inline editor).

我们使用已存在的resource dictionary来创建一个对话框 editor 。

    public class CustomDialogEditor : DialogPropertyValueEditor
    {
        public CustomDialogEditor()
        {

this.InlineEditorTemplate = EditorDictionary.SimpleTextBox;

this.DialogEditorTemplate = EditorDictionary.HelloWorldListBox;
        }
    }

我们需要将custom dialog editor 绑定到MyStringProperty上:

    AddMemberAttributes(typeof(myControl), "MyStringProperty",
        new DescriptionAttribute("I am a property"),
        new DisplayNameAttribute("My String Property"),
        new CategoryAttribute("My Category"),
        new PropertyOrderAttribute(PropertyOrder.Early),
        PropertyValueEditor.CreateEditorAttribute(typeof(CustomDialogEditor)));

当在 Blend 中运行时:


    
     然后在该对话模中修改值也将会造成Textbox 和后面属性值的变化:

尽管Dialog property editors 与extended property editors有相似的API, 但却有一个主要不同– 它支持事务

(transactions).

您看到的“OK” 和“Cancel” 按钮就是基于触发CommitTransaction 或 AbortTransaction 命名而定义的.

那么, 如果我们在选择“World”之后点击 cancel,该属性会回复到它的原始值“foo”:

希望您从这个tutorial中学到一些知识,  它包括许多 knocks 和 crannies, 但这应该让您明确您自己要采用的方式,

-- Justin Angel

Microsoft Silverlight Toolkit Program Manager

译者:能看到这里的都不是凡人,起码你有时间一路阅读下来

好了,今天的内容就到这里。

原文链接:http://www.cnblogs.com/daizhj/archive/2008/11/27/1342175.html

作者: daizhj, 代震军

Tags: silverlight,blend,wpf,design-time,run-time,interface,Extensibility,设计时,运行时,扩展

网址: http://daizhj.cnblogs.com/

转载于:https://www.cnblogs.com/daizhj/archive/2008/12/11/1342175.html

【翻译】SILVERLIGHT设计时扩展(注:内容超长,请用IE浏览)相关推荐

  1. Android发送短信时 短信内容超长处理

    Android发送短信时 短信内容超长处理 一条短信只可容纳70个中文,所以当短信长度超过70个中文字符时程序就要特殊处理了. 通常有两种方式: 一.通过sendTextMessage()方法逐条依次 ...

  2. Silverlight设计时特性的使用

    1.[Category("MyCategory")]: 属性分组,让设计师能在"属性的海洋"中快速找到自己需要的属性 2.[EditorBrowsable(Ed ...

  3. VFP在运行时扩展报表系统,这是报表转换任意格式的秘决

    VFP在运行时扩展报表系统,这是报表转换任意格式的秘决 译者:Fbilo.其实你只要掌握了VFP9的报表系统,你就可以开发出报表转任意的文件格式. 除了在第六章"在设计时扩展报表系统&quo ...

  4. VS2010向工具箱中添加控件解决 Microsoft Communications Control,未能实例化 设计时授权

    第一步是Visual Studio 2010中添加注册控件的方法 在VC6.0中添加ADO Data Control等控件是很很方便的,"Project" --> " ...

  5. JAVA同时输入用户名和密码_用java模拟设计一个简单的“用户注册”程序。当用户输入用户名和密码时,单击“注...

    用java模拟设计一个简单的"用户注册"程序.当用户输入用户名和密码时,单击"注 2020 - 9 - 26 TAG : 所有功能均已实现,如有不满意的地方我再修改imp ...

  6. (翻译)设计人员应去除正文中的孤行内容的原因

      知不知道段落文本的排版方式会影响用户的阅读?设计人员可能会创造影响阅读的孤行内容.孤行是指出现在段落尾部的短行内容,它是由段落长度导致的.   用户阅读正文内容时会形成阅读节奏,而孤行内容会打断他 ...

  7. IE和Outlook Express的翻译插件设计

    IE和Outlook Express的翻译插件设计 摘要:本文主要介绍了在IE和Outlook Express中加入翻译插件的设计过程.对于IE,插件开发主要采用创建BHO对象,浏览器调用过程中载入C ...

  8. Docker网络体系结构:设计可扩展、可移植的Docker容器网络

    原文地址 译者:本人翻译水平有限,目的仅是为了学好Docker,如有错误请见谅. 翻译版本:v1.01(将不断优化翻译质量) 本文包含以下内容 Docker容器就是将应用及其所依赖运行环境的完整文件系 ...

  9. Windows 窗设计时结构

    Windows 窗设计时结构 Windows 窗体编程 设计时结构 下面的阐释概述了 .NET Framework 中的设计时结构. screen.width*0.7) {this.resized=t ...

最新文章

  1. 关于SSH远程连接报错
  2. python免费课程讲解-Python零基础免费入门课程
  3. Acwing第 28 场周赛【完结】
  4. 华中科技大学c语言作业答案,华中科技大学标准C语言程序设计及应用习题答案...
  5. java private 命名_java private关键字用法实例
  6. 微服务升级_SpringCloud Alibaba工作笔记0007---spring gateway搭建
  7. iOS开发 -------- Block技术中的weak - strong
  8. 可实现的python拟牛顿法的DFP算法
  9. [渝粤教育] 苏州科技大学 混凝土结构设计原理 参考 资料
  10. 阿帕奇服务器文件上传,windows基于阿帕奇+PHP服务器,实现vc++文件上传功能
  11. 应聘php面试自我介绍,应聘工作面试自我介绍
  12. 大写汉字转阿拉伯数字c语言,把中文汉字大写数字 转换成 阿拉伯数字
  13. Mixamo骨骼转为Unreal骨骼方法(1)
  14. 阿里范皓宇:互联网汽车会为汽车行业带来全新的用户价值
  15. shell脚本练习(随机取名)
  16. win10怎么在桌面添加计算机,win10怎么添加我的电脑(计算机)快捷方式到桌面
  17. 论文篇 | 2020-Facebook-DETR :利用Transformers端到端的目标检测=>翻译及理解(持续更新中)
  18. QT实现打印预览及生成Pdf功能
  19. 【SQL】MySQL的查询语句
  20. Fifth 补充堆空间的阐述,结构体的运用和复合体的简单描述

热门文章

  1. ubuntu中用wine安装office2007
  2. QuorumPeerMain数量可否和HRegionServer节点数量不一致?
  3. 有用的网址集合, IT杂谈
  4. php 频繁dom和 文件,性能优化之为什么不要频繁操作DOM
  5. dataframe数据标准化处理_数据预处理——标准化/归一化(实例)
  6. c++ opencv实现区域填充_帮你解锁一个新技能,opencv完美媲美PS,图片PS,我们代码实现...
  7. 入门Demo---SpringMVC学习笔记(二)
  8. apache 二级域名设置
  9. 转:java中static、final、static final的区别
  10. iOS 苹果app提交 ITC.apps.validation.prerelease_build_missing