SOUI官方论坛

 找回密码
 立即注册
查看: 1793|回复: 2

[转]SOUI中ListCtrl的自绘 listctrl可编辑

[复制链接]
  • TA的每日心情
    开心
    6 天前
  • 签到天数: 942 天

    [LV.10]以坛为家III

    580

    主题

    1340

    帖子

    2万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    28797
    发表于 2019-9-11 13:08:19 | 显示全部楼层 |阅读模式
    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/Wuzm_/article/details/85053812
    一些知识的补充:

    /*
    2018-12-14 14:32:42
    单项选择设置背景color ListCtrl
    */
    通过重写ListCtrl加入部分属性

    详细代码参照ListCtrlex
    核心思想:
    通过记录设置的几行使用什么颜color(没有设置颜color则使用缺省参数)
    通过数据结构map来组织
    设置一次颜color相当于在map中插入一条数据
    擦除颜color相当于在map中删除一条数据
    最后整理好的数据结构在 OnPaint中进行处理

    Tip:删除完数据后 记得需要Invalid 刷新一下控件


    扩展 LLPoint结构 可以用CPoint数组来代替
    //列表中的行的线
    CPoint pts[2];
    pts[0] = { rcItem.left,  rcItem.top };
    pts[1] = { rcItem.right, rcItem.top };
    pRT->DrawLines(pts, 2);

    /*
    2018-12-17 14:47:26
    记录在ListCtrl中提供的一个特殊的坐标值
    */
    CPoint          m_ptOrigin;
    一个原点坐标
    在列表没有滚动条的时候 m_ptOrigin = {0,0}
    当滚动条进行滑动的时候 这个时候原点坐标也会改变
    详细计算过程见 SListCtrl::UpdateScrollBar()

    在重新实现了左键按下的消息事件的时候 m_nSelectItem 这个值将为-1
    也就是选中哪一行 这个需要自己实现。实现过程和选中哪一列类似

    需求:设置多个行选项的背景color,加上框线 列表单元格可编辑

    设计思想:

    背景color思路:
    确定背景的绘制在OnPaint中 通过确认几行需要绘制 通过一些数据结构的处理 很容易在OnPaint中进行实现

    编辑功能思路:
    通过控件SListCtrl中可以很轻松的得到 宽 高 行 列 这些参数 然后可以知道点击的每个单元格的Rect
    在单元格需要编辑的地方双击 通过new SEdit 来进行编辑框的输入 最后将输入的值写入ListCtrl控件
    当Edit失去焦点的时候就是写入值的时候 并销毁SEdit

    代码设计如下

    SListCtrlEx.h
    1. #pragma once
    2. /*
    3. 2018-12-14 14:30:30
    4. 拓展ListCtrl控件 增加背景color 和 编辑功能
    5. */
    6. #include <map>
    7. namespace SOUI
    8. {
    9. #define COLORRED                RGBA(255, 0, 0, 155)
    10.         class ListCtrlExEdit;
    11.         class SListCtrlEx : public SListCtrl
    12.         {
    13.                 SOUI_CLASS_NAME(SListCtrlEx,L"listctrlex")

    14.         public:
    15.                 SListCtrlEx();
    16.                 ~SListCtrlEx();

    17.         public:
    18.                 void SetRowBackGndColor(int iRow, COLORREF clr = COLORRED);        //设置某行的背景color
    19.                 void EraseRowBackGndColor(int iRow);        //去掉某行设置的颜color iRow从0开始
    20.                 void OnSetEditText();

    21.         protected:
    22.                 void OnLButtonDown(UINT nFlags, CPoint point);
    23.                 void OnLButtonDblClk(UINT nFlags, CPoint point);
    24.                 void OnPaint(IRenderTarget * pRT);
    25.                 void DrawItem(IRenderTarget *pRT, CRect rcItem, int nItem);
    26.                

    27.                 SOUI_MSG_MAP_BEGIN()
    28.                         MSG_WM_PAINT_EX(OnPaint)
    29.                         MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
    30.                         MSG_WM_LBUTTONDOWN(OnLButtonDown)
    31.                         SOUI_MSG_MAP_END()

    32.         private:
    33.                 std::map<int, COLORREF> m_maplistexRow;
    34.                 ListCtrlExEdit *m_edit;
    35.                 int m_iRow;
    36.                 int m_iCol;

    37.         };

    38.         class ListCtrlExEdit : public SEdit
    39.         {
    40.         public:
    41.                 SOUI_CLASS_NAME(ListCtrlExEdit, L"listctrlexedit")

    42.                 ListCtrlExEdit(SListCtrlEx* pOwner) :m_ListCtrlEx(pOwner) {}

    43.                 void OnKillFocus(SWND wndFocus)
    44.                 {
    45.                         __super::OnKillFocus(wndFocus);
    46.                         m_ListCtrlEx->OnSetEditText();
    47.                 }

    48.                 SOUI_MSG_MAP_BEGIN()
    49.                         MSG_WM_KILLFOCUS_EX(OnKillFocus)
    50.                 SOUI_MSG_MAP_END()

    51.                 virtual void OnFinalRelease()
    52.                 {
    53.                         delete this;
    54.                 }
    55.         private:
    56.                 SListCtrlEx * m_ListCtrlEx;
    57.         };

    58. }
    复制代码
    SListCtrlEx.cpp
    1. #include "stdafx.h"
    2. #include "SListCtrlEx.h"

    3. namespace SOUI
    4. {
    5.         SListCtrlEx::SListCtrlEx():m_iRow(0),m_iCol(0), m_edit(nullptr)
    6.         {
    7.                 m_maplistexRow.swap(std::map<int, COLORREF>());
    8.         }

    9.         SListCtrlEx::~SListCtrlEx()
    10.         {
    11.                 if (m_maplistexRow.size() > 0)
    12.                 {
    13.                         m_maplistexRow.swap(std::map<int, COLORREF>());
    14.                 }

    15.                 if (m_edit != nullptr)
    16.                 {
    17.                         m_edit->Release();
    18.                         m_edit = nullptr;
    19.                 }
    20.         }

    21.         void SListCtrlEx::SetRowBackGndColor(int iRow, COLORREF clr)
    22.         {
    23.                 if (iRow > GetItemCount())
    24.                 {
    25.                         return;
    26.                 }
    27.                 bool bflag = false;
    28.                 for (auto value : m_maplistexRow)
    29.                 {
    30.                         if (value.first == iRow && value.second == clr)
    31.                         {
    32.                                 bflag = true;
    33.                                 break;
    34.                         }
    35.                 }

    36.                 if (!bflag)
    37.                 {
    38.                         m_maplistexRow.insert(std::pair<int, COLORREF>(iRow, clr));
    39.                 }
    40.         }

    41.         void SListCtrlEx::EraseRowBackGndColor(int iRow)
    42.         {
    43.                 auto pValue = m_maplistexRow.find(iRow);
    44.                 if (pValue != m_maplistexRow.end())
    45.                 {
    46.                         m_maplistexRow.erase(pValue);
    47.                 }
    48.                 Invalidate();
    49.         }

    50.         void SListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
    51.         {
    52.                 if (m_edit != nullptr)
    53.                 {
    54.                         m_edit->KillFocus();
    55.                 }
    56.         }

    57.         void SListCtrlEx::OnLButtonDblClk(UINT nFlags, CPoint point)
    58.         {
    59.                 //计算鼠标点击的位置 在 列表中的是哪一行哪一列   行高:m_nItemHeight
    60.                 m_iRow = 0;
    61.                 m_iCol = 0;
    62.                 CRect rclist = GetListRect();
    63.                 CPoint pt{ 0,0 };
    64.                 pt.x = point.x - rclist.left + m_ptOrigin.x;
    65.                 pt.y = point.y - rclist.top + m_ptOrigin.y;
    66.                 int WidthHead = 0;
    67.                 int WidthBack = 0;

    68.                 //判断第几行 在重写了左键按下事件后 就无法使用基类中给出的选中第几行的那个变量了
    69.                 for (int i = 0; i < GetItemCount(); ++i)
    70.                 {
    71.                         WidthBack = WidthHead + m_nItemHeight;
    72.                         if (pt.y > WidthHead && pt.y < WidthBack)
    73.                         {
    74.                                 m_iRow = i;
    75.                                 break;
    76.                         }
    77.                         WidthHead = WidthBack;
    78.                 }

    79.                 WidthHead = 0;
    80.                 WidthBack = 0;
    81.                 //获取第几列
    82.                 for (int i = 0; i < GetColumnCount(); ++i)
    83.                 {
    84.                         //获取列的宽度  然后计算坐标 与 得到的坐标进行比较 判断区域
    85.                         WidthBack = WidthHead + m_pHeader->GetItemWidth(i);
    86.                         if (pt.x > WidthHead && pt.x < WidthBack)
    87.                         {
    88.                                 m_iCol = i;
    89.                                 break;
    90.                         }
    91.                         WidthHead = WidthBack;
    92.                 }

    93.                 //SMessageBox(NULL, SStringT().Format(L"%d,%d", m_iRow,m_iCol), L"", NULL);
    94.                 //return;

    95.                 //得到了行列 就可以得到区域
    96.                 CRect ItemRect = GetItemRect(m_iRow, m_iCol);

    97.                 wchar_t szEditAttr[] = L"<listctrlexedit transparent="1" align="left" mouse***="1" colorBkgnd="#FFFFFF" colorText="#000000" />";
    98.                 pugi::xml_document xmlDoc;
    99.                 xmlDoc.load_buffer(szEditAttr, sizeof(szEditAttr));
    100.                 if (m_edit == nullptr)
    101.                 {
    102.                         m_edit = new ListCtrlExEdit(this);
    103.                 }
    104.                
    105.                 InsertChild(m_edit);
    106.                 m_edit->InitFromXml(xmlDoc.first_child());
    107.                 ItemRect.left += 4;
    108.                 m_edit->Move(ItemRect);        //将窗口移动到指定位置
    109.                 SStringT strText = GetSubItemText(m_iRow, m_iCol);
    110.                 m_edit->SetWindowTextW(strText);
    111.                 m_edit->SetFocus();
    112.         }

    113.         void SListCtrlEx::OnPaint(IRenderTarget * pRT)
    114.         {
    115.                 SPainter painter;
    116.                 BeforePaint(pRT, painter);
    117.                 CRect rcList = GetListRect();
    118.                 int nTopItem = GetTopIndex();
    119.                 pRT->PushClipRect(&rcList);
    120.                 CRect rcItem(rcList);
    121.                 pRT->DrawRectangle(rcList);

    122.                 rcItem.bottom = rcItem.top;
    123.                 int ilistitem = GetItemCount();
    124.                 rcItem.OffsetRect(0, -(m_ptOrigin.y%m_nItemHeight));
    125.                 for (int nItem = nTopItem; nItem <= (nTopItem + GetCountPerPage(TRUE)) && nItem < GetItemCount(); nItem++)
    126.                 {
    127.                         rcItem.bottom = rcItem.top + m_nItemHeight;

    128.                         DrawItem(pRT, rcItem, nItem);

    129.                         //列表中的行的线
    130.                         CPoint pts[2];
    131.                         pts[0] = { rcItem.left,  rcItem.top };
    132.                         pts[1] = { rcItem.right, rcItem.top };
    133.                         pRT->DrawLines(pts, 2);

    134.                         for (auto value : m_maplistexRow)
    135.                         {
    136.                                 if (nItem == value.first)
    137.                                 {
    138.                                         pRT->FillSolidRect(rcItem, value.second);        //设置变color的行
    139.                                         break;
    140.                                 }
    141.                         }

    142.                         rcItem.top = rcItem.bottom;
    143.                 }

    144.                 pRT->PopClip();
    145.                 AfterPaint(pRT, painter);
    146.         }

    147.         void SListCtrlEx::DrawItem(IRenderTarget * pRT, CRect rcItem, int nItem)
    148.         {
    149.                 BOOL bTextColorChanged = FALSE;
    150.                 int nBgImg = 0;
    151.                 COLORREF crOldText = RGBA(0xFF, 0xFF, 0xFF, 0xFF);
    152.                 COLORREF crItemBg = m_crItemBg;
    153.                 COLORREF crText = m_crText;
    154.                 DXLVITEM lvItem = m_arrItems[nItem];
    155.                 CRect rcIcon, rcText;

    156.                 if (nItem % 2)
    157.                 {
    158.                         if (CR_INVALID != m_crItemBg2)
    159.                         {
    160.                                 crItemBg = m_crItemBg2;
    161.                         }
    162.                 }

    163.                 if (lvItem.checked)
    164.                 {
    165.                         if (m_pItemSkin != NULL)
    166.                         {
    167.                                 nBgImg = 2;
    168.                         }
    169.                         else if (CR_INVALID != m_crItemSelBg)
    170.                         {
    171.                                 crItemBg = m_crItemSelBg;
    172.                         }

    173.                         if (CR_INVALID != m_crSelText)
    174.                         {
    175.                                 crText = m_crSelText;
    176.                         }
    177.                 }
    178.                 else if (m_bHotTrack && nItem == m_nHoverItem)
    179.                 {
    180.                         if (m_pItemSkin != NULL)
    181.                         {
    182.                                 nBgImg = 1;
    183.                         }
    184.                         else if (CR_INVALID != m_crItemHotBg)
    185.                         {
    186.                                 crItemBg = m_crItemHotBg;
    187.                         }

    188.                         if (CR_INVALID != m_crSelText)
    189.                         {
    190.                                 crText = m_crSelText;
    191.                         }
    192.                 }

    193.                 //绘制背景
    194.                 if (CR_INVALID != crItemBg)//先画背景
    195.                 {
    196.                         pRT->FillSolidRect(rcItem, crItemBg);
    197.                 }

    198.                 if (m_pItemSkin != NULL)//有skin,则覆盖背景
    199.                 {
    200.                         m_pItemSkin->Draw(pRT, rcItem, nBgImg);
    201.                 }


    202.                 //  左边加上空白
    203.                 rcItem.left += 4;

    204.                 if (CR_INVALID != crText)
    205.                 {
    206.                         bTextColorChanged = TRUE;
    207.                         crOldText = pRT->SetTextColor(crText);
    208.                 }

    209.                 CRect rcCol(rcItem);
    210.                 rcCol.right = rcCol.left;
    211.                 rcCol.OffsetRect(-m_ptOrigin.x, 0);

    212.                 for (int nCol = 0; nCol < GetColumnCount(); nCol++)
    213.                 {
    214.                         CRect rcVisiblePart;

    215.                         SHDITEM hdi;
    216.                         hdi.mask = SHDI_WIDTH | SHDI_ORDER;
    217.                         m_pHeader->GetItem(nCol, &hdi);
    218.                         rcCol.left = rcCol.right;
    219.                         rcCol.right = rcCol.left + hdi.cx.toPixelSize(GetScale());

    220.                         rcVisiblePart.IntersectRect(rcItem, rcCol);

    221.                         if (rcVisiblePart.IsRectEmpty())
    222.                         {
    223.                                 continue;
    224.                         }

    225.                         // 绘制 checkbox
    226.                         if (nCol == 0 && m_bCheckBox && m_pCheckSkin)
    227.                         {
    228.                                 CSize sizeSkin = m_pCheckSkin->GetSkinSize();
    229.                                 int nOffsetX = 3;
    230.                                 int nOffsetY = (m_nItemHeight - sizeSkin.cy) / 2;
    231.                                 CRect rcCheck;
    232.                                 rcCheck.SetRect(0, 0, sizeSkin.cx, sizeSkin.cy);
    233.                                 rcCheck.OffsetRect(rcCol.left + nOffsetX, rcCol.top + nOffsetY);
    234.                                 m_pCheckSkin->Draw(pRT, rcCheck, lvItem.checked ? 4 : 0);

    235.                                 rcCol.left = sizeSkin.cx + 6 + rcCol.left;
    236.                         }

    237.                         DXLVSUBITEM& subItem = lvItem.arSubItems->GetAt(hdi.iOrder);

    238.                         if (subItem.nImage != -1 && m_pIconSkin)
    239.                         {
    240.                                 int nOffsetX = m_ptIcon.x;
    241.                                 int nOffsetY = m_ptIcon.y;
    242.                                 CSize sizeSkin = m_pIconSkin->GetSkinSize();
    243.                                 rcIcon.SetRect(0, 0, sizeSkin.cx, sizeSkin.cy);

    244.                                 if (m_ptIcon.x == -1)
    245.                                 {
    246.                                         nOffsetX = m_nItemHeight / 6;
    247.                                 }

    248.                                 if (m_ptIcon.y == -1)
    249.                                 {
    250.                                         nOffsetY = (m_nItemHeight - sizeSkin.cy) / 2;
    251.                                 }

    252.                                 rcIcon.OffsetRect(rcCol.left + nOffsetX, rcCol.top + nOffsetY);
    253.                                 m_pIconSkin->Draw(pRT, rcIcon, subItem.nImage);
    254.                         }

    255.                         UINT align = DT_SINGLELINE;
    256.                         rcText = rcCol;

    257.                         if (m_ptText.x == -1)
    258.                         {
    259.                                 rcText.left = rcIcon.Width() > 0 ? rcIcon.right + m_nItemHeight / 6 : rcCol.left;
    260.                         }
    261.                         else
    262.                         {
    263.                                 rcText.left = rcCol.left + m_ptText.x;
    264.                         }

    265.                         if (m_ptText.y == -1)
    266.                         {
    267.                                 align |= DT_VCENTER;
    268.                         }
    269.                         else
    270.                         {
    271.                                 rcText.top = rcCol.top + m_ptText.y;
    272.                         }

    273.                         pRT->DrawText(subItem.strText, subItem.cchTextMax, rcText, align);

    274.                         //划线 列的线段
    275.                         CPoint pt[2];
    276.                         pt[0] = { rcText.left,rcText.top };
    277.                         pt[1] = { rcText.left,rcText.bottom };
    278.                         pRT->DrawLines(pt, 2);
    279.                 }

    280.                 if (bTextColorChanged)
    281.                 {
    282.                         pRT->SetTextColor(crOldText);
    283.                 }
    284.                 CPoint pt[2];
    285.                 pt[0] = { rcText.left,rcText.top };
    286.                 pt[1] = { rcText.left,rcText.bottom };
    287.                 pRT->DrawLines(pt, 2);
    288.         }

    289.         void SListCtrlEx::OnSetEditText()
    290.         {
    291.                 if (m_edit != nullptr)
    292.                 {
    293.                         SStringT strEdit = m_edit->GetWindowTextW();
    294.                         SetSubItemText(m_iRow, m_iCol, strEdit);
    295.                         m_edit->Release();
    296.                         RemoveChild(m_edit);
    297.                         m_edit = nullptr;
    298.                 }
    299.         }
    300. }
    复制代码
    测试代码:
    1. BOOL TestDlg::OnInitDialog(HWND wnd, LPARAM lInitParam)
    2. {
    3.         SListCtrlEx *plist = FindChildByName2<SListCtrlEx>(L"list_testlist");
    4.         SASSERT(plist);

    5.         int pos = 0;
    6.         for (int i = 0; i < 10; ++i)
    7.         {
    8.                 pos = plist->InsertItem(i, L"");
    9.                 plist->SetSubItemText(pos, 0, L"Test");
    10.                 plist->SetSubItemText(pos, 1, L"Test");
    11.                 plist->SetSubItemText(pos, 2, L"Test");
    12.                 plist->SetSubItemText(pos, 3, L"Test");
    13.                 plist->SetSubItemText(pos, 4, L"Test");
    14.                 plist->SetSubItemText(pos, 5, L"Test");
    15.                 plist->SetSubItemText(pos, 6, L"Test");
    16.         }

    17.         plist->SetRowBackGndColor(1);

    18.         plist->SetRowBackGndColor(3);

    19.         plist->SetRowBackGndColor(5);

    20.         plist->SetRowBackGndColor(7);

    21.         plist->SetRowBackGndColor(9);

    22.         return 0;
    23. }
    复制代码
    XML :
    1. <listctrlex pos="16,40" size="562, 200" hotTrack="1" itemHeight="30" headerHeight="30" name="list_testlist" margin="1,1" colorBorder="#000000">
    2.                                 <!-- colorBkgnd="#F8E4EA" -->
    3.                                 <header align="left" itemSwapEnable="1" fixWidth="1">
    4.                                         <items>
    5.                                                 <item width="150">name</item>
    6.                                                 <item width="150">age</item>
    7.                                                 <item width="150">score</item>
    8.                                                 <item width="150">age</item>
    9.                                                 <item width="150">score</item>
    10.                                                 <item width="150">age</item>
    11.                                                 <item width="150">score</item>
    12.                                                 <item width="100" />
    13.                                         </items>
    14.                                 </header>
    15.                         </listctrlex>
    复制代码
    最后实现的效果:
    1.gif

  • TA的每日心情
    开心
    6 天前
  • 签到天数: 942 天

    [LV.10]以坛为家III

    580

    主题

    1340

    帖子

    2万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    28797
     楼主| 发表于 2020-1-29 21:25:14 | 显示全部楼层
    根据自己的需求又扩展了一下

    SouiWizard1.rar (34.86 KB, 下载次数: 9, 售价: 1 SOUI币) 新增SOUI2与SOUI3的兼容性代码,通过宏定义切换,附件为SOUI2下的完整demo


    /*
    2020年1月29日18:05:56  YTM
    1、去除网格左侧偏移的4距离,使表头列与内容列对齐
    2、增加编辑框边框,避免上下行背景色为白色时不好看的问题
    3、增加指定单元格、指定行、指定列不可编辑的功能;
    4、增加整个列表框是否可编辑的属性edit,默认为可编辑*/

    /*
    版权声明:本文为CSDN博主「JamesWu9527」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Wuzm_/article/details/85053812
    2018-12-14 14:30:30
    拓展ListCtrl控件 增加背景色 和 编辑功能
    */



    该用户从未签到

    0

    主题

    19

    帖子

    350

    积分

    02:00元婴期

    Rank: 3Rank: 3

    积分
    350
    发表于 2023-2-17 17:57:16 | 显示全部楼层
    不错,学习了。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|SOUI官方论坛

    GMT+8, 2024-5-4 04:16

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表