其实这是我的一个两年前的项目中用到的东西,今天刚好翻以前的代码,看到,觉得也是自己想出来的东西,写写也无妨,哪怕是为了填充我空虚的blog。这个是一个智能客户端,但是里面涉及的表单非常之多,统计一下,有200个张。本来也不是问题,用FORM一个一个画也能够画出来,但是这样有几个缺点,一方面导致智能客户端的dll变的比较大,每次更新的时候,都要下载整个dll;一方面分工不好分,原来那个公司那时候只有我用过winform,老板就把这个任务分配给我了。但是我一个人,要做那么多的表单,而且在比较短的时间内,也不是说不可能,太累了。我想想看能不能有其他方法。
当时XFORM的概念相当热,我也看了一些资料,觉得还是不太成熟,没有敢用。
后来看了微软office2007中的infopath,感觉功能非常强,它可以实现用infopath设计表单,然后用表单控件把表单嵌入到winform中去,并且可以实现表单中的数据加载和保存功能。但是唯一的遗憾就是如果要使用它,就必须要客户机器上安装office2007,这样就不太现实了。
最后来想到的是用xml+xslt是动态生成静态页面,然后把静态页面嵌入在winform的浏览器控件里面,并且屏蔽一些浏览器控件的一些属性,看起来就和winform设计的表单差不多了。这样就方便多了,我可以叫美工帮我设计表单,叫其他的asp.net的程序员帮忙做XML和XSLT以及里面的javascript脚本。后来的项目进展说明了,这种方式真的大大的提高了工作效率,也让我轻松了不少,嘿嘿,又偷懒了。
下面我说说具体的过程吧。
1。首先我在设计表的时候,把整个表单的内容设计成XML类型的字段,当然,其他需要搜索的字段还是要单独建字段,并且保存xslt文件的版本号。
2。设计表单的XML结构,下面是示例。
<?
xml version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
<!--
现场会议记录
-->
<
meetnote
>
<
enterprise
></
enterprise
>
<!--
会议名称
-->
<
meets
>
<
meet
>
<
name
>
meet1
</
name
>
<
checked
>
false
</
checked
>
</
meet
>
<
meet
>
<
name
>
meet2
</
name
>
<
checked
>
false
</
checked
>
</
meet
>
<
meet
>
<
name
>
meet3
</
name
>
<
checked
>
false
</
checked
>
</
meet
>
</
meets
>
<
compere
/>
<
recorder
/>
<
address
/>
<
meetdate
/>
<
content
/>
</
meetnote
>
3.设计表单的XSLT文件,下面是示例。
<?
xml version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
<
xsl:stylesheet xmlns:xsl
=
"
http://www.w3.org/1999/XSL/Transform
"
xmlns:msxsl
=
"
urn:schemas-microsoft-com:xslt
"
version
=
"
1.0
"
>
<
xsl:template match
=
"
/
"
>
<
html
>
<
head
>
<
link href
=
"
../css/xmltable.css
"
rel
=
"
stylesheet
"
type
=
"
text/css
"
/>
<
script language
=
"
javascript
"
>
//
使用客户端日期JS脚本初始化
var date
=
new
Date();
var Fun_Year
=
date.getYear();
var Fun_Month
=
date.getMonth()
+
1
;
var Fun_Day
=
date.getDate();
</
script
>
<
script language
=
"
javascript
"
src
=
"
../javascript/calendar.js
"
></
script
>
<
script language
=
"
javascript
"
src
=
"
../javascript/check.js
"
></
script
>
<
script language
=
"
javascript
"
src
=
"
../javascript/common.js
"
></
script
>
<
script language
=
"
javascript
"
>
function validate()

{
var errorinfo="";
if(clearSpace(txtcompere.value)=="")

{
errorinfo += "主持人为空\n";
}
if(clearSpace(txtaddress.value)=="")

{
errorinfo+= "会议地点为空\n";
}
if(clearSpace(txtrecorder.value)=="")

{
errorinfo+="记录人为空\n";
}
if(clearSpace(txtmeetdate.value)=="")

{
errorinfo += "会议时间为空\n";
}
if(clearSpace(txtcontent.innerText)=="")

{
errorinfo+= "会议内容为空\n";
}
//会议名称
if(txtmeet1.checked != true && txtmeet2.checked != true && txtmeet3.checked != true)

{
errorinfo+= "请选择会议名称\n";
}
if(errorinfo=="")

{
return true;
}
else

{
window.external.ShowErrorMessage(errorinfo);
return false;
}

}
//
数据保存
function savetoxml()

