posts - 8, comments - 71, trackbacks - 44, articles - 0

用XSL来定义ASP.NET Web Control

Posted on 2008-02-27 02:15 夏天的感觉 阅读(2501) 评论(15)  编辑 收藏 所属分类: SharePointASP.NET

在开发Web Control的时候,经常需要在源代码中嵌入一些HTML代码, 比如这样(代码片段):

protected override void RenderContents(HtmlTextWriter output)
        
{
            SPList myList 
= GetListByName(List);
            
if (myList != null)
            
{
                
uint currentLanguageCode = getCurrentLanguageCode();
                output.WriteBeginTag(
"ul");
                output.WriteAttribute(
"class""lng");
                output.WriteLine(HtmlTextWriter.TagRightChar);

                
foreach (SPListItem item in myList.Items)
                
{
                    
if (uint.Parse((string)item["Language Code"]) != getCurrentLanguageCode())
                    
{
                        output.WriteBeginTag(
"li");
                        output.WriteAttribute(
"class", (string)item["CSS Class"]);
                        output.WriteLine(HtmlTextWriter.TagRightChar);
                        output.WriteBeginTag(
"a");
                        output.WriteAttribute(
"href", ((Microsoft.SharePoint.Publishing.Fields.LinkFieldValue)item["Language URL"]).NavigateUrl);
                        output.WriteLine(HtmlTextWriter.TagRightChar);
                        output.Write((
string)item["Title"]);
                        output.WriteEndTag(
"a");
                        output.WriteEndTag(
"li");
                    }

                }

                output.WriteEndTag(
"ul");
            }

        }



这个控件的HTML会输出成类似这样:

<ul class="lng">
  
<li class="en">
    
<href="real-url-of-en">EN</a>
  
</li>
  
<li class="fr">
    
<href="real-url-of-fr">FR</a>
  
</li>
</ul>

但是这样做有个问题,假如我们想改变这个web control的表现,比如把CSS Class从“lng” 改为“language”,那么我们就必须要在这个代码里面修改,并且需要重新编译,这样就带来了极大的不方便。我们需要一种能够不需要重新编译代码就可以改变HTML的方法。
这篇文章就介绍一种这样的方法,解决方案是XML + XSL

原理是,在上面方法中,我们不直接负责HTML输出,而是我们构建一个XML文件在内存中,然后我们需要自己编写一个XSL文件来定义这个XML的表现。 

具体这样:(一共两步)
1。 改写上面方法为:

protected override void RenderContents(HtmlTextWriter output)
        
