多列布局控件






4.88/5 (38投票s)
一个ASP.NET控件,允许你在网页上使用多栏布局(CSS3中已知)...
图 1. - 三栏布局
引言
该控件允许你简单地自动将HTML内容分成多列,并以更易读的多栏布局呈现文章。如果你观察随机报纸的网页,你会注意到文章文本的宽度大约是400px。这是因为更宽的文本阅读起来不太舒适。在纸质报纸中,文章文本被分成多列。这种技术在网页上并不常用,因为它更难实现,因为你需要手动将文章分成多列。这个控件使得这一切成为可能...
查看在线演示
多栏布局
多栏布局是CSS 3规范的一部分(更多信息请访问w3c.org - CSS3模块:多栏布局 [^]),但由于CSS 3仍处于草案阶段,要通过CSS 3在网页上实现多栏布局还需要很长时间。如果你现在想使用它,你需要手动将每篇文章分成多列,并将这些列放入表格或浮动的 DIV
元素中。
该控件会自动完成所有工作,所以你只需要把它放到你的网页上,然后控件就会获取其内容并将其分成指定的列数。
<cc:ColumnControl ColumnCount="3" runat="server">
.. your original long html content ..
</cc:ColumnControl>
该控件如何工作
实现这个控件有两个主要问题。第一个是如何计算内容的预估高度,第二个是如何在不破坏任何东西的情况下将内容分成多列,因为它不能仅仅在特定数量的字符后拆分HTML。我决定在服务器端解决这个问题,但也可以在客户端使用JavaScript实现。我认为服务器端更好,因为它需要相当复杂的代码,在客户端运行会非常慢,而且可能无法在所有客户端上正常工作。另一方面,在服务器端预估HTML元素的大小更加困难。
该控件包含许多功能,允许你指定原始HTML代码的分割方式。该控件将HTML标签分为以下几类:标题标签(h1
, h2
, ...),段落标签(p
, div
, ...),列表标签(ul
, ol
, dl
)和列表项(li
, dd
, dt
)等。如果控件在渲染时达到列的限制,其行为取决于当前的顶层标签。
如果在标题标签中发生断开,该标题将被移到下一列的开头。在段落标签中,控件会将标签分成两部分,第二部分移到下一列。在列表中,控件会等待当前列表项结束,然后将剩余的列表项移到下一列。其他标签不能被分割,控件只会移到标签的末尾。
标题和列表分割
这些图片展示了控件在分割内容时的行为。绿色高亮显示的文本是计算分割位置时第一部分,红色是第二部分。在灰色框中,你可以看到结果。从第二张图片中可以看到一个有趣的事实,如果你给列表(以及段落)添加一些额外的属性,它们会自动复制到第二列,所以这些属性不会丢失。
格式化标签
自动分割效果很好,但有时你可能需要更具体的设置。例如,你希望文档的某个部分被分成三列,第二部分不分割,第三部分分成两列。有时你可能还需要在指定列中插入一些额外的空白(并将内容移到后面)。这正是格式化标签的用途!格式化标签可以作为HTML注释插入到HTML代码中,所以它相当简单。格式化标签在你想从外部资源加载内容,但仍能轻松更改分割设置时也非常有用。
图 2. - 格式化标签的使用
以下代码显示了如何控制分割行为。如果你通过EnableFormatTags
属性启用格式化标签,控件将不会将整个内容分割成多列,而只会分割由cc:section
标签标记的部分。(右侧的图片显示了这段代码生成的示例。)
<cc:ColumnControl runat="server"
EnableFormatTags="true">
<h1>Header</h1>
<!--[cc:section cols=2]-->
<p>First paragraph..</p>
<p>Second paragraph..</p>
<!--[/cc:section]-->
<h2>Small header</h2>
<!--[cc:section cols=3]-->
<p>
This very long paragraph will be
divided into three columns...
</p>
<!--[/cc:section]-->
</cc:ColumnControl>
格式化标签的第二个用途是当你需要将一些内容移动到下一列,并且需要在第一列插入额外空间时。在这种情况下,你可以使用cc:space
标签,正如在下面的例子中可以看到的那样。
<cc:ColumnControl ColumnCount="3" runat="server">
<p>First paragraph..</p>
<!--[cc:space size=50]-->
<p>Second paragraph..</p>
<p>Third paragraph..</p>
</cc:ColumnControl>
有关格式化标签的更高级示例,请参阅 在线演示应用程序 [^]。
最小控件宽度
如下面(请看图3)所示,该控件允许你指定保留多栏布局的最小宽度。这可以防止控件为屏幕分辨率较低或浏览器窗口较小的用户显示过多狭窄的列。此功能仅当控件使用 div
元素渲染列布局时可用。该控件包含此功能的两种不同实现,一种用于Internet Explorer,另一种用于其他浏览器。我认为IE的实现非常有趣,所以我想对此写几句。
当你使用此控件显示HTML时,它会生成两个 div
元素,CSS样式为 width:50%
。当控件宽度小于指定宽度时,我们需要将其更改为 width:100%
。在基于Mozilla的浏览器中,唯一的方法是使用JavaScript(有关更多信息,请查看源代码中的mozscript.js文件)。在IE中,我们可以使用CSS表达式,它看起来像这样(此样式将添加到每个列)
width:expression(document.getElementById('col_ctrl').offsetWidth<600?'100%':'50%');
这正是我们需要的!如果ID为col_ctrl
的元素(该元素包含整个控件)的宽度大于600像素,则列的宽度为50%(内容以多栏布局显示),否则宽度设置为100%,内容以单栏显示。
控件属性
通用属性
从前面的示例可以看出,你可以使用ColumnCount
属性更改列数。当你希望通过格式化标签从内容中控制分割时,可以使用EnableFormatTags
。如果启用了格式化标签,当你在section标签(<!--[cc:section]-->
)中未指定列数时,ColumnCount
将用作默认值。
图 3. - 使用MinColumnsWidth
属性可以做什么
生成的代码
在当前的HTML中,有两种方法可以实现多栏布局。第一种是使用具有指定列数的 table
,第二种是使用 div
标签(带有CSS样式)。这两种方法各有利弊,所以你可以通过RenderMode
属性决定使用哪种。它有以下三种可能的值:
DivFixed
- 使用div
元素生成列。除最后一列外,所有列都具有float:left
的CSS样式来实现列布局。每列都有CSS类cc_col
,其中包含另一个具有cc_cont
类的div
元素。最后一列包含具有cc_last
CSS类的元素。TableFixed
- 生成指定列数的表格。每列的CSS类设置为cc_col
,并且其宽度也以百分比精确设置,因此列宽无法更改。TableVariable
- 类似于前一种方法,生成表格,每列的CSS类设置为cc_col
。表格列未指定宽度,因此可以通过Web浏览器调整宽度。
如果你使用DivFixed
渲染模式,你还可以使用MinColumnsWidth
属性来指定列布局将被保留的最小控件宽度。这意味着如果你将控件缩小到较小的宽度,它将以单列显示全部内容。此功能在 第二个示例 中有演示 [^]。
外观 - 列分割
由于估算元素大小很困难,你可以通过设置TagConstants
和ElementsSizes
属性来帮助控件。第一个属性可以用来指定元素大小之间的比例。例如,如果你期望一个 pre
元素中的一个字符大小与 p
元素中的10个字符大小相同,你可以将此属性设置为 "pre=10"
,控件将使用这些设置来更好地分割。ElementsSizes
属性允许你指定非成对标签所占用的空间。如果你想在文档中插入图像,这非常有用,只需使用此属性(例如 "img=500"
),控件就能更好地估算 img
标签的大小。这些属性的使用在 第三个在线示例页面 中有所演示 [^]。
如上所述,该控件使用三种不同的分割方法。你可以使用HeaderTags
属性指定哪些HTML标签应被视为标题标签(控件不会将标签分割成多列,也不会将其留在列的末尾)。下一类是列表,列表只会在列表项结束后分割。作为列表处理的标签可以使用ListTags
设置,列表项可以使用ListItemTags
更改。最后一类标签是ParagraphTags
,它们可以被分割成多列。SpaceChars
允许你指定可用于分割段落内容的字符。控件还使用所有其他用于文本格式化的标签列表。此列表可以使用PairTags
属性进行修改,但要小心 - 控件期望所有这些标签都有匹配的结束标签!
已知问题
- 控件使用的用于分割HTML内容的解析器并不十分智能。它不期望完全有效的XHTML(它不尝试使用XML类来处理它),但它期望所有成对标签都有结束标签。
- 我尽力测试了控件,但如果你发现任何导致奇怪结果的示例,请联系我!我期待着改进它。
未来工作和历史
- (2005/7/7) - 控件适用于ASP.NET 1.1和ASP.NET 2.0 beta 2。
- (2005/7/7) - 本文的第一个版本发布于CodeProject。