当前位置: 代码网 > it编程>编程语言>C# > WPF实现html中的table控件的示例代码

WPF实现html中的table控件的示例代码

2024年05月15日 C# 我要评论
前言相信很多做wpf开发的小伙伴都遇到过表格类的需求,虽然现有的grid控件也能实现,但是使用起来的体验感并不好,比如要实现一个excel中的表格效果,估计你能想到的第一个方法就是套border控件,

前言

相信很多做wpf开发的小伙伴都遇到过表格类的需求,虽然现有的grid控件也能实现,但是使用起来的体验感并不好,比如要实现一个excel中的表格效果,估计你能想到的第一个方法就是套border控件,用这种方法你需要控制每个border的边框,并且在一堆bordr中找到grid.row,grid.column来确定位置,明明很简单的一个功能,硬是耗费了大量时间。grid的这种设计虽然功能很强大,但是同时也导致了操作繁琐可读性非常差的问题。此时做过web开发的人肯定很想念html中的table元素,没错,我也是这样想的,如果能把html中的table元素搬到wpf中,那问题就轻松解决了,今天我们就来解决这个问题。

一、准备工作

我们先来认识一下table元素,其实最开始的网页功能相对简单,table元素主要用于展示文本和基本的排版。然而随着html标准的更新,table元素越来越复杂,很多功能在不同的标准中写法可能不一样,甚至有的功能只能在css中实现,这种情况我们成全照搬html中的写法肯定不现实,也完全没必要。所以必须做一个取舍。由于wpf中并没有css的概念,所以我们尽量舍弃css中的写法,使用wpf中类似的属性写法来开发,以下为统计出来的可用属性。

二、需求分析

既然我们要复刻一个东西,第一步肯定是要先搞清楚这个东西的内在逻辑,所以我们先来看看html中的table元素是怎么回事。

2.1 table结构

<table>
  <tr>
    <th>header1</th>
    <th>header2</th>
    <th>header3</th>
  </tr>
  <tr>
    <td>value1</td>
    <td>value2</td>
    <td>value3</td>
  </tr>
  <tr>
    <td>value4</td>
    <td>value5</td>
    <td>value6</td>
  </tr>
</table>

2.1.1 table

table为表格根元素,table内可以放置多个tr。

2.1.2 tr 

tr表示表格中的一行,一行可以放置若干个td。

2.1.3 td

td为表格单元格,td可以设置rowspan属性合并多个行,可以设置colspan合并多个列。

2.2 尺寸单位

2.2.1适用范围

table的width,height属性,tr的height属性,td的width,heigth属性。

2.2.2 取值范围

百度比(例:width="50%")

像素(例:width="500")

不设置(自动计算)

2.3 布局逻辑

2.3.1 table

2.3.1.1 width="50%"

宽度占可用空间的50%,当父控件尺寸改变时会重新计算宽度,如果所有td子元素的尺寸之合大于table宽度(width="50%"),table宽度==sum(td.width)。

2.3.1.2 width="500"

宽度占500像素,当父控件尺寸改变时不会重新计算宽度,如果所有td子元素的尺寸之合大于table宽度(width="500"),table宽度==sum(td.width)。

2.3.1.3 不设置宽度

不设置宽度的情况下,宽度根据td子元素的宽度计算,sum(td.width)。

2.3.2 tr

2.3.2.1 height="50%"

高度占table元素总高的50%,当父控件尺寸改变时会重新计算高度,当tr中高度最高的td超过了tr的50%时,整行高度以该td的高度为准。

2.3.2.2 height="500"

高度占500像素,当父控件尺寸改变时会重新计算高度,当tr中高度最高的td超过了tr的500像素时,整行高度以该td的高度为准。

2.3.2.3 不设置高度

不设置高度的情况下,以最高的td子元素为准。

2.3.3 td

2.3.3.1 width="50%"

宽度占table宽度的50%,当剩余宽度不足以分配给其它列时会压缩该列的50%宽度,分配给其它列。该列的实际宽度以该列所有td的最大宽度为准。

2.3.3.2 width="50"

宽度占50像素,当剩余宽度不足以分配给其它列时会压缩该列的50像素宽度,分配给其它列。该列的实际宽度以该列所有td的最大宽度为准。

2.3.3.3 不设置宽度

不设置宽度的情况下,如果其它设置了宽度的列分配完宽度后,剩余宽度大于所有td的最小宽度的总合,那么未设置宽度的列会平均分配剩余的宽度,如果剩余的宽度小于所有td最小宽度的总合,那么所有td的宽度按最小宽度分配,其它已设置宽度的列则压缩宽度。该列的实际宽度以该列所有td的最大宽度为准。

