{S0} {S1}简介
这是我的第二条关于PropertyGrid控制,这为WPF的时间。基础上,我将我以前的文章:" quot;
虽然使用Windows Workflow Foundation 4.0的工作,我意识到,PropertyInspectorView控制确实是一个不折不扣的WPF PropertyGrid控制,包括自定义属性和自定义编辑器的支持。一个PropertyInspectorView与父WorkflowDesigner对象和同级工作流视图对象,这是真正的拖动和放下画布,在这个MSDN的截图所示:{S2}
Workflow Foundation的例子。左:工具箱活动,中东:设计视图,右:物业督察。内部架构
PropertyInspector一般用途的整体方法如下:从网格类派生一个新的控制。该网格将包含真正的UI元素。合并为一个私有的类成员的工作流基金会的WorkflowDesigner对象。
对于设计师的对象,添加相应网格的孩子PropertyInspectorView。虽然它是一个网格暴露出来,真正的类型是ProperyInspector。
通过反思,为进一步利用捕捉一些PropertyInspector方法。
实施SelectedObject和SelectedObjects属性,在定期的PropertyGrid,处理的选择改变PropertyInspector。添加GridSplitter和TextBlock模仿原PropertyGrid的HelpText功能。
下图描绘的WpfPropertyGrid类的内部结构,在前面的行解释:
{S3}
它至少需要调用公共构造和设置SelectedObject或SelectedObjects。刷新时选定的对象已经改变外部控制RefreshPropertyList方法将是有益的的。
设置的HelpVisible属性显示在底部的属性描述,而ToolbarVisible将显示或隐藏上部的工具栏。这些属性有相同的名称为WINFORM的PropertyGrid中,为了保持某种程度的兼容性。
PropertySort财产,接受PropertySort枚举类型相同。它将允许按类别分组的属性,或者在一个扁平化的方式显示。
FontAndColorData财产可用于给页面的控制,因为它内部设置WorkflowDesigner.PropertyInspectorFontAndColorData的属性,但有几个信息在互联网上提供。下面是一些有趣的论坛MSDN页面:基本用法 - Person类
提供的示范项目,将允许您测试的所有WpfropertyGrid功能。有三个类定义中DemoClasses.cs,具有不同的功能,如自定义属性和编辑。这里的第一个也是最简单的声明:
的
{五} public class Person
{
public enum Gender { Male, Female }
#region private fields
private string[] _Names = new string[3];
#endregion
// The following properties are wrapping an array of strings
#region Public Properties
[Category("Name")]
[DisplayName("First Name")]
public string FirstName
{
set { _Names[0] = value; }
get { return _Names[0]; }
}
[Category("Name")]
[DisplayName("Mid Name")]
public string MidName
{
set { _Names[1] = value; }
get { return _Names[1]; }
}
[Category("Name")]
[DisplayName("Last Name")]
public string LastName
{
set { _Names[2] = value; }
get { return _Names[2]; }
}
// The following are auto-implemented properties (C# 3.0 and up)
[Category("Characteristics")]
[DisplayName("Gender")]
public Gender PersonGender { get; set; }
[Category("Characteristics")]
[DisplayName("Birth Date")]
public DateTime BirthDate { get; set; }
[Category("Characteristics")]
public int Income { get; set; }
// Other cases of hidden read-only property and formatted property
[DisplayName("GUID"), ReadOnly(true), Browsable(true)]
public string GuidStr
{
get { return Guid.ToString(); }
}
[Browsable(false)] // this property will not be displayed
public System.Guid Guid
{
get;
private set;
}
#endregion
public Person()
{
// default values
for (int i = 0; i < 3; i++)
_Names[i] = "";
this.PersonGender = Gender.Male;
this.Guid = System.Guid.NewGuid();
}
public override string ToString()
{
return string.Format("{0} {1} {2}", FirstName,
MidName, LastName).Trim().Replace(" ", " ");
}
}
请注意,该控件将显示刚才的属性,而不是等领域。由于我们使用的是C#3.0或4.0,我们能够避免使用自动实现的属性声明的基础领域,只要是方便。
要显示一个Person对象的属性是安静简单,只需将它分配给控制的SelectedObject属性,如下所示:
{C}基本属性
Person类中实现的,你会发现有一些属性,有属性(方括号中的),他们将不会有任何对您的类的行为的效果,但会与属性网格。这些属性是类似的WinForms的PropertyGrid的实施。让我们看看详细。
分类:让您指定为受影响的物业类别组。 A类默认情况下会出现带有灰色背景的属性网格的,你可以看到在第一个屏幕。如果该属性没有属性分类,它属于一个空白的类别组,与在以前的截图的GUID属性。建议总是指定为每个属性的类别。
DisplayName的:当你想显示一个属性的名称,从实际不同,将是有益的。通常情况下,它是用来当你用空格来增加可读性,或缩写名称。只读:设置为true时,将防止从正在编辑的财产;这将是刚刚在属性网格中显示。为了防止被隐藏的只读属性,将必要的,以纪念他们可浏览= TRUE,与GUIDStr财产。可浏览:设置为false时,该属性将不会显示。当你有像第一个例子中的GUID属性,你不想显示在所有的属性,它是有用的。
所有这些属性在System.ComponentModel命名空间声明和自动属性检查确认。可自定义的属性 - 车辆类别
,虽然简单的实现了WpfPropertyGrid公开一类(除可浏览属性设置为false)的所有属性,ICustomProperties接口将允许有条件地揭露一些属性。有做到这一点需要在下面的例子,一些自定义:
{中六}
{七}{S8}
public class Vehicle :
ICustomTypeDescriptor, INotifyPropertyChanged
{
public enum CarType { Sedan, StationWagon, Coupe,
Roadster, Van, Pickup, Truck }
public enum CarBrand { Acura, Audi, BMW, Citroen,
Ford, GMC, Honda, Lexus, Mercedes, Mitsubishi,
Nissan, Porshe, Suzuki, Toyota, VW, Volvo }
#region Private fields
private CarType _TypeOfCar;
#endregion
#region Public Properties
[Category("Classification")]
public CarBrand Brand { get; set; }
[Category("Classification")]
[DisplayName("Type")]
public CarType TypeOfCar
{
get { return this._TypeOfCar; }
set {
this._TypeOfCar = value;
NotifyPropertyChanged("TypeOfCar");
}
}
[Category("Classification")]
public string Model { get; set; }
[Category("Identification")]
[DisplayName("Manuf.Year")]
public int Year { get; set; }
[Category("Identification")]
[DisplayName("License Plate")]
public string Plate { get; set; }
// Will shown only for Pickup and Truck
[Category("Capacity")]
[DisplayName("Volume (ft鲁)")]
public int Volume { get; set; }
[Category("Capacity")]
[DisplayName("Payload (kg)")]
public int Payload { get; set; }
[Category("Capacity")]
[DisplayName("Crew cab?")]
public bool CrewCab { get; set; }
#endregion
#region ICustomTypeDescriptor Members
public AttributeCollection GetAttributes() ...
public string GetClassName() ...
public string GetComponentName() ...
public TypeConverter GetConverter() ...
public EventDescriptor GetDefaultEvent() ...
public PropertyDescriptor GetDefaultProperty() ...
public object GetEditor(Type editorBaseType)
public EventDescriptorCollection
GetEvents(Attribute[] attributes) ...
public EventDescriptorCollection GetEvents() ...
public object
GetPropertyOwner(PropertyDescriptor pd) ...
public PropertyDescriptorCollection
GetProperties(Attribute[] attributes) ...
// Method implemented to expose Capacity properties
// conditionally, depending on TypeOfCar
public PropertyDescriptorCollection GetProperties()
{
var props = new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor prop in
TypeDescriptor.GetProperties(this, true))
{
if (prop.Category=="Capacity" &&
(this.TypeOfCar != CarType.Pickup &&
this.TypeOfCar != CarType.Truck))
continue;
props.Add(prop);
}
return props;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
}
注意,最重要的方法需要实施PropertyGrideCE.ICustomProperties接口的是GetProperties()。此方法应返回作为一个数组,取决于一些条件,要公开所有的属性名称。在这个例子中,如果汽车类型的皮卡或卡车,体积,有效载荷和CrewCab属性将被暴露。
自定义编辑器 - Place类免责声明
虽然本文的目的不是去与编辑器定制深,我将展示一个夫妇的两个编辑的例子:扩展和基于对话框的。更多的信息可以发现沿与Workflow Foundation的4个API,或者在我的下一篇文章:)
自定义编辑器是这种控制的最强大的功能。有几个技巧,你可以用它做。默认情况下,控制系统将提供所有的基础类的编辑器:整数,浮点,双等,也可用于字符串和枚举,后者作为一个ComboBox。如果你有一个自定义类作为属性的对象,它会显示字符串表示形式,但只是为ReadOnly,电网的控制,因为不知道如何编辑它。
Place类实现同时显示。尽管主编,它必须从PropertyValueEditor类派生,我们将在详细见后面。
为了指定一个自定义编辑器的属性,它是需要添加一个EditorAttribute属性的财产申报,CountryInfo和图片属性。
public class Place
{
public struct CountryInfo
{
public static readonly CountryInfo[] Countries = {
// African countries
new CountryInfo(Continent.Africa , "AO", "ANGOLA" ),
new CountryInfo(Continent.Africa, "CM", "CAMEROON" ),
// American countries
new CountryInfo(Continent.America, "MX", "MEXICO" ),
new CountryInfo(Continent.America, "PE", "PERU" ),
// Asian countries
new CountryInfo(Continent.Asia, "JP", "JAPAN" ),
new CountryInfo(Continent.Asia, "MN", "MONGOLIA" ),
// European countries
new CountryInfo(Continent.Europe, "DE", "GERMANY" ),
new CountryInfo(Continent.Europe, "NL", "NETHERLANDS" ),
// Oceanian countries
new CountryInfo(Continent.Oceania, "AU", "AUSTRALIA" ),
new CountryInfo(Continent.Oceania, "NZ", "NEW ZEALAND" )
};
public Continent Contin { get; set; }
public string Abrev { get; set; }
public string Name { get; set; }
public override string ToString()
{
return string.Format("{0} ({1})", Name, Abrev);
}
public CountryInfo(Continent _continent,
string _abrev, string _name) : this()
{
this.Contin = _continent;
this.Abrev = _abrev;
this.Name = _name;
}
}
#region Private fields
private string[] _Address = new string[4];
#endregion
#region Public properties
[Category("Address")]
public string Street
{
get { return _Address[0]; }
set { _Address[0] = value; }
}
[Category("Address")]
public string City
{
get { return _Address[1]; }
set { _Address[1] = value; }
}
[Category("Address")]
public string Province
{
get { return _Address[2]; }
set { _Address[2] = value; }
}
[Category("Address")]
public string Postal
{
get { return _Address[3]; }
set { _Address[3] = value; }
}
// Custom editor for the following 2 properties
[Category("Address")]
[Editor(typeof(CountryEditor), typeof(PropertyValueEditor))]
public CountryInfo Country { get; set; }
[Category("Characteristics")]
[Editor(typeof(PictureEditor), typeof(PropertyValueEditor))]
public BitmapImage Picture { get; set; }
[Category("Characteristics")]
public int Floors { get; set; }
[Category("Characteristics")]
public int CurrentValue { get; set; }
#endregion
public Place()
{
for (int i = 0; i < _Address.Length; i++)
_Address[i] = string.Empty;
this.Country = CountryInfo.Countries[0];
}
}
如前所述,有在广场类的自定义编辑器实现的两个例子,第一个,CountryEditor,是一个扩展的编辑器。它要求一个国家有两个组合框:一个大陆,为国家之一,如截图所示。为了简化演示,所需的XAML的DataTemplate是放置在源代码的文件,这是不使平常:
class CountryEditor : ExtendedPropertyValueEditor
{
public CountryEditor()
{
// Template for normal view
string template1 = @"...xaml template here...";
// Template for extended view. Shown when dropdown button is pressed.
string template2 = @"...xaml template here...";
// Load templates
using (var sr = new MemoryStream(Encoding.UTF8.GetBytes(template1)))
{
this.InlineEditorTemplate = XamlReader.Load(sr) as DataTemplate;
}
using (var sr = new MemoryStream(Encoding.UTF8.GetBytes(template2)))
{
this.ExtendedEditorTemplate = XamlReader.Load(sr) as DataTemplate;
}
}
}
扩展编辑器,它必须从ExtendedPropertyValueEditor类派生。这将允许属性网格下拉属性数据输入一个自定义控制。
构造函数应该载入模板,正常和扩展,从一些XAML的DataTemplate声明。通常这些模板被放置在一个XAML资源文件。
自定义编辑器的第二个例子是PictureEditor,它是从一个扩展编辑器不同,因为它显示了一个新的对话框的下拉按钮被按下时,使这将需要单独实施该窗口。此外,来自不同的基类:DialogPropertyValueEditor。 Sample类的缩写,目的是显示部分:
多重选择 class PictureEditor : DialogPropertyValueEditor
{
// Window to show the current image and optionally pick a different one
public class ImagePickerWindow : Window
{
// regular window implementation here
}
public PictureEditor()
{
string template = @"...xmal template here...";
using (var sr = new MemoryStream(Encoding.UTF8.GetBytes(template)))
{
this.InlineEditorTemplate = XamlReader.Load(sr) as DataTemplate;
}
}
// Open the dialog to pick image, when the dropdown button is pressed
public override void ShowDialog(PropertyValue propertyValue, IInputElement commandSource)
{
ImagePickerWindow window = new ImagePickerWindow(propertyValue.Value as BitmapImage);
if (window.ShowDialog().Equals(true))
{
var ownerActivityConverter = new ModelPropertyEntryToOwnerActivityConverter();
ModelItem activityItem = ownerActivityConverter.Convert(propertyValue.ParentProperty,
typeof(ModelItem), false, null) as ModelItem;
using (ModelEditingScope editingScope = activityItem.BeginEdit())
{
propertyValue.Value = window.TheImage;
editingScope.Complete(); // commit the changes
}
}
}
}
虽然单一的选择可以通过SelectedObject属性设置为任何值,多重选择是通过设置SelectedObjects属性。
当选择多个对象,在控制顶部的类型标签将显示单词quot; LT; multiplegt"类型rigth。如果所有选定的对象都是同一类型,类型名称显示(见下面的截图)。如果不是,它是类型quot; Objectquot;
的所有属性,具有相同的类型和所有选定对象的名称所示,即使选定的对象是不是同一类型的。在演示应用,尝试用人员和地点,共享FirstName和LastName属性。
{S0}帮助文本
控制底部的文本框被称为HelpText。它会显示DescriptionAttribute属性(见上面的截图)设置的属性的描述。
当有多个选定的对象,说明将只显示所有选定对象是同一类型的。
HelpText框可以显示或隐藏设置PropertyGrid的HelpVisible属性。
如何使用它
WpfPropertyGrid可以被直接嵌入到您的应用程序。它并不需要在一个单独的DLL编译。包括一些XAML声明,你必须指定正确的命名空间(本地System.Windows.Control),并添加相应的标签到您的WPF窗口或对话框:依赖项属性
由于控制属性都是依赖属性,它们可以被绑定到其他元素的容器对话框或窗口在演示应用程序(简体),如:<Window x:Class="WpfPropertyGrid_Demo.MainWindow"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:wpg="clr-namespace:System.Windows.Controls"
Title="WpfPropertyGrid Demo" mc:Ignorable="d" ResizeMode="CanResizeWithGrip"
Width="360" Height="360" MinWidth="360" MinHeight="400">
<wpg:WpfPropertyGrid x:Name="PropertyGrid1"
Margin="20,20,118,21" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HelpVisible="{Binding ElementName=ShowDescrip, Path=IsChecked}"
ToolbarVisible="{Binding ElementName=ShowToolbar, Path=IsChecked}"
PropertySort="{Binding ElementName=ComboSort, Path=SelectedItem}" />
演示应用已建立与Visual Studio 2010。由于WPF属性检查器是一个。NET 4.0中的新功能,这将不会被执行。NET 3.0或3.5编写的应用程序,甚至当他们实施Workflow Foundation的。
使用此控件,你只需要添加到您的项目WpfPropertyGrid.cs的文件。在您的解决方案将需要一些参考:
System.ActivitiesSystem.Activities.Core.Presentation
System.Activities.Presentation历史
2010年6月14日,第一版。2010年8月31日,第二版。简体实施(感谢Drammy和brannonking)2010年9月13日,主要改进:多种选择,帮助文本框,延长演示。2011年7月12日:第四版。依赖属性,显示/隐藏工具栏和categories.nbsp;