WPF实现html中的table控件

这篇具有很好参考价值的文章主要介绍了WPF实现html中的table控件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

  相信很多做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中类似的属性写法来开发,以下为统计出来的可用属性。

WPF实现html中的table控件

 

二、需求分析

    既然我们要复刻一个东西,第一步肯定是要先搞清楚这个东西的内在逻辑,所以我们先来看看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 取值范围

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

    2. 像素(:width="500")

    3. 不设置(自动计算)

       

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));
}

 

3.4.1 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方法,以供下一步排列使用;

 

    3.4.2 ArrangeCore()

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

 

    3.4.3 OnRender()

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

 

四、行效果

4.1 默认效果

WPF实现html中的table控件

<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 合并相邻的线

WPF实现html中的table控件

 

<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 合并单元格

WPF实现html中的table控件

 

<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 综合案例

WPF实现html中的table控件

  

<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 课表

WPF实现html中的table控件

 

<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使用的是设备无关的尺寸单位,请注意分辨。文章来源地址https://www.toymoban.com/news/detail-843557.html

到了这里,关于WPF实现html中的table控件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • WPF 组态软件实现思路(WPF控件可视化布局)

    一、实现控件选中及自由拖动 二、实现控件对齐功能 三、实现对齐辅助线功能 四、实现框选功能 GitHub地址点此 属性编辑控件基于Devexpress V21.2.3 控件库,如需编译需购买及安装 Devexpress V21.2.3 开发库 脚本编辑基于AvalonEdit开源库 https://github.com/icsharpcode/AvalonEdit 图标控件基于

    2024年02月04日
    浏览(69)
  • WPF-利用装饰器实现控件的自由拖动

    在项目中经常会遇到类似如下要求的需求,创建允许自由拖动的控件,这样的需求可以使用WPF的装饰器Adorner来实现。   装饰器是一种特殊类型的FrameworkElement,装饰器始终呈现在被装饰元素的顶部,用于向用户提供可视化提示。装饰器可以在不改变原有控件结构的基础上,将

    2024年02月11日
    浏览(42)
  • C# wpf 实现Canvas内控件拖动

    第一章 Grid内控件拖动 第二章 Canvas内控件拖动(本章) 第三章 任意控件拖动 第四章 窗口拖动 第五章 附加属性实现任意拖动 第六章 拓展更多拖动功能 有一些业务场景中我们需要拖动控件,上一篇文章《C# wpf 实现Grid内控件拖动》在Grid中实现了控件拖动,在Canvas中也可以实

    2024年02月05日
    浏览(46)
  • WPF --- 非Button自定义控件实现点击功能

    今天在做一个设置文件夹路径的功能,就是一个文本框,加个按钮,点击按钮,弹出 FolderBrowserDialog 再选择文件夹路径,简单做法,可以直接 StackPanel 横向放置一个 TextBox 和一个 Image Button ,然后点击按钮在 后台代码中给 ViewModel 的 FilePath 赋值。但是这样属实不够优雅,UI 不

    2024年02月12日
    浏览(39)
  • 在WPF应用中使用GongSolutions.WPF.DragDrop实现列表集合控件的拖动处理

    WPF应用中,控件本身也可以通过实现事件代码实现拖动的处理,不过如果我们使用GongSolutions.WPF.DragDrop来处理,事情会变得更加简单轻松,它支持很多控件的拖动处理,如ListBox, ListView, TreeView, DataGrid等源自ItemsControl的控件,本篇随笔介绍在工作流模块中拖动TreeView和DataGrid列表

    2024年02月05日
    浏览(42)
  • C# wpf 附加属性实现任意控件拖动调整大小

    第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小(本章) 第五章 拓展更多调整大小功能 前面几节讲了控件拖动改变大小的几种方法,根据不同的布局可以有不同的实现方式。本节主要讲的是利用

    2024年02月11日
    浏览(49)
  • [WPF]原生TabControl控件实现拖拽排序功能

    在UI交互中,拖拽操作是一种非常简单友好的交互。尤其是在ListBox,TabControl,ListView这类列表控件中更为常见。通常要实现拖拽排序功能的做法是自定义控件。本文将分享一种在原生控件上设置附加属性的方式实现拖拽排序功能。 该方法的使用非常简单,仅需增加一个附加属

    2024年02月08日
    浏览(45)
  • C# wpf 实现任意控件(包括窗口)更多调整大小功能

    第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小 第五章 拓展更多调整大小功能(本章) 上一章我们已经实现了任意控件统一的拖动调整功能,能够方便的给任意控件设置拖动调整大小。开发过程

    2024年01月18日
    浏览(42)
  • element-ui table 表格控件实现单选功能

    今天遇到一个需求,感觉挺简单的,需求如下: 就是一个表格,在表格的前面加上一个选择框,注意: 只能单选 然后我就去element ui上粘代码: 虽然element ui上有对应的单选表格,可是并不符合我的需求 然后我又发现下面有一个多选的表格,样式虽然相似,可是还是不符合我

    2024年02月11日
    浏览(54)
  • 使用WPF开发自定义用户控件,以及实现相关自定义事件的处理

    在前面随笔《使用Winform开发自定义用户控件,以及实现相关自定义事件的处理》中介绍了Winform用户自定义控件的处理,对于Winform自定义的用户控件来说,它的呈现方式主要就是基于GDI+进行渲染的,对于数量不多的控件呈现,一般不会觉察性能有太多的问题,随着控件的数量

    2024年02月02日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包