2.3.3.4 height="50%"

高度占table高度的50%,当剩余高度不足以分配给其它行时会压缩该行的50%高度,分配给其它行。该行的实际高度以该行所有td的最大高度为准。如果最高td的高度大于tr,则以最高的td为准,如果小于tr,则以tr的高度为准。

2.3.3.5 height="50"

高度占50像素,当剩余高度不足以分配给其它行时会压缩该行的50像素高度,分配给其它行。该行的实际高度以该行所有td的最大高度为准。如果最高td的高度大于tr,则以最高的td为准,如果小于tr,则以tr的高度为准。

2.3.3.6 不设置高度

不设置高度的情况下,如果其它设置了高度的行分配完高度后,剩余高度大于所有td的最小高度的总合,那么未设置高度的行会平均分配剩余的高度,如果剩余的高度小于所有td最小高度的总合,那么所有td的高度按最小高度分配,其它已设置高度的行则压缩高度。该行的实际高度以该列所有td的最大高度为准。

三、功能实现

通过对需求的分析,我们知道至少应该有3个类来实现表格功能,分别是table、tr、td,我们下面来看看怎么来实现它们。

3.1 table控件

table是一个在界面上需要呈现的元素,该控件主要处理布局及排列,不需要控件模板,所以不应该继承自control类,那么可不可以继承自panel呢,明显也不行,panel的尺寸及布局系统继承自frameworkelement,并不能给它的宽度设置width="50%"这种值,所以它不仅不能继承自panel,也不能继承自frameworkelement,所以table应该继承自uielement类,我们需要在table写自己的尺寸及布局管理功能,以下为talbe的示例代码。

[contentproperty("rows")]
public class table : uielement
{
    /// <summary>
    /// 获取或设置行
    /// </summary>
    public trcollection rows
    {
        get { return (trcollection)getvalue(rowsproperty); }
        private set { setvalue(rowsproperty, value); }
    }

    public static readonly dependencyproperty rowsproperty =
        dependencyproperty.register("rows", typeof(trcollection), typeof(table));

    /// <summary>
    /// 获取或设置宽度
    /// </summary>
    public tablelength width
    {
        get { return (tablelength)getvalue(widthproperty); }
        set { setvalue(widthproperty, value); }
    }

    public static readonly dependencyproperty widthproperty =
        dependencyproperty.register("width", typeof(tablelength), typeof(table));

    /// <summary>
    /// 获取或设置高度
    /// </summary>
    public tablelength height
    {
        get { return (tablelength)getvalue(heightproperty); }
        set { setvalue(heightproperty, value); }
    }

    public static readonly dependencyproperty heightproperty =
        dependencyproperty.register("height", typeof(tablelength), typeof(table));
}

3.2 tr

tr在table里主要的作用是表达逻辑关系,不需要在界面上呈现,所以我们可以让它继承自dependencyobject,可以绑定属性就行了,以下为示例代码。

[contentproperty("cells")]
public class tr : dependencyobject
{
    /// <summary>
    /// 获取或设置单元格
    /// </summary>
    public tdcollection cells
    {
        get { return (tdcollection)getvalue(cellsproperty); }
        private set { setvalue(cellsproperty, value); }
    }

    public static readonly dependencyproperty cellsproperty =
        dependencyproperty.register("cells", typeof(tdcollection), typeof(tr));

    /// <summary>
    /// 获取或设置高度
    /// </summary>
    public tablelength height
    {
        get { return (tablelength)getvalue(heightproperty); }
        set { setvalue(heightproperty, value); }
    }

    public static readonly dependencyproperty heightproperty =
        dependencyproperty.register("height", typeof(tablelength), typeof(tr));
}

3.3 td

td的情况与table类似,需要在界面上呈现,并且有自己的尺寸及布局逻辑,所以继承自uielement,以下为示例代码。

public class td : uielement
{
    /// <summary>
    /// 获取或设置需要跨的列数
    /// </summary>
    public int colspan
    {
        get { return (int)getvalue(colspanproperty); }
        set { setvalue(colspanproperty, value); }
    }

    public static readonly dependencyproperty colspanproperty =
        dependencyproperty.register("colspan", typeof(int), typeof(td), new propertymetadata(1));

    /// <summary>
    /// 获取或设置需要跨的行数
    /// </summary>
    public int rowspan
    {
        get { return (int)getvalue(rowspanproperty); }
        set { setvalue(rowspanproperty, value); }
    }

