开发者论坛

 找回密码
 注册 (请使用非IE浏览器)
查看: 9484|回复: 17

XtraLayout,让用户拥有自己个性化的界面

[复制链接]

0

精华

9

贡献

41

赞扬

帖子
68
软币
1133
在线时间
157 小时
注册时间
2013-9-3
发表于 2013-12-5 07:24:23 | 显示全部楼层 |阅读模式
要实现用户界面的个性化定义,我们不妨来看看通常需要考虑哪些因素是必要的。首先、既然是用户个性化设置,自然少不了用户姓名(或用户账号);其次,目前绝大多数软件都有一套权限机制,自定义用户界面作为涉及到系统级的设计、自然也少不了权限的机制;第三,对于软件开发而言,一个窗体经过处理从而作为多种应用界面的情况屡见不鲜(这也是软件设计中的一个基本方针了),所以,自定义用户界面数据的存储、应该考虑的是最终呈现给用户时显示的窗体标题而不是设计期间的窗体名称;第四,既然是要保存XtraLayout的界面设置信息,我们需要知道保存的是哪一个XtraLayout,也就是控件的名称。有了这些因素,我们就可以来设计我们需要的用于保存界面设置信息的数据库表结构了。来看下面的表格:
  
记录号
  
窗体标题
用户
权限
控件名称
界面流信息
ID
FormName
UserName
FormPower
FormComent
FormStream
用于标识不同的记录
用于区分每一个不同的应用界面
用于区分用户
不同权限
个性化设置的控件容器名称
具体的界面设置信息,通常以内存流的形式保存
用自增字段或guid字符串类型
字符型,通常50个汉字足够用
字符型,长度20
字符型,考虑到有些应用存在多种权限组合,可定义为256长度
字符型,200长度一般够用了
长文本。
         有了以上的信息,我们就可以以此建立需要的数据表了,详细的建表脚本,我们这里就不说了,示例程序中有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控件的用户自定义布局,从而给特定用户呈现特定的界面布局状态;
                }
            }
        }
示例项目运行界面如下:(默认界面)

评分

参与人数 1赞扬 +1 收起 理由
maple + 1 感谢分享

查看全部评分

回复

使用道具 举报

0

精华

275

贡献

2252

赞扬

正版授权组

Rank: 14Rank: 14Rank: 14Rank: 14

帖子
211
软币
7155
在线时间
634 小时
注册时间
2013-12-13
发表于 2013-12-14 12:09:31 | 显示全部楼层
学习了,原因这样我都是本地保存。。。
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
9
软币
84
在线时间
4 小时
注册时间
2013-12-26
发表于 2013-12-28 09:55:29 | 显示全部楼层
没有看到界面
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
1
软币
126
在线时间
11 小时
注册时间
2013-8-28
发表于 2014-1-10 13:10:56 | 显示全部楼层
1222222222
回复

使用道具 举报

0

精华

8

贡献

134

赞扬

帖子
76
软币
640
在线时间
57 小时
注册时间
2014-1-13
发表于 2014-1-13 18:25:23 | 显示全部楼层
感觉是一个不错的方案,不过为什么看不到运行界面啊?
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
8
软币
84
在线时间
0 小时
注册时间
2014-2-17
发表于 2014-2-17 10:15:38 | 显示全部楼层
表示没有看到运行界面啊
回复

使用道具 举报

0

精华

0

贡献

1

赞扬

帖子
7
软币
83
在线时间
1 小时
注册时间
2014-3-4
发表于 2014-3-4 15:00:28 | 显示全部楼层

非常感谢,学习下
回复

使用道具 举报

0

精华

638

贡献

297

赞扬

赞助者组

Rank: 14Rank: 14Rank: 14Rank: 14

帖子
118
软币
1389
在线时间
118 小时
注册时间
2013-10-4
发表于 2014-3-4 21:01:57 | 显示全部楼层
学习了,感谢分享,看不到界面。
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
5
软币
65
在线时间
3 小时
注册时间
2014-3-12
发表于 2014-3-12 13:08:18 | 显示全部楼层
学习了,感谢分享,看不到界面。
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
16
软币
156
在线时间
14 小时
注册时间
2014-6-8
发表于 2014-7-21 11:14:01 | 显示全部楼层
很不错 学习了 很好的方案 准备试试
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
17
软币
147
在线时间
10 小时
注册时间
2014-3-10
发表于 2015-1-6 09:57:02 | 显示全部楼层
学习了,感谢分享
回复

使用道具 举报

0

精华

14

贡献

4

赞扬

帖子
110
软币
451
在线时间
43 小时
注册时间
2014-4-9
发表于 2015-1-6 10:51:52 | 显示全部楼层
不错的东西啊,谢谢。。。
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
1
软币
56
在线时间
0 小时
注册时间
2015-10-7
发表于 2015-10-7 21:12:02 | 显示全部楼层
正在制作个性化界面,学习下
回复

使用道具 举报

0

精华

0

贡献

272

赞扬

帖子
184
软币
2057
在线时间
247 小时
注册时间
2014-4-22
发表于 2015-10-8 09:30:04 | 显示全部楼层
没有看到界面呢!
回复

使用道具 举报

0

精华

0

贡献

0

赞扬

帖子
11
软币
67
在线时间
0 小时
注册时间
2016-1-21
发表于 2016-1-21 16:12:06 | 显示全部楼层
好好好好好好好好好好好好好好好好好
回复

使用道具 举报

0

精华

1

贡献

0

赞扬

帖子
30
软币
196
在线时间
20 小时
注册时间
2015-9-7
发表于 2016-7-21 11:36:11 | 显示全部楼层
感谢分享,非常好的资源
回复

使用道具 举报

0

精华

0

贡献

518

赞扬

帖子
95
软币
1120
在线时间
70 小时
注册时间
2017-8-2
发表于 2024-2-21 17:21:50 | 显示全部楼层
Thanks for Sharing.
回复

使用道具 举报

Archiver|手机版|小黑屋|开发者网 ( 苏ICP备08004430号-2 )
版权所有:南京韵文教育信息咨询有限公司

GMT+8, 2024-5-30 23:55

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表