{
            
            SPList myList 
= GetListByName(List);
           
            
if (myList != null)
            
{
                
try
                
{
                    XslCompiledTransform transformer 
= new XslCompiledTransform();
                    
string s = SPUtility.GetGenericSetupPath("TEMPLATE\\LAYOUTS"+ XslFileRelativeUrl;
                    transformer.Load(s);
                    StringWriter result 
= new StringWriter();
                    XmlDocument mydoc 
= BuildXML(myList);
                    transformer.Transform(mydoc, 
null, result);
                    output.WriteLine(result.ToString());
                }

                
catch (Exception ex)
                
{
                    Page.Response.Write(ex.Message);
                }

            }

        }


其中的BuildXML()方法为:

//     <languages>
//       <language title="EN" code="1033" cssclass="en" url="en-url" />
//       <language title="FR" code="1036" cssclass="fr" url="fr-url" />
//     </languages>
public XmlDocument BuildXML(SPList myList)
        
{
            
if (myList != null)
            
{
                XmlDocument doc 
= new XmlDocument();
                XmlNode docNode 
= doc.CreateXmlDeclaration("1.0""UTF-8"null);
                doc.AppendChild(docNode);
                XmlNode languagesNode 
= doc.CreateElement("languages");
                doc.AppendChild(languagesNode);

                
foreach (SPListItem item in myList.Items)
                
{
                    
if (uint.Parse((string)item["Language Code"]) != getCurrentLanguageCode())
                    
{
                        XmlNode languageNode 
= doc.CreateElement("language");

                        XmlAttribute languageAttribute 
= doc.CreateAttribute("title");
                        languageAttribute.Value 
= (string)item["Title"];
                        languageNode.Attributes.Append(languageAttribute);
                        
                        languageAttribute 
= doc.CreateAttribute("code");
                        languageAttribute.Value 
= (string)item["Language Code"];
                        languageNode.Attributes.Append(languageAttribute);
                        
                        languageAttribute 
= doc.CreateAttribute("cssclass");
                        languageAttribute.Value 
= (string)item["CSS Class"];
                        languageNode.Attributes.Append(languageAttribute);
                        
                        languageAttribute 
= doc.CreateAttribute("url");
                        languageAttribute.Value 
= ((Microsoft.SharePoint.Publishing.Fields.LinkFieldValue)item["Language URL"]).NavigateUrl;
                        languageNode.Attributes.Append(languageAttribute);
                        
                        languagesNode.AppendChild(languageNode);                      
                        
                    }

                }

                
return doc;
            }

            
else return null;
        }


2. 定义XSL文件:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
 
<xsl:template match="/">
  
<ul class="lng">
   
<xsl:for-each select="languages/language">
    
<li class="{@cssclass}"> 
     
<href="{@url}">
      
<xsl:value-of select="@title"/>
     
</a>
    
</li>
   
</xsl:for-each>
  
</ul>
 
</xsl:template>
</xsl:stylesheet>

这样就好了,以后想改HTML就直接改上面的XSL文件就可以了,再也不用重新编译了。而且这样做就把表现层的东西从代码中分离了,比较符合现代的软件设计思想。
可以看到上面这个控件是在SharePoint中用的,但是其思想完全不局限于SharePoint。

Feedback

#1楼    回复  引用  查看    

2008-02-27 04:49 by zhkn      
性能就是这么下降的

#2楼    回复  引用    

2008-02-27 08:01 by zjfeiye [未注册用户]
楼主是否考虑过部署的问题,本来直接拖放控件即可,现在需要在网站项目中部署XSLT文件了

#3楼    回复  引用    

2008-02-27 09:01 by uioi [未注册用户]
如果是嵌在某个网页的代码里面 这样会比较麻烦,不过还是提供了一个解决方法。

#4楼    回复  引用  查看    

2008-02-27 09:10 by nfa2dfa      
(生成xml+额外的文件IO+解释xml+xsl处理器执行) ?? "所谓的不用重新编译"

#5楼    回复  引用  查看    

2008-02-27 09:20 by wingoo      
文件是可以缓存的,,
我以前的做法是所有的html代码都放在资源文件里面
这样把程序写完后,其他的工作就是美工的了
可以参考下cs2

#6楼    回复  引用    

2008-02-27 09:26 by yingzi2 [未注册用户]
...LZ佩服你的勇气...

#7楼    回复  引用    

2008-02-27 09:41 by zhy2002 [未注册用户]
@zhkn
这个方法的核心在于表现层代码与数据的分离,如果不用html改用svg表现数据只要换一个xsl就行了。支持楼主。

#8楼    回复  引用  查看    

2008-02-27 10:16 by kmfree      
基本思路不错,但还存在一些问题。同一个控件如果在不同的页面中有不一样的显示的话,就有些问题。
比如说我们制作一个文章列表的控件,在A页面中,只需要显示出标题,不需要其它内容;而在B页面中,却需要显示出发布时间,这个时候如何解决?

#9楼    回复  引用  查看    

2008-02-27 10:37 by Cat Chen      
思路不错,其实又回到了UserControl的模型──用mark up表示视图,用code表示逻辑。不过,既然有现成的UserControl模型了,我还是建议使用UserControl模型,或者稍微底层一些的TemplateControl模型,而没必要自己实现一种全新的模板。

#10楼    回复  引用    

2008-02-27 10:47 by yw [未注册用户]
还不如搞个客户端的XSL...
XslCompiledTransform 转换XML时为进行编译,有临时的dll,速度倒是快,不过请求量大了,临时的dll无数啊。

#11楼    回复  引用  查看    

2008-02-27 11:19 by 汉广      
很有创意,不过在服务器用xsl效率就下来了。
其实用Web Control制造Html还是挺不错的,你提到的那些问题也不是没办法解决。。。

-------------------------
汉广同学正在找工作,你可以点击

,浏览他的简历,欢迎点评:-)

#12楼    回复  引用  查看    

2008-02-27 15:49 by 非我      
的确有点画蛇添足,嗯嗯,不过还是要学习,xsl还是很强的

#13楼 [楼主]   回复  引用  查看    

2008-02-27 17:49 by 夏天的感觉      
用这个方法的最大初衷就是,涉及到任何HTML的更改,不需要再经过开发人员的介入就可以完成。
关于部署问题,是的,我们需要在服务器上放置这个XSL文件。
性能问题呢,我想我们往往需要在性能和功能上面做一些平衡,而且至少现在在用户的角度看起来,没有性能方面的差别。还好我们不是嵌入式手机编程,我们有强大的服务器 :)
至于在不同页面显示不同的问题,我想我们可以在control中引进Property,比如:
A页面: namespace1:control showContent="false" runat = "server"
B页面: namespace1:control showContent="true" runat = "server"

在服务器端用XSL效率到底会有多大的影响?老实说我没有考虑很多这方面。
这里主要是提一种思路来抛砖引玉,诸位的评论也让我想到一些以前没考虑到的问题。多谢

#14楼    回复  引用    

2008-02-28 10:34 by Intermapper [未注册用户]
XSL还是挺强的,但是就是复杂!

#15楼    回复  引用  查看    

2008-02-28 17:30 by xiao_p      
很不错啊,就是这样的话,通过xsl转换有的时候反而不如直接改写html简单。

不过,确实是一种好的思路。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-02-27 02:23 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: