一个Web系统界面设计和开发.2
星期一, 05月 31st, 2004
2.需求分析
在需求分析阶段,主要针对界面交互相关问题,对用户进行若干调研。
主要包括以下内容
一个Web系统界面设计和开发.1
星期一, 05月 31st, 2004
早在中国IT业方兴未艾之时,计算机应用系统主要以功能实现为主,几乎没有界面设计这个概念。时至今日,随着计算机和网络的不断普及,社会信息化程度日益加深,用户和市场的不断成熟,人们已经不仅仅满足于“够用”,而是更加强调“好用”“易用”;因此,不论是普通最终用户的个人软件,还是企业应用的大型系统,界面设计在系统构建中都成为了一个非常重要的方面。
但是,(至少在中国)由于IT业发展滞后、市场还不够成熟等原因,在绝大多数企业中,界面设计在软件系统开发中还没有获得与之重要性相匹配的一席之地,并且在企业运作和协调中也没有形成成熟的模式和解决方案,如何做好界面设计和开发,仍然是大家不断研究探讨的一个问题。
我写这篇文章,主要内容是我参加一个面向质检行业的Web系统界面设计和开发工作的过程,包括其间的一些构思和想法;希望能和大家一起探讨一下这个问题,供大家参考。
另外,我同时承担了系统开发和界面设计工作,所以,虽然这是一篇讨论界面设计的文章,我会尽量把文章限制在界面设计范围内,但也有可能包含一些开发和系统设计的内容,请大家辨析清楚,欢迎指正。
1.工作流程
下图,是整个开发过程中与界面设计相关的主要流程工作。
从最初需求分析开始,我就加入项目,自始自终参加整个开发过程。
在需求分析阶段,参与了对客户的访问和调研;
在概要设计阶段,参与了部分系统设计分析工作;
在详细设计阶段,完成了整个系统界面设计和Demo制作,并提交用户反馈;
在代码开发阶段,参与了系统表现层的设计开发。
利用.NET的XMl序列化解决系统配置问题
星期六, 05月 22nd, 2004
在Web系统开发中,我们经常需要读取和设置一些系统配置项,常见的例如数据库连接字符串、上传路径等等。
在最初的ASP系统中,比较常用的方法是将值保存为Application或Session变量;在Asp.net系统中,目前比较常见的简单方法是把相应的配置项写入Web.Config中,例如
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> ... </system.web> <appSettings> <add key="ConnectionString" value="server=(local);database=dbname;uid=user;pwd=pass"/> </appSettings> </configuration>
然后在程序中通过如下方式读取
string connString = ConfigurationSettings.AppSettings["ConnectionString"];
这种方法在系统规模较小复杂度较低的时候也不失为一种简单明了的轻量级方法,不过如果系统较复杂,配置项会较多,同时我们需要按不同模块对配置进行划分,并且还希望能以面向对象方法来对其进行封装,那么如果仍然采用这种过于简化方式就不太合时宜了。
下面,我讲述一下通过XML类序列化解决系统配置问题的方法。
关于XML类序列化和反序列化(另外一种描述是串行化和并行化)的技术细节,大家可以查看MSDN了解;这里简单讲两句,XML序列化是把一个对象序列化到XML文档的过程,反序列化则是从XML输出中重新创建原始状态的对象。
直观表现就是如下图模式

