Silverlight表单控件实现自动属性编辑代码讲解
2011-07-16 21:05
453 查看
我们将结合FreeForm表单设计器的实际设计来说明。
[设想]
我们的设想是在选择控件的时候,控件的属性通过反射自动体现一个属性表格中,这样可以通过属性表格来更改控件的属性,就像VS.Net的IDE做到的那样。
[分析]
控件属性显示的重点实现代码讲解
我们设计的有2个重要的类
重要的类:PropertyItem
:这个是属性的基础元素,只实现INotifyPropertyChanged接口,PropertyItem的构造函数定义了一个对象实例_instance,将一个反射属性字段_propertyInfo赋值:
public PropertyItem(object instance, object value, PropertyInfo property, bool readOnly)
{
_instance = instance;
_propertyInfo = property;
_value = value;
_readOnly = readOnly;
if (instance is INotifyPropertyChanged)
((INotifyPropertyChanged)instance).PropertyChanged += new PropertyChangedEventHandler(PropertyItem_PropertyChanged);
}
重要的类:PropertyGrid
:这个是自定义的控件,继承自System.Windows.Controls.ContentControl,其中在继承OnApplyTemplate的时候,引用了一个
重要的方法this.ResetObject。
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.LayoutRoot = (ScrollViewer)this.GetTemplateChild("LayoutRoot");
this.MainGrid = (Grid)this.GetTemplateChild("MainGrid");
loaded = true;
if (resetLoadedObject)
{
resetLoadedObject = false;
this.ResetObject(this.SelectedObject);
}
}
这个ResetObject方法的代码:
void ResetObject(object obj)
{
this.ResetMainGrid();
int rowCount = this.SetObject(obj);
if (rowCount > 0)
AddGridSplitter(rowCount);
}
SetObject返回一个整数,代表的意思就是传入对象的属性数量,这个SetObject是重要的入口方法,代码如下:
int SetObject(object obj)
{
List<PropertyItem> props = new List<PropertyItem>();
int rowCount = -1;
// Parse the objects properties
props = PropertyGrid.ParseObject(obj);
var categories = (from p in props
orderby p.Category
select p.Category).Distinct();
foreach (string category in categories)
{
this.AddHeaderRow(category, ref rowCount);
var items = from p in props
where p.Category == category
orderby p.Name
select p;
foreach (var item in items)
this.AddPropertyRow(item, ref rowCount);
}
return rowCount++;
}
通过ParseObject组合了PropertyItem的列表
static List<PropertyItem> ParseObject(object objItem)
完整代码:
View Code
FreeForm控件属性编辑
点击其中一个控件,然后打开 “Property”页,可以看到控件的属性
细节:
可以进入一些相关复合属性
比如动作触发规则属性:
再比如控件属性
我们可以即时修改,效果:
Demo:
http://crmwin.com/FreeForm2011TestPage.html
.
我们的网站(昕友软件):http://crmwin.com
[设想]
我们的设想是在选择控件的时候,控件的属性通过反射自动体现一个属性表格中,这样可以通过属性表格来更改控件的属性,就像VS.Net的IDE做到的那样。
[分析]
控件属性显示的重点实现代码讲解
我们设计的有2个重要的类
重要的类:PropertyItem
:这个是属性的基础元素,只实现INotifyPropertyChanged接口,PropertyItem的构造函数定义了一个对象实例_instance,将一个反射属性字段_propertyInfo赋值:
public PropertyItem(object instance, object value, PropertyInfo property, bool readOnly)
{
_instance = instance;
_propertyInfo = property;
_value = value;
_readOnly = readOnly;
if (instance is INotifyPropertyChanged)
((INotifyPropertyChanged)instance).PropertyChanged += new PropertyChangedEventHandler(PropertyItem_PropertyChanged);
}
重要的类:PropertyGrid
:这个是自定义的控件,继承自System.Windows.Controls.ContentControl,其中在继承OnApplyTemplate的时候,引用了一个
重要的方法this.ResetObject。
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.LayoutRoot = (ScrollViewer)this.GetTemplateChild("LayoutRoot");
this.MainGrid = (Grid)this.GetTemplateChild("MainGrid");
loaded = true;
if (resetLoadedObject)
{
resetLoadedObject = false;
this.ResetObject(this.SelectedObject);
}
}
这个ResetObject方法的代码:
void ResetObject(object obj)
{
this.ResetMainGrid();
int rowCount = this.SetObject(obj);
if (rowCount > 0)
AddGridSplitter(rowCount);
}
SetObject返回一个整数,代表的意思就是传入对象的属性数量,这个SetObject是重要的入口方法,代码如下:
int SetObject(object obj)
{
List<PropertyItem> props = new List<PropertyItem>();
int rowCount = -1;
// Parse the objects properties
props = PropertyGrid.ParseObject(obj);
var categories = (from p in props
orderby p.Category
select p.Category).Distinct();
foreach (string category in categories)
{
this.AddHeaderRow(category, ref rowCount);
var items = from p in props
where p.Category == category
orderby p.Name
select p;
foreach (var item in items)
this.AddPropertyRow(item, ref rowCount);
}
return rowCount++;
}
通过ParseObject组合了PropertyItem的列表
static List<PropertyItem> ParseObject(object objItem)
完整代码:
View Code
#region Using Directives using System; using System.ComponentModel; using System.Linq; using System.Reflection; using SLPropertyGrid.Converters; #endregion #region PropertyItem /// <summary> /// PropertyItem hold a reference to an individual property in the propertygrid /// </summary> public sealed class PropertyItem : INotifyPropertyChanged { #region Events /// <summary> /// Event raised when an error is encountered attempting to set the Value /// </summary> public event EventHandler<ExceptionEventArgs> ValueError; /// <summary> /// Raises the ValueError event /// </summary> /// <param name="ex">The exception</param> private void OnValueError(Exception ex) { if (null != ValueError) ValueError(this, new ExceptionEventArgs(ex)); } #endregion #region Fields private PropertyInfo _propertyInfo; private object _instance; private bool _readOnly = false; #endregion #region Constructors /// <summary> /// Constructor /// </summary> /// <param name="instance"></param> /// <param name="property"></param> public PropertyItem(object instance, object value, PropertyInfo property, bool readOnly) { _instance = instance; _propertyInfo = property; _value = value; _readOnly = readOnly; if (instance is INotifyPropertyChanged) ((INotifyPropertyChanged)instance).PropertyChanged += new PropertyChangedEventHandler(PropertyItem_PropertyChanged); } void PropertyItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == this.Name) Value = _propertyInfo.GetValue(_instance, null); } #endregion #region Properties public string Name { get { return _propertyInfo.Name; } } public string DisplayName { get { if (string.IsNullOrEmpty(_displayName)) { DisplayNameAttribute attr = GetAttribute<DisplayNameAttribute>(_propertyInfo); _displayName = (attr != null) ? attr.DisplayName : Name; } return _displayName; } } private string _displayName; public string Category { get { if (string.IsNullOrEmpty(_category)) { CategoryAttribute attr = GetAttribute<CategoryAttribute>(_propertyInfo); if (attr != null && !string.IsNullOrEmpty(attr.Category)) _category = attr.Category; else _category = "Misc"; } return this._category; } } private string _category; public object Value { get { return _value; } set { if (_value == value) return; object originalValue = _value; _value = value; try { Type propertyType = this._propertyInfo.PropertyType; if (((propertyType == typeof(object)) || ((value == null) && propertyType.IsClass)) || ((value != null) && propertyType.IsAssignableFrom(value.GetType()))) { _propertyInfo.SetValue(_instance, value, (BindingFlags.NonPublic | BindingFlags.Public), null, null, null); OnPropertyChanged("Value"); } else { try { if (propertyType.IsEnum) { object val = Enum.Parse(_propertyInfo.PropertyType, value.ToString(), false); _propertyInfo.SetValue(_instance, val, (BindingFlags.NonPublic | BindingFlags.Public), null, null, null); OnPropertyChanged("Value"); } else { TypeConverter tc = TypeConverterHelper.GetConverter(propertyType); if (tc != null) { object convertedValue = tc.ConvertFrom(value); _propertyInfo.SetValue(_instance, convertedValue, null); OnPropertyChanged("Value"); } else { // try direct setting as a string... _propertyInfo.SetValue(_instance, value.ToString(), (BindingFlags.NonPublic | BindingFlags.Public), null, null, null); OnPropertyChanged("Value"); } } } catch (Exception ex) { _value = originalValue; OnPropertyChanged("Value"); OnValueError(ex); } } } catch (MethodAccessException mex) { _value = originalValue; _readOnly = true; OnPropertyChanged("Value"); OnPropertyChanged("CanWrite"); OnValueError(mex); } } } private object _value; public Type PropertyType { get { return _propertyInfo.PropertyType; } } public bool CanWrite { get { return _propertyInfo.CanWrite && !_readOnly; } } public bool ReadOnly { get { return _readOnly; } internal set { _readOnly = value; } } #endregion #region Helpers public static T GetAttribute<T>(PropertyInfo propertyInfo) { var attributes = propertyInfo.GetCustomAttributes(typeof(T), true); return (attributes.Length > 0) ? attributes.OfType<T>().First() : default(T); } public T GetAttribute<T>() { return GetAttribute<T>(_propertyInfo); } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName"); PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } #endregion } #endregion
FreeForm控件属性编辑
属性编辑
我们打开一个表单模板,或者自己新建一个模板,拖放一些控件,例如下图:点击其中一个控件,然后打开 “Property”页,可以看到控件的属性
细节:
可以进入一些相关复合属性
比如动作触发规则属性:
再比如控件属性
即时属性修改
我们在选择一个控件,修改属性的时候,能够立即在表单设计器中产生效果,比如控件的标签:我们可以即时修改,效果:
Demo:
http://crmwin.com/FreeForm2011TestPage.html
.
我们的网站(昕友软件):http://crmwin.com
相关文章推荐
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)