基于DotNetNuke的动态窗体支持(一)
我在使用了DotNetNuke之后,对其中的UserDefinedTable模块比较感兴趣,并且认真的研究了 UserDefinedTable的数据结构和代码,认为这个模块的开发模式非常不错,于是,就基于这个模块,做了一些扩展的工作;和两年前基于 Joolma的一个FacileForms所做的工作有点类似,就是希望以列表式、灵活展示的方式,让用户可以去定义自己的数据结构,无论是列表、展示还 是查询,都希望相对比较方便一些。其实,类似的模块,在DotNetNuke、Drupal、Joomla里面都有相若的例子,可见,对于CMS来说,这 样的需求,是较为普遍的,一个更为“开放”的模块,对于用户来说,其实更容易接受和扩展。
这里我所讲的东西,其实和DotNetNuke关系好像并不是太大,其实,都是ASP.Net的东西。
对比一下UDT、FacileForms等模块,其实已经是相当不错了:支持较为丰富的数据类型、数据结构设计也非常合理,存储和查询做的也非常好——其实我现在对UDT的理解还是在3.3.7基础上的,后面升级了很多,但是都没有时间详细的去看。
不过,有一个问题就是,我觉得我们在日常生活中所使用的Forms,“零碎”更多,比如说,要增加什么颜色的 Title、什么Banner、什么字体等等,所以,我觉得UserDefinedTable本来已经做得很好了,但是在这些“零碎”上,基本上却没有什 么扩展性,于是,在我开发的Form里面,我希望界面的定义可以更加灵活一些,比如说,完全由用户自己定义界面,美工使用DreamWeaver等工具去 定义就可以了,而不需要和程序人员去沟通;只要在设计完成界面之后,在特定的地方,插入一些标识符(TAG),然后,模块就可以对这些标识符进行解析,并 且插入相应的控件。如果可以这样做的话,我们就将所有的界面工作可以完全的丢给美工,让美工去处理就好了。
至于UDT和Joomla的FacileForms为什么没有这么做,我还不太理解;下面,我来说一下我这样做的思路。
1、ASP.Net已经支持动态加载用户控件,用户控件基本上是一个ascx的文件,对于美工来说,这个可以是一个html文件,只要在设计完成之后,修改一下扩展名就可以了。所以,对于Form来说,每一个动态的Form,对应一个唯一的ascx文件;
2、ASP.Net在加载完成ascx文件之后,可以读取ascx文件中的server标记,这就为加载完成之后,立即对服务器控件进行解析提供了方法,举一个例子,我们用下面的方法加载一个ascx控件:

Dim uc AS Control
Dim strFormatFileName = "/Portals/0/RedstartForm/371_1.ascx"
uc = CType(Page.LoadControl(strFormatFileName), Control)
For Each c As Control In uc.Controls
If c.GetType.ToString().Equals("System.Web.UI.WebControls.PlaceHolder") Then
Dim strControlName As String = c.ID.Replace("__", "")
For Each objField As RedstartFormFieldInfo In colFields
If strControlName.ToUpper() = objField.FieldName.ToUpper() Then
objCtl = MyControls.Add(objField.FieldTitle, objField.FieldType, objField.KeyID, objField.FieldName, _
PublicFunction.FormatParameter(objField.DefaultValue, strRowID, strParentID, strRelateRowID), _
objField.Required, bEnabled, objField.IsReadOnly, PublicFunction.FormatParameter(objField.Extended, strRowID, strParentID, strRelateRowID), _
objField.CssEnable, objField.CssDisable)
c.Controls.Add(objCtl)
Exit For
End If
Next
End If
Next
Dim strControlName As String = c.ID.Replace("__", "")
For Each objField As RedstartFormFieldInfo In colFields
If strControlName.ToUpper() = objField.FieldName.ToUpper() Then
objCtl = MyControls.Add(objField.FieldTitle, objField.FieldType, objField.KeyID, objField.FieldName, _
PublicFunction.FormatParameter(objField.DefaultValue, strRowID, strParentID, strRelateRowID), _
objField.Required, bEnabled, objField.IsReadOnly, PublicFunction.FormatParameter(objField.Extended, strRowID, strParentID, strRelateRowID), _
objField.CssEnable, objField.CssDisable)
c.Controls.Add(objCtl)
Exit For
End If
Next
End If
Next
上面的代码,是遍历我们动态加载的ascx控件中的PlaceHolder控件,如果找到PlaceHolder控件的话,则判断,是不是我们已经对之有了定义,如果有了定义的话,则为其增加子控件,这样,就完成了用户界面的遍历过程。
上面的例子中,由于我们只是在动态加载的控件中找PlaceHolder控件,而对其他任何的控件(无论是服务器端, 还是客户端)都置之不理,所以,对于ascx文件来说,我们并不在乎其他的东西,比如样式、风格、字体、图片等等,尽可以全部都放到这个ascx文件中, 我们的解析程序都对之视而不见。比如,我们可以画一个Table,让控件在Table的某一个单元格中,所需要做的,只是在该单元格的代码中放置下面这样 的一个标识符而已:
<asp:PlaceHolder runat="server" ID="__RowName"></asp:PlaceHolder> <!--名称-->
当然,我们在PlaceHolder的ID的前面增加了双下划线(__),这是为了和控件的ID做一个区别(因为我们在PlaceHolder所动态增加的控件是没有下划线的,如果ID重复则会出现错误;)
还有一点需要说明的是,在上面的遍历ascx子控件的代码中,我们并没有对子控件做子控件的遍历,也就是说,如果我们的ascx控件中包含如下的内容的话:
<asp:Table runat="server" id="tblAll">
<TableRow>
<TableCell>
<asp:PlaceHolder runat="server" ID="__RowName"></asp:PlaceHolder> <!--名称-->
</TableCell>
</TableRow>
</asp:Table>
我们遍历到tblAll这个控件的时候,就不会往下去遍历了;这也就要求美工在设计ascx的时候,必须要将我们的 PlaceHolder控件放在顶层;当然,我相信这个对美工不会造成障碍,因为他们在设计的时候,理论上应该不会使用到服务器端的Table控件,他们 使用Client端的<table>应该已经足以完成所有的设计工作了。
我对UDT模块的扩展后,将“是否自定义界面”做成一个Settings,放在模块设置中,这样,一个模块是用系统自行产生的界面(相对比较死板),还是用动态加载ascx的方式产生界面(可以由美工设计,相对比较灵活),就可以由模块的管理员进行设置了。
这个系列的后面,我们谈一下产生的控件的过程,和一些其他的要素等等。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步