    public static readonly dependencyproperty rowspanproperty =
        dependencyproperty.register("rowspan", typeof(int), typeof(td), new propertymetadata(1));

    /// <summary>
    /// 获取或设置宽度
    /// </summary>
    public tablelength width
    {
        get { return (tablelength)getvalue(widthproperty); }
        set { setvalue(widthproperty, value); }
    }

    public static readonly dependencyproperty widthproperty =
        dependencyproperty.register("width", typeof(tablelength), typeof(table));

    /// <summary>
    /// 获取或设置高度
    /// </summary>
    public tablelength height
    {
        get { return (tablelength)getvalue(heightproperty); }
        set { setvalue(heightproperty, value); }
    }

    public static readonly dependencyproperty heightproperty =
        dependencyproperty.register("height", typeof(tablelength), typeof(table));
}

measurecore()

该方法传入一个名为availablesize的size参数,该参数为控件可用的最大尺寸,我们需要通过这个参数计算各个单元格的排列位置及尺寸,并根据排列情况返回一个控件的期望尺寸,以下为实现的大致流程。

1 通过table的height及width参数计算出真实的尺寸;

2 读取table的tr属性,取出所有td,并定义一个二维数组将所有td存放进去(td[n,n]),如果td的rowspan或colspan参数大于1则将被合并的位置存入一个null。

3 根据第2步的填充结果再定义一个存放坐标的二维数据(size[n,n]);

4 测量td子元素的尺寸,计算每个单元格实际尺寸,根据td子元素尺寸计算是否需要压缩尺寸,计算完成后将单元格的尺寸存入第3步的数组中;

5 根据第3步保存的尺寸数据计算单元格跨行或跨列后的尺寸;

6 将计算出的table实际尺寸返回给measurecore方法,以供下一步排列使用;

arrangecore()

该方法处理子控件的位置排列,循环调用每一个单元格的arrange()方法,传入测量位置及尺寸就可以了。

onrender()

该方法读取bordercolor、bgcolor等参数画线及填充颜色,表格的外观都是由它画出来的。

四、运行效果

4.1 默认效果

<qs:table
    width="50%"
    height="50%"
    align="center"
    border="1 solid red"
    valign="middle">
    <qs:tr>
        <qs:th>11</qs:th>
        <qs:th>12</qs:th>
        <qs:th>13</qs:th>
        <qs:th>14</qs:th>
        <qs:th>15</qs:th>
        <qs:th>16</qs:th>
    </qs:tr>
    <qs:tr>
        <qs:td>21</qs:td>
        <qs:td>22</qs:td>
        <qs:td>23</qs:td>
        <qs:td>24</qs:td>
        <qs:td>25</qs:td>
        <qs:td>26</qs:td>
    </qs:tr>
    <qs:tr>
        <qs:td>31</qs:td>
        <qs:td>32</qs:td>
        <qs:td>33</qs:td>
        <qs:td>34</qs:td>
        <qs:td>35</qs:td>
        <qs:td>36</qs:td>
    </qs:tr>
    <qs:tr>
        <qs:td>41</qs:td>
        <qs:td>42</qs:td>
        <qs:td>43</qs:td>
        <qs:td>44</qs:td>
        <qs:td>45</qs:td>
        <qs:td>46</qs:td>
    </qs:tr>
</qs:table>

4.2 合并相邻的线

<qs:table
    width="50%"
    height="50%"
    align="center"
    border="1 solid red collapse"
    valign="middle">
    <qs:tr>
        <qs:th>11</qs:th>
        <qs:th>12</qs:th>
        <qs:th>13</qs:th>
        <qs:th>14</qs:th>
        <qs:th>15</qs:th>
        <qs:th>16</qs:th>
    </qs:tr>
    <qs:tr>
        <qs:td>21</qs:td>
        <qs:td>22</qs:td>
        <qs:td>23</qs:td>
        <qs:td>24</qs:td>
        <qs:td>25</qs:td>
        <qs:td>26</qs:td>
    </qs:tr>
    <qs:tr>
        <qs:td>31</qs:td>
        <qs:td>32</qs:td>
        <qs:td>33</qs:td>
        <qs:td>34</qs:td>
        <qs:td>35</qs:td>
        <qs:td>36</qs:td>
    </qs:tr>
    <qs:tr>
        <qs:td>41</qs:td>
        <qs:td>42</qs:td>
        <qs:td>43</qs:td>
        <qs:td>44</qs:td>
        <qs:td>45</qs:td>
        <qs:td>46</qs:td>
    </qs:tr>
