要实现用户界面的个性化定义,我们不妨来看看通常需要考虑哪些因素是必要的。首先、既然是用户个性化设置,自然少不了用户姓名(或用户账号);其次,目前绝大多数软件都有一套权限机制,自定义用户界面作为涉及到系统级的设计、自然也少不了权限的机制;第三,对于软件开发而言,一个窗体经过处理从而作为多种应用界面的情况屡见不鲜(这也是软件设计中的一个基本方针了),所以,自定义用户界面数据的存储、应该考虑的是最终呈现给用户时显示的窗体标题而不是设计期间的窗体名称;第四,既然是要保存XtraLayout的界面设置信息,我们需要知道保存的是哪一个XtraLayout,也就是控件的名称。有了这些因素,我们就可以来设计我们需要的用于保存界面设置信息的数据库表结构了。来看下面的表格: 记录号 | | | | | | | | | | | | | | | | | | | | | | | | 字符型,考虑到有些应用存在多种权限组合,可定义为256长度 | | | |
有了以上的信息,我们就可以以此建立需要的数据表了,详细的建表脚本,我们这里就不说了,示例程序中有access数据库,表名称为:Sys_UIStream,朋友们可自行查看。 准备好了数据库,我们来看看XtraLayout控件是否能满足我们的需要。 XtraLayout控件有一个保存的方法,在代码智能提示中我们可以看到该方法的三个重载: layoutControl1.SaveLayoutToXml(string sXMLFileName);——保存Layout布局到 XML 文件; layoutControl1.SaveLayoutToRegistry(stringsRegPath); ——保存Layout布局到 系统注册表; layoutControl1.SaveLayoutToStream(myStream); ——保存Layout布局到 内存流; 考虑到我们的要求、xml和注册表的方法我们放弃,内存流是无法直接写入到数据库的,不过我们可以把内存流数据转换成长字符串(ToBase64String),这样就可以顺利写入数据库了。好了、有了这样的分析结论、我们首先用代码来实现内存流写入数据库的部分功能: using System; using System.Data; using System.IO; namespace DevControlpractice.LFH.Function { public classUIStream_Database { ///<summary> /// 从数据库读取Layout 界面配置数据流。 ///</summary> ///<param name="ProjectName"></param> ///<param name="IsUpdate"></param> ///<returns></returns> publicstatic MemoryStream GetUIStream_ForceDB(string MenuName, string ControlName,string RightValue, string sUserName) { var dt= new DataTable(); MemoryStream myStream = null; //定义一个内存流变量、用于存放从数据库中读取到的界面配置流。 stringsSq = string.Format( "select * from Sys_UIStream where FormName='{0}' andFormPower='{1}' and FormComent='{2}' and UserName='{3}'", MenuName, RightValue, ControlName, sUserName); dt =AccessHelper.ExecuteSqlQuery(AccessHelper.connStr, sSq, null); //从数据库中读取指定的配置; if (dt.Rows.Count > 0) { //将数据库中存储的流字符串转化为内存流。 myStream =LFHConvert.ConvertBase64StringToObject(dt.Rows[0]["FormStream"].ToString())as MemoryStream; return myStream; } else return null; } ///<summary> /// 保存流到数据库; ///</summary> ///<param name="sFName">窗体名称</param> ///<param name="sComponentName">控件名称</param> ///<param name="sFPower">权限标识</param> ///<param name="myStream">内存流字符串</param> publicstatic string _saveStream(string sFName, string sComponentName, string sFPower,string myStreamStr, string sUserName) { stringsSq = string.Format( "select * from Sys_UIStream where FormName='{0}' andFormPower='{1}' and FormComent='{2}' and UserName='{3}'", sFName, sFPower, sComponentName, sUserName); //首先检查数据库中是否已经存在当前窗体、当前控件在当前权限下的界面数据流信息 DataTable dt =AccessHelper.ExecuteSqlQuery(AccessHelper.connStr, sSq, null); boolisExist = false; if ((dt!= null) && (dt.Rows.Count > 0)) { isExist = true; } stringsqltxt = ""; if(isExist) { //如果数据库中已经存在当前界面配置信息,则使用更新方法更新数据库中的信息; sqltxt = string.Format( "update sys_uistream set FormStream='{0}',ModifyDate=convert(datetime,'{1}') where[FormName]='{2}' and [FormPower]='{3}'and [FormComent]='{4}' andUserName='{5}'", myStreamStr, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),sFName, sFPower, sComponentName, sUserName); } else { //否则插入新纪录,将当前界面流数据保存到数据库中去; sqltxt = string.Format( "insert intosys_uistream([ID],[DelMark],[CreateDate],[CreateBy],[FormName],[FormPower],[FormComent],[FormStream],[UserName])values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}')", FuncCommon.getGuidString(), "False",DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Your Name here", sFName, sFPower, sComponentName,myStreamStr, sUserName); } returnAccessHelper.ExecuteNonQuery(AccessHelper.connStr, sqltxt, null).ToString(); } //**************************************************************** } } Ok,接下来我们要做的、就是在界面层中根据不同的需要来调用这些方法: 代码实现: 以下代码、位于项目中的:namespaceDevControlpractice.LFH.Function. LFHConvert ///<summary> /// 将object对象序列化转存为字符串 /// </summary> /// <param name="o">对象名</param> /// <returns>对象序列化后的字符串</returns> public static string ConvertObjectToBase64String(object o) { if (o == null) { return null; } var stream = new MemoryStream(); var formatter = new BinaryFormatter(); formatter.Serialize(stream, o); byte[] b = stream.ToArray(); stream.Close(); stream.Dispose(); return Convert.ToBase64String(b); } /// <summary> /// 将序列化的字符串还原为对象 /// </summary> /// <param name="Base64String">序列化后的字符串对象</param> /// <returns>对象</returns> public static object ConvertBase64StringToObject(string Base64String) { if ((Base64String == null) | (Base64String == "")) { return null; } else { var ms = new MemoryStream(); var formatter = newBinaryFormatter(); byte[] b =Convert.FromBase64String(Base64String); ms.Write(b, 0, b.Length); ms.Position = 0; object o =formatter.Deserialize(ms); ms.Close(); ms.Dispose(); return o; } } 界面层按钮事件: 开始自定义布局按钮: /// <summary> /// 开始自定义界面布局; /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SimpleButton2Click(object sender, EventArgs e) { layoutControl1.ShowCustomizationForm(); } 保存界面和加载界面按钮: private void simpleButton4_Click(objectsender, EventArgs e) { var myStream = new MemoryStream();//新建一个内存数据流对象; layoutControl1.SaveLayoutToStream(myStream);//将Layout界面布局保存到内存流,这是Xtralayout控件自身的方法; string myStreamStr = LFHConvert.ConvertObjectToBase64String(myStream);//把内存流对象转换为Base64string 类型。 sUser = "李四"; UIStream_Database._saveStream(Text, layoutControl1.Name,"None", myStreamStr, sUser);//保存到数据库中去; } /// <summary> /// 加载用户自定义界面; /// </summary> /// <paramname="sender"></param> /// <paramname="e"></param> private void simpleButton5_Click(objectsender, EventArgs e) { if (!DesignMode) { sUser = "张三";//设置用户名称 MemoryStream myStream =UIStream_Database.GetUIStream_ForceDB(Text, layoutControl1.Name,"None", sUser);//根据当前窗体标题、Layout控件名称、权限标识、用户名称这几个条件来读取用户自定义的界面数据流信息。 if (myStream != null) { //设置指针到内存流的起始位置0,——这一点很重要! myStream.Seek(0,SeekOrigin.Begin); layoutControl1.RestoreLayoutFromStream(myStream);//从读取到的界面配置流中恢复Layout控件的用户自定义布局,从而给特定用户呈现特定的界面布局状态; } } } 示例项目运行界面如下:(默认界面)
|