{
var doc = new ActiveXObject("Msxml2.DOMDocument");
doc.async = false;
//调用c#方法获取xml文件路径
var xmlfilepath = window.external.GetXMLPathForJava();
doc.load(xmlfilepath);

//会议名称
doc.selectNodes("meetnote/meets/meet")[0].lastChild.text=(txtmeet1.checked==0)?'false':'true';
doc.selectNodes("meetnote/meets/meet")[1].lastChild.text=(txtmeet2.checked==0)?'false':'true';
doc.selectNodes("meetnote/meets/meet")[2].lastChild.text=(txtmeet3.checked==0)?'false':'true';

doc.selectSingleNode("meetnote/compere").text=txtcompere.value;
doc.selectSingleNode("meetnote/recorder").text=txtrecorder.value;
doc.selectSingleNode("meetnote/address").text=txtaddress.value;
doc.selectSingleNode("meetnote/meetdate").text=txtmeetdate.value;
doc.selectSingleNode("meetnote/content").text=txtcontent.innerText;

//调用c#方法保存xml文件
var examfiletype = window.external.GetExamFileType();
var examfilename = window.external.GetExamFileName();
window.external.Save(doc.xml,examfiletype,examfilename);
return true;
}
</
script
>
</
head
>
<
body
>
<
xsl:apply
-
templates
/>
</
body
>
</
html
>
</
xsl:template
>

<
xsl:template match
=
"
meetnote
"
>
<
table border
=
"
1
"
align
=
"
center
"
cellPadding
=
"
0
"
cellSpacing
=
"
0
"
class
=
"
table
"
>

<
tr
>
<
td
class
=
"
td1
"
colspan
=
"
4
"
>
<
div align
=
"
center
"
class
=
"
title
"
>
现场审核会议记录
</
div
>
</
td
>
</
tr
>
<
tr
>
<
td
class
=
"
td2
"
>
<
span
>
受审核方:
</
span
>

<
xsl:value
-
of select
=
"
enterprise
"
/>

