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

原文链接: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 { getset;  }
}


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

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


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

public class myControl : Control
{
    
public string MyStringProperty { getset;  }
}


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

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, Largenormal

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(null1100nullnull));



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

 
AddMemberAttributes(typeof(myControl), "MyIntProperty",
                    
new NumberRangesAttribute(null1100nullnull),
                    
new NumberIncrementsAttribute(0.515));


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


 AddMemberAttributes(typeof(myControl), "MyIntProperty",
                    
new NumberRangesAttribute(null1100nullnull),
                    
new NumberIncrementsAttribute(0.515),
                    
new NumberFormatAttribute("0'%'"null10));


   运行这个例子并采用普通 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/


     
posted @ 2008-12-11 09:05  代震军  阅读(3291)  评论(24编辑  收藏  举报