</qs:table>

4.3 合并单元格

<qs:table
    width="50%"
    height="50%"
    align="center"
    border="1 solid red collapse"
    valign="middle">
    <qs:tr>
        <qs:th>11</qs:th>
        <qs:th>12</qs:th>
        <qs:th>13</qs:th>
        <qs:th>14</qs:th>
        <qs:th>15</qs:th>
        <qs:th>16</qs:th>
    </qs:tr>
    <qs:tr>
        <qs:td colspan="6">21</qs:td>
    </qs:tr>
    <qs:tr>
        <qs:td rowspan="2">31</qs:td>
        <qs:td>32</qs:td>
        <qs:td colspan="2" rowspan="2">33</qs:td>
        <qs:td>35</qs:td>
        <qs:td rowspan="2">36</qs:td>
    </qs:tr>
    <qs:tr>
        <qs:td>42</qs:td>
        <qs:td>45</qs:td>
    </qs:tr>
</qs:table>

4.4 综合案例

<qs:table
       width="50%"
       height="50%"
       align="center"
       border="1 solid black collapse"
       valign="middle">
    <qs:tr
           height="40"
           align="center"
           bgcolor="#ffaaaaaa"
           valign="middle">
        <qs:th width="40" bgcolor="#ffaaaaaa">11</qs:th>
        <qs:th width="10%">12</qs:th>
        <qs:th>13</qs:th>
        <qs:th>14</qs:th>
        <qs:th>15</qs:th>
        <qs:th>16</qs:th>
    </qs:tr>
    <qs:tr
           height="30%"
           align="center"
           bgcolor="#ff5f5ff1"
           valign="middle">
        <qs:td bgcolor="#ffaaaaaa">21</qs:td>
        <qs:td>22</qs:td>
        <qs:td width="20%">23</qs:td>
        <qs:td>24</qs:td>
        <qs:td>25</qs:td>
        <qs:td>26</qs:td>
    </qs:tr>
    <qs:tr
           height="30%"
           align="center"
           bgcolor="#ffea8633"
           valign="middle">
        <qs:td bgcolor="#ffaaaaaa">31</qs:td>
        <qs:td>32</qs:td>
        <qs:td>33</qs:td>
        <qs:td>34</qs:td>
        <qs:td>35</qs:td>
        <qs:td>36</qs:td>
    </qs:tr>
    <qs:tr
           height="30%"
           align="center"
           bgcolor="#ff5f5ff1"
           valign="middle">
        <qs:td bgcolor="#ffaaaaaa">41</qs:td>
        <qs:td>42</qs:td>
        <qs:td>43</qs:td>
        <qs:td>44</qs:td>
        <qs:td width="150">45</qs:td>
        <qs:td>46</qs:td>
    </qs:tr>
</qs:table>

4.5 课表

<qs:table
    width="600"
    height="250"
    align="center"
    border="1 solid black collapse"
    valign="middle">
    <qs:tr
        height="60"
        align="center"
        bgcolor="#ffe5e5e5"
        valign="middle">
        <qs:th colspan="2">
            <textblock text="课时/日期" />
        </qs:th>
        <qs:th>星期一</qs:th>
        <qs:th>星期二</qs:th>
        <qs:th>星期三</qs:th>
        <qs:th>星期四</qs:th>
        <qs:th>星期五</qs:th>
    </qs:tr>
    <qs:tr align="center" valign="middle">
        <qs:td rowspan="4">上午</qs:td>
        <qs:td width="100">第1节</qs:td>
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
    </qs:tr>
    <qs:tr align="center" valign="middle">
        <qs:td>第2节</qs:td>
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
    </qs:tr>
    <qs:tr align="center" valign="middle">
        <qs:td>第3节</qs:td>
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
    </qs:tr>
    <qs:tr align="center" valign="middle">
        <qs:td>第4节</qs:td>
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
    </qs:tr>
    <qs:tr align="center" valign="middle">
        <qs:td rowspan="2">上午</qs:td>
        <qs:td width="100">第5节</qs:td>
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
    </qs:tr>
    <qs:tr align="center" valign="middle">
        <qs:td>第6节</qs:td>
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
        <qs:td />
    </qs:tr>
</qs:table>

说明:文中用“像素”代替尺寸单位是为了便于理解,实际上wpf使用的是设备无关的尺寸单位,请注意分辨。

以上就是wpf实现html中的table控件的示例代码的详细内容,更多关于wpf table控件的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com