看了这个图,就很清楚了,通过序列化,可以采用面向对象的方法,非常自然和方便的读取和设置系统配置;.NET Framework承担了对象和XML文件映射工作,我们只需要简单的使用就OK。下面我们讲一下具体内容。
上面图示已经表明,首先需要一个XML配置文件,格式内容如上图,具体配置项可以自行增减。
然后我们需要编写一个类,如上图所示;特殊的一点,为了使类能够实现XML序列化,需要在类的所有属性声明前添加属性信息XmlElement,如下所示。
[XmlElement]
public string ConnectionString
{
get { return connectionString; }
set { connectionString = value; }
}
由于AppConfig类本身没有实现方法,因此我们需要一个配置类AppConfigSetting.cs。类的结构很简单,只需要两个静态方法,Get()获取AppConfig对象,Save()保存AppConfig对象。
另外,我们需要在 Web.Config中添加该XML配置文件的地址。
<appSettings>
<add key="AppConfigPath" value="/FilePath/file.config"/>
</appSettings>
public class AppConfigSetting
{
//获取配置对象
public static AppConfig Get()
{
//尝试获取缓存中的对象
AppConfig config = (AppConfig)HttpContext.Current.Cache["AppConfig"];
//如果缓存中没有该配置对象,则直接获取对象
if (config == null)
{
//新建序列化对象并指定其类型
XmlSerializer serial = new XmlSerializer(typeof(AppConfig));
try
{
string file = HttpContext.Current.Server.MapPath(GetFile());
//读取文件流
FileStream fs = new FileStream(file, FileMode.Open);
//文件流反序列化为对象
config = (AppConfig)serial.Deserialize(fs);
fs.Close();
//将对象加入到缓存中
HttpContext.Current.Cache.Insert("AppConfig",
config, new CacheDependency(file));
}
catch (System.IO.FileNotFoundException)
{
config = new AppConfig();
}
}
return config;
}
//保存配置对象
public static void Save(AppConfig config)
{
string file = HttpContext.Current.Server.MapPath(GetFile());
XmlSerializer serial = new XmlSerializer (typeof(AppConfig));
FileStream fs = new FileStream(file, FileMode.Create);
//对象序列化为文件
serial.Serialize(fs, config);
fs.Close();
}
//获取配置文件路径
private static string GetFile()
{
string path = (string)HttpContext.Current.Cache["FilePath"];
if (path == null)
{
path=ConfigurationSettings.AppSettings["AppConfigPath"];
HttpContext.Current.Cache["FilePath"] = path;
}
return path;
}
}
类的使用非常简单,基本方式如下
//代码仅为使用演示 AppConfig config = AppConfigSetting.Get(); string connString = config.ConnectionString; ... config.ConnectionString = connString; AppConfigSetting.Save(config);
看到这样的代码,不禁有令人赏心悦目之感;相对于原来的直接读取AppSetting,可谓解脱矣!
OK,就此结束了。这篇文章只涉及对象序列化的非常简单的应用,没有涉及太多的技术原理和细节,大家要深入了解请参考MSDN。
希望文章能对大家有所补益和启发。
CSS设计器代码参考.3
星期一, 05月 17th, 2004
界面交互
在XML中一共定义了 select/ input_ColorSelect / input_SizeSelect / input_BorderSelect(后3种为颜色/大小/边框输入模式)共4种输入模式,除select为直接选择外,其他在对应控件初始化的时候作为class属性赋值到控件中,类似class代码如下
/* 颜色输入模式input框的样式类 */
.input_ColorSelect
{
width:100px;
font-family:Tahoma;
behavior:url(htc/effColorSelect.htc);
}
通过behavior属性,把该输入控件和相应的组件相关联,该组件effColorSelect.htc代码如下
<public:ATTACH EVENT="onfocus" ONEVENT="getShow()"/> <public:METHOD NAME="getChange"/> <script LANGUAGE="JScript"> function getShow() { element.blur(); //记录当前交互控件的ID effElement=element.id; //在页面中加载输入控件 ShowControl ("SelectColor"); } function getChange() { //当值发生变化时,对可视化样式元素赋值 SetAttribute(element.id,element.value); } </script>
其他
设计器中的值输入模式框只是页面中的几个层,通过上面的htc组件触发显示出来,输入后再把值传入到样式属性控件中,同时也会设置可视化样式元素。
另外还需要注意的是,XML文档是可以自行扩展或缩减的,但是在实际应用中,不能完全依据CSS标准来定义,因为可视化元素的style属性会自动格式化。例如如果你在XML中定义border-bottom-width属性,在将值取出时会自动格式化为border-bottom,这样会造成设计器中控件不能匹配。我在MSDN没有查到相关文档,所以只有经过实际测试来验证。
OK,比较关键的代码已经差不多了……希望能对大家有所帮助。
参考
另外再列出部分技术参考,如果大家对其中的技术细节如HTC和XmlDom等有所疑问,可以再详细研究一下,也欢迎大家来和我交流。
这是英文文档,网上没有看到比较详细的中文文档,好在不复杂,大家将就一下吧
(最近MSDN不知道什么毛病,经常访问有问题,如果无法访问,请先登录msdn,再输入地址浏览)
网上也没看见比较全面的讲述,这个简单易学,基本概念清楚了。
CSS设计器代码参考.2
星期三, 05月 12th, 2004
设计器初始化
Js脚本读取解析样式元素的style属性值,然后为设计器中构建的控件赋值
//设计器初始化
function Init()
{
//获得由服务器端赋值的样式属性值
var txt=document.all("DemoShow").style.cssText;
if (txt.length>0)
{
var strClassName;
//解析字符串
var aryClass = txt.split(/[:;]/);
for( i in aryClass)
{
var str = aryClass[i].replace(/(^s*)|(s*$)/g, "");
if(!(i%2==1))
{
//当i为奇数时,解析的字符串应该为样式属性名称
strClassName=str;
}
else
{
//当i为偶数时,获得属性值
//属性名称即控件ID
//判断该属性对应的控件是输入框还是选择列表
if(document.all(strClassName).type=="select-one")
{
//如果是选择列表通过setIndexOfValue函数设定选择项
setIndexOfValue(strClassName,str);
}
else
{
document.all(strClassName).value=str;
}
}
i++;
}
}
}
CSS设计器代码参考.1
星期一, 05月 10th, 2004
这里对前面讲的CSS设计器系统关键代码作一些小结,如果没有看过前面文章请先参看前文
解析CSS样式文件
这段代码主要作用是把CSS文件分解为多个样式类,并按名称/文本属性生产ClassItem对象,并保存在一个ArrayList(cssList)中(C#代码)
//读取文件 FileInfo theSource= new FileInfo (@m_filePath); StreamReader reader = theSource.OpenText(); //将文件流转化为文本 m_cssText = reader.ReadToEnd(); reader.Close(); //定义CSS文本分割符 char[] delimiters = new char[] { '{','}'}; int iCheck = 1;string className = null; //将文本转化为ArrayList foreach ( string substring in m_cssText.Split(delimiters)) { if (iCheck%2==0) //当iCHeck为偶数时,字符串为样式属性内容 //将解析的样式名和属性作为ClassItem对象存入cssList cssList.Add( new ClassItem ( className, substring.Trim() ) ); else //当iCHeck为奇数时,字符串为样式名,暂存 className = substring.Trim(); iCheck++; }
交互界面构建
交互界面由Javascript通过XmlDocument读取Xml文件动态生成。
首先要读取XML文件,然后遍历整个XML文件,先遍历样式分类,再对每个分类遍历其下的所有样式属性。比较关键的代码是对XML的遍历,下面是对样式分类的遍历代码。
//LoadXML是XML文件读取函数 var dom = LoadXML("css.xml"); //通过XPath和selectNodes方法返回一个XMLDOMNodeList对象 var oNode = dom.selectNodes("//Category/Name"); //获取该对象长度,即XML文档中该路径节点的数量 var intCategory = oNodes.length; for (i=0; i<intCategory; i++) { //获取集合中的节点 oNode = oNodes.nextNode; if (oNode != null) { //样式分类界面构建代码-略 …… } }
样式输入控件构建函数,该函数作用是根据XPath路径查询XML定义,生成交互控件
function BuildInput ( path ) { var str=""; var aNode=null; var attValue=null; //通过selectSingleNode返回符合条件的第一个节点 var actNode = dom.selectSingleNode(path+"ActionType"); var nameNode = dom.selectSingleNode(path+"CssName"); //如果属性为选择输入,则读取SelectItems,并构建select控件 if (actNode.text=="select") { str += "<select id='"+nameNode.text+"' name='"+nameNode.text+"'class='eSelect'>"; //查询该项的所有选择列表项 var itemsNodes = dom.selectNodes (path+"SelectItems/Item"); str += "<option value='-1'>未设置</option>"; for (ii=0;ii<itemsNodes.length;ii++){ aNode = dom.selectSingleNode (path+"SelectItems/Item["+ii+"]"); //如果该项含有Name属性则在列表中显示Name属性值 attValue = aNode.getAttribute("Name") var txtNode = dom.selectSingleNode (path+"SelectItems/Item["+ii+"]"); if (attValue==null) str += "<option value='"+txtNode.text+"'>"+txtNode.text+"</option>"; else str += "<option value='"+txtNode.text+"'>"+attValue+"</option>"; } str += "</select>"; } else //如果属性为其他模式,则构建input输入,设置class属性为ActionType { str = "<input name='"+nameNode.text+"' id='"+nameNode.text+"' class='"+actNode.text+"'>"; } return(str); }
asp.net开发技巧
星期四, 05月 6th, 2004
不断学习的过程中,经常遇到一些代码编写技巧性的东西,虽然通常不是什么特别困难的技术问题,不过也十分重要。
为避免遗忘并方便查证,特记录于此。
在页面中直接发文件到客户端
说明: 将下面代码加入干净的aspx页或隐藏代码页即可(此处假设页面为download.aspx);
效果: 链接到地址[download.aspx?file=filename]直接产生下载;
目的: 与直接链接文件地址下载方式比较,可以在下载前加入代码,统计下载数;另外,根据需要可以改写参数(例如改为数据库中该下载文件记录的id),做简单的代码处理,就可以在一定程度上隐藏下载地址,防止盗链……
private void Page_Load(object sender, System.EventArgs e)
{
string path = Server.MapPath(Request.Params["File"]);
System.IO.FileInfo file = new System.IO.FileInfo(path);
Response.Clear();
Response.AddHeader(“Content-Disposition”, “attachment; filename=” + file.Name);
Response.AddHeader(“Content-Length”, file.Length.ToString());
Response.ContentType = “application/octet-stream”;
Response.WriteFile(file.FullName);
Response.End();
}
CSS设计器之界面交互
星期四, 04月 29th, 2004
整个操作交互过程,除了最后保存文件外,其他都是由javascript完成。
首先DesignerBuild函数读取XML样式属性定义文件,构建整个设计器界面。然后Init函数读取服务器端赋给设计元素的Style属性,并把属性作为输入控件ID在设计器中查找并赋值,完成初始化。
在操作过程中,根据输入控件的样式类Class,触发绑定的HTC组件,做相应的客户端操作。
最后再读取设计元素的style属性,保存。
设计器界面
不同的设计元素
不同输入控件的不同class属性(根据XML中ActionType生成)触发不同HTC组件,实现不同输入模式。
由于商业原因,这里不便提供源代码;我将在后面提供部分关键代码供参考。
CSS设计器之样式表操作类
星期一, 04月 26th, 2004
为了操作样式表,设计了一个简单的样式表操作类。功能主要是解析操作指定样式表文件,实现对样式类的添加、修改、删除、保存。
机制:读取Web服务器上某样式表文件,将文本转化为一个ArrayList,数组元素为自定义的ClassItem对象,包含Name和Text属性(Name即样式名称,Text即样式的内容);然后通过对ArrayList操作,控制样式,最后保存。
由于在服务器段我们不作具体样式定义,因此该类只操作到样式类级别,不涉及样式属性和值。
下面提供该类的UML图 ClassItem 是一个结构体,仅包含两个属性;

CSS设计器之流程
星期五, 04月 23rd, 2004
最近做了一个CSS设计器,主要功能是在web界面上操作设计CSS样式表,目的是方便用户自定义系统界面。
其实我个人看法这个东西并没有太大必要,对于最终用户来说,一般不熟悉网页制作,是不可能进行这种操作的,而且就系统整体来考虑,也不应该给用户这种权限。另外,相对于本来就比较紧张时间资源来讲,花这么多时间,实现这种用户需求优先度较低的功能,实在不合算……
奈何项目经理雄心勃勃,在大而全的方针指导下,不得不做啊。
月初就作的差不多,来总结一下吧
流程再简单说明一下