您的位置:首页 > 编程语言

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

#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