Silverlight控件的属性值编辑器
介绍
这是Silverlight工具包 2009年3月发布一系列的设计时间,在实施变化的一部分。 这篇文章讨论了如何增强属性编辑使用属性值编辑器和类型转换为Silverlight控件的经验。 我将开始描述的整体属性编辑架构在WPF / Silverlight设计器扩展框架,然后用在范例Silverlight工具包 2009年3月推出,以证明它是如何做,在Silverlight设计时代发展的独特的问题/过关。
属性编辑架构
可视化编辑对象的属性,是设计师的重要组成部分。 设计师通常不知道如何使自定义类型的属性(结构,类或接口),更提供了一个很好的编辑用户界面。 控制项开发人员通常需要提供的TypeConverter , PropertyValueEditor ,或两者兼而有之,提供渲染/编辑自定义类型的属性的XAML UI和系列化。
设计师可扩展框架定义了三种类型的属性值编辑器:内嵌编辑器,扩展编辑器,对话框编辑器,每个类实现:
编辑这些编辑的UI定义的DataTemplate 。 正在编辑的财产作为编辑的DataContext的PropertyValue类型。 编辑器UI通常绑定到编辑的三个属性通过一个PropertyValue : 价值 , StringValue的 ,或者收集的基本属性。
PropertyValueEditor
PropertyValueEditor持有一个单一的内联编辑器由定义InlineEditorTemplate财产。 内嵌编辑器属性窗口内出现。 下面是一个简单的例子的InlineEditorTemplate ,使用一个TextBox来显示和编辑属性:
x:Key ="TextBoxEditor" > <DataTemplate中 的x:Key =的“TextBoxEditor”> Text ="{Binding Path=Value}" /> <TextBox的Text =“{绑定路径=值}”/> > </ DataTemplate中 >
ExtendedPropertyValueEditor
x:Key ="inlineEditor" > <DataTemplate中 的x:Key =的“inlineEditor”> Content ="..." Command ="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}" /> < 按钮 内容 =“...” 命令 =“{X:的静态PropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}”/> > </ DataTemplate中 > x:Name ="slider" Value ="{Binding Path=Value}" /> < 滑块 :名称 =“滑块” 值 =“{绑定路径=值}”/> > </ DataTemplate中 >
DialogPropertyValueEditor
DialogPropertyValueEditor有两个编辑:内嵌编辑器,继承PropertyValueEditor ,一个额外的对话框编辑器定义DialogEditorTemplate财产。 通常弹出对话框编辑器由威盛内嵌编辑PropertyValueEditorCommands的 。 ShowDialogEditor命令。 下面是一个简单的例子:
x:Key ="inlineEditor" > < Button Content ="..." Command ="{x:Static <DataTemplate的 X:重点 “inlineEditor”> <Button 的Content =“...” 命令 =“{X:静态 ="Center" HorizontalAlignment ="Right" Margin ="0,0,4,4" /> < TextBox Text ="{Binding 名称:“VerticalAlignment =”中心“HorizontalAlignment =”右“ 保证金 ”0,0,4,4“/> <TextBox 的Text =”{结合
实现自定义的属性编辑器
为了实现Silverlight控件自定义属性编辑器:
- 实现一个自定义的属性编辑器类
- 继承,从PropertyValueEditor,ExtendedPropertyValueEditor,或DialogPropertyValueEditor
- 设置它的编辑模板(S): InlineEditorTemplate , ExtendedEditorTemplate ,和/或DialogEditorTemplate ;
- 与Silverlight控件的属性通过AddCustomAttributes调用自定义属性编辑器,类似关联
- 实施正确的属性值编辑器必须满足以下要求:
- 属性值编辑器的设计必须使内联编辑器和扩展编辑器部件可以单独使用。
- 属性值编辑器不能保存状态。 属性值编辑器是无状态的,可能是由一台主机实现缓存,并且可以重新使用多个属性值。
- 属性值编辑器不能假定在给定时间只有一个值编辑器(视图/联/扩展)部分控制处于活动状态。 例如,一个对话框,可视图的一部分,内嵌的一部分,同时积极扩展的用户界面的一部分。
- 属性值编辑器的一部分所实施的控制,不能存储状态。 作为一个值编辑器的一部分实现的控制不应该假定,它只会被绑定到一个属性值。 可循环使用的控制,改变不同的属性值。 如果更新的数据模型,是缓存的任何信息应该被刷新。
- 主机或它的父控件的属性值编辑器的一部分所实施的控制,不能使任何假设。 应使用唯一的通信机制是PropertyValue数据模型的DataContext的方式,标准的命令集。
同时引用WPF和Silverlight组件
一个设计大会是.NET / WPF大会由Visual Studio或混合加载,但它通常需要引用的Silverlight组件(至少在Silverlight控制组件,它提供了设计时功能)。 这可能会产生歧义参考设计组装项目:它有时需要参考在WPF和Silverlight相同的完全限定的类型说,在双方的WPF和Silverlight的System.Windows.dll中PresentationFramework.dll System.Windows.FrameworkElement。 为了避免混乱的Visual Studio,您可以使用extern别名区分WPF和Silverlight引用。
例如,下面的截图中看到Controls.DataVisualization.Toolkit.Design在Silverlight 3工具包项目,在2009年3月发布 :
- 的项目引用PresentationFramework的System.Windows,但System.Windows是根据Silverlight的别名,而不是默认的全局别名。 System.Windows参考下Silverlight的别名是坚持在csproj文件如下:
Include ="System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, < 参考 包括 =“System.Windows,版本= 2.0.5.0,文化=中立,公钥= 7cec85d7bea7798e,
- 在源代码中,我们添加
extern别名的Silverlight; 使用偏南=的Silverlight :: System.Windows;
通过偏南参考Silverlight的FrameworkElement的:: FrameworkElement的。
TextBoxEditor
有一个Controls.DataVisualization.Toolkit.Design项目的内联编辑器用于显示和编辑对象类型的名称属性TextBoxEditor区|酒吧|泡泡|专栏|线|派|散]系列和图表控件,如下所示:
在右边的属性窗口中的字段标题填写的文字自动显示在XAML设计的意见,并在中间。
的TextBoxEditor实施前的步骤:
- TextBoxEditor是的PropertyValueEditor的子类。 它规定在其默认构造InlineEditorTemplate 。 这里我使用的XAML代码,而不是兴建的 DataTemplate ,因为不知何故,我不能得到XAML建立,并在XAML中的一切都可以重写在C#反正。
- 它与在ChartMetadata.cs Chart.Title( * Metadata.cs文件的更多信息,请参阅Silverlight工具包的设计时功能的实现 )
TextBoxEditor.cs:
/ / /(C)版权所有Microsoft公司/ /此源是受微软公共许可协议(MS-PL)/ /详情请参阅http://go.microsoft.com/fwlink/?LinkID=131993。 /所有其他权利 TextBoxEditor : PropertyValueEditor { /// <summary> /// Preserve the constructor prototype from PropertyValueEditor. /// </summary> /// <param name="inlineEditorTemplate">Inline editor template.</param> public TextBoxEditor(DataTemplate inlineEditorTemplate) : base (inlineEditorTemplate) { } /// <summary> /// Default constructor builds the default TextBox inline editor template. /// </summary> public TextBoxEditor() { FrameworkElementFactory textBox = new FrameworkElementFactory( typeof (TextBox)); Binding binding = new Binding(); binding.Path = new PropertyPath( "Value" ); binding.Mode = BindingMode.TwoWay; textBox.SetBinding(TextBox.TextProperty, binding); DataTemplate dt = new DataTemplate(); dt.VisualTree = textBox; InlineEditorTemplate = dt; } } } {/ / / <summary> / / /简单的TextBox的行内编辑器/ / / </摘要> 公共部分类 TextBoxEditor。PropertyValueEditor {/ / / <summary> / / /保留从PropertyValueEditor的构造函数的原型/ / / </摘要> </ PARAM> 公共 TextBoxEditor(inlineEditorTemplate的DataTemplate):/ / / <param name="inlineEditorTemplate">的内联编辑模板。 基地 (inlineEditorTemplate){} / / / <summary> / / /默认构造函数建立了默认的TextBox联编辑模板/ / / </摘要> 公共 TextBoxEditor(){FrameworkElementFactory的TextBox = 新FrameworkElementFactory(typeof运算 (文本框));约束力= 新的绑定(); binding.Path = 新 PropertyPath的(“值”); binding.Mode = BindingMode.TwoWay; textBox.SetBinding(TextBox.TextProperty,装订); DataTemplate的DT = 新的DataTemplate(); dt.VisualTree = TextBox的; InlineEditorTemplate = DT;}}}
CultureInfoEditor
我的同事的RJ写道联为TimePicker.Culture财产的编辑CultureInfoEditor,因为默认的编辑经验,在Blend中的CultureInfo导致无效的XAML。 下面的截图显示CultureInfoEditor使用一个ComboBox显示所有的CultureInfo,并产生正确的XAML中。
CultureInfoEditor是比TextBoxEditor的更复杂的例子,真正显示了如何通过DataContext属性编辑器相关的基本属性。
CultureInfoEditor.cs:
/ / /(C)版权所有Microsoft公司/ /此源是受微软公共许可协议(MS-PL)/ /详情请参阅http://go.microsoft.com/fwlink/?LinkID=131993。 /所有其他权利 CultureInfoEditor : PropertyValueEditor { /// <summary> /// The ComboBox being used to edit the value. /// </summary> private ComboBox _owner; /// <summary> /// Preserve the constructor prototype from PropertyValueEditor. /// </summary> /// <param name="inlineEditorTemplate">Inline editor template.</param> public CultureInfoEditor(DataTemplate inlineEditorTemplate) : base (inlineEditorTemplate) { } /// <summary> /// Default constructor builds a ComboBox inline editor template. /// </summary> public CultureInfoEditor() { // not using databinding here because Silverlight does not support // the WPF CultureConverter that is used by Blend. FrameworkElementFactory comboBox = new FrameworkElementFactory( typeof (ComboBox)); comboBox.AddHandler( ComboBox.LoadedEvent, new RoutedEventHandler( (sender, e) => { _owner = (ComboBox) sender; _owner.SelectionChanged += EditorSelectionChanged; INotifyPropertyChanged data = _owner.DataContext as INotifyPropertyChanged; if (data != null ) { data.PropertyChanged += DatacontextPropertyChanged; } _owner.DataContextChanged += CultureDatacontextChanged; })); comboBox.SetValue(ComboBox.IsEditableProperty, false ); comboBox.SetValue(ComboBox.DisplayMemberPathProperty, "DisplayName" ); comboBox.SetValue(ComboBox.ItemsSourceProperty, CultureInfo.GetCultures(CultureTypes.SpecificCultures)); DataTemplate dt = new DataTemplate(); dt.VisualTree = comboBox; InlineEditorTemplate = dt; } /// <summary> /// Handles the SelectionChanged event of the owner control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> /// instance containing the event data.</param> private void EditorSelectionChanged( object sender, SelectionChangedEventArgs e) { // serialize with name. object DataContext = _owner.DataContext; DataContext .GetType() .GetProperty( "Value" , BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) .SetValue(DataContext, ((CultureInfo)_owner.SelectedItem).Name, new object [] { }); } /// <summary> /// Handles the PropertyChanged event of the context object. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param> private void DatacontextPropertyChanged( object sender, PropertyChangedEventArgs e) { // deserialize from name. if (e.PropertyName == "Value" ) { object value = sender .GetType() .GetProperty( "Value" , BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) .GetValue(sender, new object [] { }); if ( value != null ) { if ( value is string ) { CultureInfo setCulture = new CultureInfo( value .ToString()); _owner.SelectedItem = setCulture; } } } } /// <summary> /// Called when the context is changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> private void CultureDatacontextChanged( object sender, DependencyPropertyChangedEventArgs e) { INotifyPropertyChanged old = e.OldValue as INotifyPropertyChanged; if (old != null ) { old.PropertyChanged -= DatacontextPropertyChanged; } INotifyPropertyChanged newDataContext = e.NewValue as INotifyPropertyChanged; if (newDataContext != null ) { newDataContext.PropertyChanged += DatacontextPropertyChanged; } } } } 。{/ / / <summary>的CultureInfo的编辑器/ / / / / / </摘要> / / / <remarks>目前不支持绑定XAML编辑器</备注> 公共类 CultureInfoEditor:PropertyValueEditor {/ / / <summary> / / / ComboBox的编辑值/ / / </摘要> 私人的ComboBox _owner; / / / <summary> / / /保留从PropertyValueEditor的构造函数的原型/ / / </摘要> </ PARAM> 公众 CultureInfoEditor(inlineEditorTemplate的DataTemplate):/ / / <param name="inlineEditorTemplate">内嵌编辑模板。 基地 (inlineEditorTemplate){} / / / <summary> / / /默认构造函数生成一个ComboBox内嵌编辑模板。 / / / </摘要> 公共 CultureInfoEditor(){/ /不使用绑定在这里,因为Silverlight不支持/ / WPF CultureConverter的混合使用的组合框FrameworkElementFactory =: 新FrameworkElementFactory(typeof运算 (组合)); comboBox.AddHandler( ComboBox.LoadedEvent, 新 RoutedEventHandler((发件人,E)=> {_owner =(组合)发件人; _owner.SelectionChanged + = EditorSelectionChanged INotifyPropertyChanged的数据= INotifyPropertyChanged的_owner.DataContext;的!(数据= NULL){data.PropertyChanged + = DatacontextPropertyChanged;} _owner.DataContextChanged + = CultureDatacontextChanged的;})); comboBox.SetValue(ComboBox.IsEditableProperty, 虚假 )的的comboBox.SetValue(ComboBox.DisplayMemberPathProperty,“显示名称”); comboBox.SetValue(ComboBox.ItemsSourceProperty,CultureInfo.GetCultures (CultureTypes.SpecificCultures));的DataTemplate DT = 新的DataTemplate(); dt.VisualTree =组合框; InlineEditorTemplate = DT;} / / / <summary> / / /处理SelectionChanged事件的所有者控制/ / / </摘要> / / / <param name="sender">的事件源。</ PARAM> / / / <param name="e">的的<see cref="System.Windows.Controls.SelectionChangedEventArgs"/> / / /实例包含事件数据</ PARAM> 私人无效 EditorSelectionChanged的( 对象发件人,SelectionChangedEventArgs E){/ /名称序列化 对象的DataContext = _owner.DataContext;的DataContext。的GetType()的getProperty(“价值”的BindingFlags。公共| BindingFlags.Instance | BindingFlags.GetProperty)的SetValue(DataContext的,((的CultureInfo)_owner.SelectedItem)名称, 新的对象 [] {});} / / /的<summary> / / /处理PropertyChanged事件上下文对象。/ / / </摘要> / / / <param name="sender">的事件源。</ PARAM> / / / <param name="e">的<CREF =“系统。 ComponentModel.PropertyChangedEventArgs“/>包含事件数据的实例。</ PARAM> 私人无效 DatacontextPropertyChanged的( 对象发件人,PropertyChangedEventArgsé){/ /反序列化的名字。(e.PropertyName ==”值“){ 对象值 =发件人。 。的GetType()的getProperty(“价值”,BindingFlags.Public可BindingFlags.Instance | BindingFlags.GetProperty)的GetValue(发件人, 新对象 [] {}); 如果 ( 值= NULL!){ 如果 ( 值是字符串 ){ CultureInfo的setCulture = 新的CultureInfo(值的ToString()); _owner.SelectedItem setCulture;}}}} <summary> / / / / / /调用的上下文改变时/ / / </摘要> / / / <参数名称=“发件人”发件人。</ PARAM> / / / <param name="e">的的<see cref="System.Windows.DependencyPropertyChangedEventArgs"/>实例包含事件数据。</ PARAM> 私人无效 CultureDatacontextChanged( 对象发件人,DependencyPropertyChangedEventArgs E){INotifyPropertyChanged的老= e.OldValue INotifyPropertyChanged的;(老= NULL){old.PropertyChanged - = DatacontextPropertyChanged;} INotifyPropertyChanged的newDataContext = e.NewValue INotifyPropertyChanged的, 如果 (newDataContext = NULL ){newDataContext.PropertyChanged的+ = DatacontextPropertyChanged的;}}}}
ExpandableObjectConverter
在开始讨论,除了自定义属性值编辑器,有时你可以使用适当的类型转换器,以提供良好的编辑体验和XAML系列化。 一个例子是ColumnSeries.DependentRangeAxis:它的IRangeAxis型,混合不知道如何编辑它,所以它显示DependentRangeAxis只在属性面板中读取。 通过关联ExpandableObjectConverter到ColumnSeries.DependentRangeAxis:
b.AddCustomAttributes( Extensions.GetMemberName <ColumnSeries>(=> x.DependentRangeAxis的), (ExpandableObjectConverter))); 新TypeConverterAttribute(typeof运算 (ExpandableObjectConverter))); b.AddCustomAttributes( Extensions.GetMemberName <ColumnSeries>(=> x.IndependentAxis的), (ExpandableObjectConverter))); 新TypeConverterAttribute(typeof运算 (ExpandableObjectConverter)));
Blend中显示一个新的按钮,旁边这个属性在属性面板,点击时,它会弹出“选择对象”对话框,过滤与属性的类型IRangeAxis:
结论
Blend和Silverlight的允许设计师直接对实际控制创造惊人的UI,所以它是非常重要的,控制开发作为整体控制设计和实施的一部分设计师的经验。 有一个自定义的属性编辑器提供了很好的编辑界面,并产生正确的XAML是一个设计师经验的重要组成部分。 希望这篇文章可以帮助您了解如何创建自定义属性编辑器。 谢谢!








最新评论