</
td
>
</
tr
>
<
tr
>
<
td
class
=
"
td2
"
>
<
span
>
会议名称:
</
span
>
<
xsl:apply
-
templates select
=
"
meets/meet
"
/>
</
td
>
</
tr
>
<
tr
>
<
td
class
=
"
td2
"
>
<
span
>
主 持 人:
</
span
>
<
input style
=
"
WIDTH: 150px;
"
type
=
"
text
"
name
=
"
txtcompere
"
>
<
xsl:attribute name
=
"
value
"
>
<
xsl:value
-
of select
=
"
compere
"
/>
</
xsl:attribute
>
</
input
>
<
span
>
会议地点:
</
span
>
<
input style
=
"
WIDTH: 200px;
"
type
=
"
text
"
name
=
"
txtaddress
"
>
<
xsl:attribute name
=
"
value
"
>
<
xsl:value
-
of select
=
"
address
"
/>
</
xsl:attribute
>
</
input
>
</
td
>
</
tr
>
<
tr
>
<
td
class
=
"
td2
"
>
<
span
>
记 录 人:
</
span
>
<
input style
=
"
WIDTH: 150px;
"
type
=
"
text
"
name
=
"
txtrecorder
"
>
<
xsl:attribute name
=
"
value
"
>
<
xsl:value
-
of select
=
"
recorder
"
/>
</
xsl:attribute
>
</
input
>
<
span
>
会议时间:
</
span
>
<
input style
=
"
WIDTH: 100px;
"
type
=
"
text
"
name
=
"
txtmeetdate
"
onfocus
=
"
calendar();
"
id
=
"
txtmeetdate
"
>
<
xsl:attribute name
=
"
value
"
>
<
xsl:value
-
of select
=
"
meetdate
"
/>
</
xsl:attribute
>
</
input
>
</
td
>
</
tr
>
<
tr
>
<
td valign
=
"
top
"
class
=
"
td2
"
>
<
span
>
会议内容:
</
span
>
<
br
/>
<
textarea style
=
"
WIDTH: 100%;HEIGHT:150px
"
type
=
"
text
"
name
=
"
txtcontent
"
>
<
xsl:value
-
of select
=
"
content
"
/>
</
textarea
>
</
td
>
</
tr
>
</
table
>
</
xsl:template
>
<!--
会议名称
-->
<
xsl:template match
=
"
meets/meet
"
>
<
xsl:
if
test
=
"
name='meet1'
"
>
<
input type
=
"
checkbox
"
name
=
"
txtmeet1
"
id
=
"
txtmeet1
"
onclick
=
"
radiobutton(this,'txtmeet')
"
>
<
xsl:
if
test
=
"
checked='true'
"
>
<
xsl:attribute name
=
"
checked
"
>
checked
</
xsl:attribute
>
</
xsl:
if
>
</
input
>
<
span
>
首次会议
</
span
>
</
xsl:
if
>
<
xsl:
if
test
=
"
name='meet2'
"
>
<
input type
=
"
checkbox
"
name
=
"
txtmeet2
"
id
=
"
txtmeet2
"
onclick
=
"
radiobutton(this,'txtmeet')
"
>
<
xsl:
if
test
=
"
checked='true'
"
>
<
xsl:attribute name
=
"
checked
"
>
checked
</
xsl:attribute
>
</
xsl:
if
>
</
input
>
<
span
>
沟通会议
</
span
>
</
xsl:
if
>
<
xsl:
if
test
=
"
name='meet3'
"
>
<
input type
=
"
checkbox
"
name
=
"
txtmeet3
"
id
=
"
txtmeet3
"
onclick
=
"
radiobutton(this,'txtmeet')
"
>
<
xsl:
if
test
=
"
checked='true'
"
>
<
xsl:attribute name
=
"
checked
"
>
checked
</
xsl:attribute
>
</
xsl:
if
>
</
input
>
<
span
>
末次会议
</
span
>
</
xsl:
if
>
</
xsl:template
>
</
xsl:stylesheet
>
4。在XSLT文件中加入JS脚本,用户验证用户输入,收集表单数据,加载表单数据等。(这里涉及到JS代码和浏览器控件所在的winform窗体的代码的相互调用。)
1)首先把winform窗体的类加上这个属性[ComVisible(true)]
2) 在winform窗体里面写上public的方法
//取得xml文件路径(供javascript调用)
public string GetXMLPathForJava()
{
return tempxmlfilepath;
}
3)在XSLT文件里面的JS代码中调用这个方法
//调用c#方法获取xml文件路径
var xmlfilepath = window.external.GetXMLPathForJava();
4)在XSLT文件中定义JS方法
//数据保存
function savetoxml()
{
}
5)在winform窗体中调用JS方法
//调用JS方法
Object result = webBrowser1.Document.InvokeScript("savetoxml");
5.在显示页面的时候,我先使用下面的代码把XSLT和XML转换成HTML文件,存放在临时文件夹中,然后把浏览器控件的URL指向它。
XslCompiledTransform transForm
=
new
XslCompiledTransform();
string
xsltemplatepath
=
TemplateHelper.GetFactoryFormXSLTemplatePath(m_FileType, m_DataRow.xsl_version);

try

{
transForm.Load(xsltemplatepath);
transForm.Transform(tempxmlfilepath, temphtmlfilepath);
}
catch
(Exception ex)

{
LogError.Write(ex.Message + "\n" + ex.StackTrace);
MessageBox.Show("模板文件加载出错!");
this.Close();
return;
}
webBrowser1.Navigate(temphtmlfilepath);
6。在用户点击保存的时候,我通过XSLT中定义的JS代码,直接把用户保存的数据以XML的方式返回给winform的代码,然后保存到数据库中去。
Object validateresult
=
webBrowser1.Document.InvokeScript(
"
validate
"
);
if
(Convert.ToBoolean(validateresult)
==
true
)

{
Object result = webBrowser1.Document.InvokeScript("savetoxml");
if (Convert.ToBoolean(result) == true)

{
this.Close();
}
else

{
MessageBox.Show("保存出错", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
this.DialogResult = DialogResult.None;
}
}
else

{
this.DialogResult = DialogResult.None;
}
7。删除刚才产生的临时文件。