1 前面已经谈到UI通过对象属性的名称从对象中获取值,UI控件的值是在changed 事件后赋给对象。因为对象实体是已知的,UI控件的值对应的是对象的属性,所以根据对象中属性名称获取值是比较容易的。当然设置对象与获取对象的值,都在对象实体中有相应的方法。这个方法比较通用,现在有不少架构都这么写.下面两个方法都使用了反射对property进行读写.
UI 与对象实体的通信
1 前面已经谈到UI通过对象属性的名称从对象中获取值,UI控件的值是在changed 事件后赋给对象。因为对象实体是已知的,UI控件的值对应的是对象的属性,所以根据对象中属性名称获取值是比较容易的。当然设置对象与获取对象的值,都在对象实体中有相应的方法。这个方法比较通用,现在有不少架构都这么写.下面两个方法都使用了反射对property进行读写.
public object GetValue(string propertyName)
{
Type t;
System.Reflection.PropertyInfo p;
System.Reflection.MethodInfo m;
object rtn;
if ((propertyName == "UniqueID"))
{
return mUniqueID;
}
string nestedPropertyName = string.Empty;
bool nestedProperty = false;
int dotIndex;
dotIndex = propertyName.IndexOf('.');
if ((dotIndex > -1))
{
nestedPropertyName = propertyName.Substring(dotIndex + 1);
propertyName = propertyName.Substring(0, dotIndex);
nestedProperty = true;
}
t = this.GetType();
p = t.GetProperty(propertyName);
if (p != null)
{
m = p.GetGetMethod(false);
if ((m == null))
{
throw new Exception("Property: " + t.Name + "." + propertyName + " has no GET method.");
}
}
else
{
m = t.GetMethod(propertyName);
if ((m == null))
{
throw new Exception("Function: " + t.Name + "." + propertyName + " does not exist.");
}
}
rtn = m.Invoke(this, null);
if ((!((rtn == null)) && nestedProperty))
{
rtn = ((BizObject)(rtn)).GetValue(nestedPropertyName);
}
return rtn;
}
public void SetProperty(string propertyName, object value)
{
try
{
Type t;
System.Reflection.PropertyInfo p;
System.Reflection.MethodInfo m;
string nestedPropertyName = string.Empty;
bool nestedProperty = false;
int dotIndex;
System.Exception Ex;
t = this.GetType();
dotIndex = propertyName.IndexOf('.');
if ((dotIndex > -1))
{
nestedPropertyName = propertyName.Substring(dotIndex + 1);
propertyName = propertyName.Substring(0, dotIndex);
nestedProperty = true;
}
p = t.GetProperty(propertyName);
if ((p == null))
{
Ex = new CustomBizObjectException("Property: " + t.Name + "." + propertyName + " does not exist.");
Ex.Source = "BizObject_SetProperty(propertyName, value)";
throw Ex;
}
else
{
if (nestedProperty)
{
object rtn;
rtn = this.GetValue(propertyName);
if ((rtn == null))
{
Ex = new CustomBizObjectException(t.Name + "." + propertyName + " does not exist.");
Ex.Source = "BizObject_SetProperty(propertyName, value)";
throw Ex;
}
else
{
((BizObject)(rtn)).SetProperty(nestedPropertyName, value);
}
}
else
{
m = p.GetSetMethod(false);
if ((m == null))
{
Ex = new CustomBizObjectException("Function: " + t.Name + "." + propertyName + " has no SET method.");
Ex.Source = "BizObject_SetProperty(propertyName, value)";
throw Ex;
}
else
{
if ((value == null))
{
m.Invoke(this, new object[] { null });
}
else if ((value.GetType().IsSubclassOf(p.PropertyType)))
{
m.Invoke(this, new object[] { value });
}
else
{
m.Invoke(this, new object[] { System.Convert.ChangeType(value, p.PropertyType) });
}
}
}
}
}
catch (Exception ex)
{
ex = new CustomBizObjectException("Caught Exception when Setting " + this.GetType().Name + "." + propertyName, ex);
ex.Source = "BizObject_SetProperty(propertyName, value)";
throw ex;
}
}
2 新建,保存,刷新,取消
在主窗口中应该可以看到 新建,保存,刷新,取消这几个BUTTON。Mediar.framework 把所有的对象看归纳成只有这几个操作,通过这几个操作来控制对象。这几个button的事件调用活动窗口中的相应方法,最后调用对应的对象中相关方法.
主窗体中:
private void toolStripBase_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
BizObjectBoundEdit m = this.ActiveMdiChild as BizObjectBoundEdit;
switch (e.ClickedItem.Name)
{
case "toolStripButtonNew":
m.NewButtonPressed();
ActiveMenu();
break;
case "toolStripButtonCancel":
m.CancelButtonPressed();
break;
case "toolStripButtonSave":
m.UpdateButtonPressed();
break;
case "toolStripButtonRefresh":
m.RefreshButtonPressed();
break;
}
}
所有的对象UI都继承一个基础窗体,下面的方法作为共用方法写在这个基础窗体中。mRootBizObject 为窗体中对应的对象实体。这里就讲的比较的粗枝大叶了,不然决对不是一个短短的文章可以写清楚的。
public virtual void NewButtonPressed()
{
this.Cursor = Cursors.WaitCursor;
if (mRootBizObject == null)
{
ChangeRootBizObject(this.CreateNewBizObject(null));
}
else
{
ChangeRootBizObject(this.CreateNewBizObject(mRootBizObject.GetType()));
}
DisplayObject(mRootBizObject);
this.Cursor = Cursors.Default;
}
public virtual void CancelButtonPressed()
{
this.Cursor = Cursors.WaitCursor;
mRootBizObject.CancelEdit();
DisplayObject(mRootBizObject);
this.Cursor = Cursors.Default;
}
public virtual bool UpdateButtonPressed()
{
bool retVal;
this.Cursor = Cursors.WaitCursor;
if (mRootBizObject.Update())
{
retVal = true;
}
else
{
retVal = false;
MessageBox.Show("Your changes were NOT saved. Please review any error messages and try your save again.", mFriendlyClassName, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
this.Cursor = Cursors.Default;
return retVal;
}
public virtual void RefreshButtonPressed()
{
this.Cursor = Cursors.WaitCursor;
mRootBizObject.ReSelect();
this.Cursor = Cursors.Default;
}
新建需要通过工厂中的 CreateBizObject方法,因为每个对象都需要包括一个工厂,就需要在新建的时候构造传入。对象新建可以在InitializeValues方法设对象default 值。
public BizObject CreateBizObject()
{
BizObject bo;
bo = CreateBizObject(this);
UpdateCache(bo);
return bo;
}
protected override BizObject CreateBizObject(BizObjectFactory bof)
{
return new Contact(bof);
}
取消就把Original值覆盖到当前值.
public void CancelEdit()
{
CancelEditChildren();
if ((mIsDirty))
{
RestoreLastPersistedValues();
this.OnBizObjectUpdatedEvent(new BizObjectUpdatedEventArgs(BizObjectUpdatedType.Changed));
}
}
protected override void RestoreLastPersistedValues()
{
mDisplayID[AttributeValueVersion.Current] = mDisplayID[AttributeValueVersion.Original];
mFirstName[AttributeValueVersion.Current] = mFirstName[AttributeValueVersion.Original];
mLastName[AttributeValueVersion.Current] = mLastName[AttributeValueVersion.Original];
mDescription[AttributeValueVersion.Current] = mDescription[AttributeValueVersion.Original];
mCompanyName[AttributeValueVersion.Current] = mCompanyName[AttributeValueVersion.Original];
mContactType[AttributeValueVersion.Current].UniqueID = mContactType[AttributeValueVersion.Original].UniqueID;
// mAddress[AttributeValueVersion.Current].UniqueID = mAddress[AttributeValueVersion.Original].UniqueID;
mEmailAddress[AttributeValueVersion.Current] = mEmailAddress[AttributeValueVersion.Original];
mWebSiteURL[AttributeValueVersion.Current] = mWebSiteURL[AttributeValueVersion.Original];
mNotes[AttributeValueVersion.Current] = mNotes[AttributeValueVersion.Original];
base.RestoreLastPersistedValues();
}
保存就把数据更新到数据库,然后再刷新当前的实体。也许有人要问:刚才保存的为什么要刷新呀?刷新目是把RecordVersion取出来,RecordVersion是timestamp类型,每更新一次数据库,该字段就是有一个新值。在更新数据库之前会和数据库中的值比较,这是防止两个人在同时修改一个对象时引发冲突。
public virtual bool Update(string AssemblyName)
{
BeginEdit();
DataSet ds;
DataRow dr;
BizObject bobjClone;
bool success = false;
if (!mAllowEdit)
{
throw new Exception("Object marked to disallow edits");
}
if (!Validate())
{
return false;
}
ds = mBOFactory.CreateDataObject();
dr = GetDataObjectRow(ds);
try
{
mBOFactory.Update( ref ds, AssemblyName);
success = !(ds.HasErrors);
if ((ds.HasErrors && DBUtility.HasDBConcurrencyError(ds)))
{
bobjClone = this.Clone(true);
OnBizObjectConcurrencyErrorEvent(new BizObjectConcurrencyErrorEventArgs(bobjClone));
}
else
{
Refresh(ds);
}
}
catch (CustomBizObjectException ex)
{
ExceptionManager.Publish(ex);
throw ex;
}
catch (Exception ex)
{
success = false;
this.OnBizObjectErrorEvent(new BizObjectErrorEventArgs(BizObjectErrorType.DBError, ex.Message, ""));
}
this.EndEdit();
return success;
}
Factory类中,就会调用数据层的更新方法。
public void Update(ref DataSet ds, string AssemblyName)
{
DataAccess da;
da = GetDataAccess(AssemblyName);
ds= da.Update(ds,true);
}
刷新就是从数据库重取数据,然后更新对象属性的值。
public virtual void ReSelect()
{
this.ReSelect(QueryFor.Complete);
}
public virtual void ReSelect(QueryFor queryFor)
{
if (this.mIsDirty)
{
throw new CustomBizObjectException("Can not ReSelect if BizObject is dirty.");
}
this.mBOFactory.ReSelect(this, queryFor, mDataAccessAssemblyName);
}
// mBOFactory 中
protected internal void ReSelect(BizObject bobj, QueryFor queryFor, string DAAssemblyName)
{
DataSet ds;
ds = this.GetDataAccess(DAAssemblyName).Select(bobj.UniqueID, QueryFor.Complete);
this.BuildBizObject(bobj.UniqueID, ds, queryFor);
}