LAPTOP-SNT8I5JK\Boounion
2025-08-20 52b626aa1918c58d2d27e82ac72db12536c13764
1.能展开的ListCtrll
已添加4个文件
已修改5个文件
389 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CControlJobDlg.cpp 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CControlJobDlg.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CExpandableListCtrl.cpp 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CExpandableListCtrl.h 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj.filters 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.cpp 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CControlJobDlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
// CControlJobDlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "CControlJobDlg.h"
#include "afxdialogex.h"
// CControlJobDlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CControlJobDlg, CDialogEx)
CControlJobDlg::CControlJobDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_CONTROL_JOB, pParent)
{
}
CControlJobDlg::~CControlJobDlg()
{
}
void CControlJobDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST1, m_listCtrl);
}
BEGIN_MESSAGE_MAP(CControlJobDlg, CDialogEx)
END_MESSAGE_MAP()
// CControlJobDlg æ¶ˆæ¯å¤„理程序
BOOL CControlJobDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    DWORD dwStyle = m_listCtrl.GetExtendedStyle();
    dwStyle |= LVS_EX_FULLROWSELECT;
    dwStyle |= LVS_EX_GRIDLINES;
    m_listCtrl.SetExtendedStyle(dwStyle);
    HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
    ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL);
    // m_list å·²ç»æ˜¯å¯¹è¯æ¡†ä¸Šçš„ CExpandableListCtrl æˆå‘˜ï¼ˆæ‹–控件改类)
    m_listCtrl.ModifyStyle(0, LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS);
    m_listCtrl.InsertColumn(0, _T("名称"), LVCFMT_LEFT, 260);
    m_listCtrl.InsertColumn(1, _T("状态"), LVCFMT_LEFT, 120);
    m_listCtrl.InsertColumn(2, _T("描述"), LVCFMT_LEFT, 260);
    auto* root1 = m_listCtrl.InsertRoot({ _T("EFEM"), _T("Ready"), _T("Front End Module") });
    m_listCtrl.InsertChild(root1, { _T("Slot #1"), _T("OK"), _T("150mm wafer") });
    m_listCtrl.InsertChild(root1, { _T("Slot #2"), _T("Empty"), _T("") });
    auto* root2 = m_listCtrl.InsertRoot({ _T("Bonder"), _T("Run"), _T("G1+G2 Process") });
    auto* ch21 = m_listCtrl.InsertChild(root2, { _T("Job A"), _T("Proc"), _T("Step 1") });
    m_listCtrl.InsertChild(ch21, { _T("SubStep A1"), _T("Done"), _T("Align") });
    m_listCtrl.InsertChild(ch21, { _T("SubStep A2"), _T("Run"),  _T("Bond") });
    // åˆå§‹è®©é¡¶å±‚展开
    //root1->expanded = true;
    //root2->expanded = true;
    m_listCtrl.RebuildVisible();
    return TRUE;  // return TRUE unless you set the focus to a control
                  // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
SourceCode/Bond/Servo/CControlJobDlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
#pragma once
#include "CExpandableListCtrl.h"
// CControlJobDlg å¯¹è¯æ¡†
class CControlJobDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CControlJobDlg)
public:
    CControlJobDlg(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CControlJobDlg();
protected:
    CExpandableListCtrl m_listCtrl;
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_CONTROL_JOB };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
};
SourceCode/Bond/Servo/CExpandableListCtrl.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,217 @@
#include "stdafx.h"
#include "CExpandableListCtrl.h"
IMPLEMENT_DYNAMIC(CExpandableListCtrl, CListCtrl)
CExpandableListCtrl::CExpandableListCtrl() {}
CExpandableListCtrl::~CExpandableListCtrl() {}
BEGIN_MESSAGE_MAP(CExpandableListCtrl, CListCtrl)
    ON_WM_CREATE()
    ON_NOTIFY_REFLECT(NM_CLICK, &CExpandableListCtrl::OnClick)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CExpandableListCtrl::OnCustomDraw)
END_MESSAGE_MAP()
int CExpandableListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CListCtrl::OnCreate(lpCreateStruct) == -1)
        return -1;
    // æŠ¥è¡¨é£Žæ ¼åˆ—举例
    SetExtendedStyle(GetExtendedStyle()
        | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES);
    // ç¤ºä¾‹åˆ—(可在外部设置)
    if (GetHeaderCtrl() == nullptr || GetHeaderCtrl()->GetItemCount() == 0) {
        InsertColumn(0, _T("名称"), LVCFMT_LEFT, 260);
        InsertColumn(1, _T("状态"), LVCFMT_LEFT, 120);
        InsertColumn(2, _T("描述"), LVCFMT_LEFT, 260);
    }
    return 0;
}
CExpandableListCtrl::Node* CExpandableListCtrl::InsertRoot(const std::vector<CString>& cols)
{
    auto n = std::make_unique<Node>((int)max(1, (int)cols.size()));
    for (size_t i = 0; i < cols.size(); ++i) n->cols[i] = cols[i];
    n->level = 0;
    Node* raw = n.get();
    m_roots.emplace_back(std::move(n));
    return raw;
}
CExpandableListCtrl::Node* CExpandableListCtrl::InsertChild(Node* parent, const std::vector<CString>& cols)
{
    ASSERT(parent);
    auto n = std::make_unique<Node>((int)max(1, (int)cols.size()));
    for (size_t i = 0; i < cols.size(); ++i) n->cols[i] = cols[i];
    n->parent = parent;
    n->level = parent->level + 1;
    Node* raw = n.get();
    parent->children.emplace_back(std::move(n));
    return raw;
}
void CExpandableListCtrl::appendVisible(Node* n)
{
    m_visible.push_back(n);
    if (n->expanded) {
        for (auto& ch : n->children) {
            appendVisible(ch.get());
        }
    }
}
void CExpandableListCtrl::RebuildVisible()
{
    // 1) é‡å»ºå¯è§åºåˆ—
    m_visible.clear();
    for (auto& r : m_roots) appendVisible(r.get());
    // 2) é‡ç»˜/重填数据
    SetRedraw(FALSE);
    DeleteAllItems();
    // æ’入可见行
    for (int i = 0; i < (int)m_visible.size(); ++i) {
        Node* n = m_visible[i];
        LVITEM lvi{};
        lvi.mask = LVIF_TEXT;
        lvi.iItem = i;
        lvi.iSubItem = 0;
        lvi.pszText = const_cast<LPTSTR>((LPCTSTR)(n->cols.empty() ? _T("") : n->cols[0]));
        InsertItem(&lvi);
        for (int col = 1; col < GetHeaderCtrl()->GetItemCount(); ++col) {
            CString txt = (col < (int)n->cols.size()) ? n->cols[col] : _T("");
            SetItemText(i, col, txt);
        }
    }
    SetRedraw(TRUE);
    Invalidate();
}
void CExpandableListCtrl::Expand(Node* n)
{
    if (!n || n->children.empty()) return;
    if (!n->expanded) { n->expanded = true; RebuildVisible(); }
}
void CExpandableListCtrl::Collapse(Node* n)
{
    if (!n || n->children.empty()) return;
    if (n->expanded) { n->expanded = false; RebuildVisible(); }
}
void CExpandableListCtrl::Toggle(Node* n)
{
    if (!n || n->children.empty()) return;
    n->expanded = !n->expanded;
    RebuildVisible();
}
CExpandableListCtrl::Node* CExpandableListCtrl::GetNodeByVisibleIndex(int i) const
{
    if (i < 0 || i >= (int)m_visible.size()) return nullptr;
    return m_visible[i];
}
CRect CExpandableListCtrl::expanderRectForRow(int row) const
{
    CRect rc;
    // å–首列矩形
    if (!GetSubItemRect(row, 0, LVIR_BOUNDS, rc))
        return CRect(0, 0, 0, 0);
    Node* n = const_cast<CExpandableListCtrl*>(this)->GetNodeByVisibleIndex(row);
    int indent = (n ? n->level : 0);
    // ç¼©è¿›ï¼šæ¯çº§ç»™ 16px
    int left = rc.left + m_expanderPadding + indent * 16;
    CRect box(left, rc.CenterPoint().y - m_expanderSize / 2,
        left + m_expanderSize, rc.CenterPoint().y + m_expanderSize / 2);
    return box;
}
void CExpandableListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
    LPNMITEMACTIVATE pia = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
    if (pia->iItem >= 0) {
        CPoint pt = pia->ptAction;
        // å‘½ä¸­å±•开按钮?
        CRect expRc = expanderRectForRow(pia->iItem);
        if (expRc.PtInRect(pt)) {
            Node* n = GetNodeByVisibleIndex(pia->iItem);
            if (n && !n->children.empty()) {
                Toggle(n);
            }
        }
    }
    *pResult = 0;
}
void CExpandableListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
    LPNMLVCUSTOMDRAW pCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
    switch (pCD->nmcd.dwDrawStage)
    {
    case CDDS_PREPAINT:
        *pResult = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYSUBITEMDRAW;
        return;
    case CDDS_ITEMPREPAINT:
        *pResult = CDRF_NOTIFYSUBITEMDRAW;
        return;
    case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
    {
        int row = (int)pCD->nmcd.dwItemSpec;
        int col = pCD->iSubItem;
        CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
        // ä»…在首列绘制展开按钮与缩进引导
        if (col == 0) {
            CRect rc;
            GetSubItemRect(row, 0, LVIR_BOUNDS, rc);
            // é»˜è®¤æ–‡æœ¬è®©ç³»ç»Ÿç”»ï¼šæˆ‘们先画按钮和缩进背景,再返回 CDRF_DODEFAULT
            Node* n = GetNodeByVisibleIndex(row);
            if (n) {
                // ç»˜åˆ¶å±•开三角/方块
                if (!n->children.empty()) {
                    CRect box = expanderRectForRow(row);
                    // å°æ–¹æ¡†
                    pDC->Rectangle(box);
                    // ç”»â€œ+”或“-”
                    CPen pen(PS_SOLID, 1, RGB(0, 0, 0));
                    CPen* oldPen = pDC->SelectObject(&pen);
                    // æ¨ªçº¿
                    pDC->MoveTo(box.left + 2, box.CenterPoint().y);
                    pDC->LineTo(box.right - 2, box.CenterPoint().y);
                    if (!n->expanded) {
                        // ç«–线(表示 + å·ï¼‰
                        pDC->MoveTo(box.CenterPoint().x, box.top + 2);
                        pDC->LineTo(box.CenterPoint().x, box.bottom - 2);
                    }
                    pDC->SelectObject(oldPen);
                }
                // æŠŠæ–‡æœ¬å·¦è¾¹ç•Œå³ç§»ï¼Œç•™å‡ºç¼©è¿›ä¸ŽæŒ‰é’®ç©ºé—´
                // è¿™é‡Œä¸æ”¹ç³»ç»Ÿç»˜åˆ¶çš„æ–‡æœ¬èµ·ç‚¹ï¼Œè€Œæ˜¯é€šè¿‡åœ¨æ–‡æœ¬å‰ç½®ç©ºæ ¼çš„æ–¹å¼å¤„理更简单:
                // æˆ‘们直接改显示文本(性能足够):在 RebuildVisible æ—¶å·²ç»å¡«äº†çº¯æ–‡æœ¬ã€‚
                // å¦‚果你要精准控制文本位置,可以改 OWNERDRAW æˆ–自绘文本。
            }
        }
        *pResult = CDRF_DODEFAULT;
        return;
    }
    }
    *pResult = CDRF_DODEFAULT;
}
SourceCode/Bond/Servo/CExpandableListCtrl.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
#pragma once
#include <vector>
#include <memory>
class CExpandableListCtrl : public CListCtrl
{
    DECLARE_DYNAMIC(CExpandableListCtrl)
public:
    struct Node {
        Node* parent = nullptr;
        std::vector<std::unique_ptr<Node>> children;
        std::vector<CString> cols; // å„列文本
        bool expanded = false;
        int level = 0; // ç¼©è¿›å±‚级
        Node(int nCols = 1) : cols(nCols) {}
    };
    CExpandableListCtrl();
    virtual ~CExpandableListCtrl();
    // æ•°æ®æž„建
    Node* InsertRoot(const std::vector<CString>& cols);
    Node* InsertChild(Node* parent, const std::vector<CString>& cols);
    // å±•å¼€/折叠
    void Expand(Node* n);
    void Collapse(Node* n);
    void Toggle(Node* n);
    // åˆ·æ–°å¯è§åˆ—表
    void RebuildVisible();
    // ä¾¿æ·ï¼šé€šè¿‡å¯è§è¡Œå·å– Node*
    Node* GetNodeByVisibleIndex(int i) const;
protected:
    // æ¶ˆæ¯
    afx_msg int  OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnClick(NMHDR* pNMHDR, LRESULT* pResult);
    afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
    DECLARE_MESSAGE_MAP()
private:
    std::vector<std::unique_ptr<Node>> m_roots;   // é¡¶å±‚节点
    std::vector<Node*>                 m_visible; // å±•开后的可见节点顺序
    int  m_expanderPadding = 6;   // é¦–列内侧边距
    int  m_expanderSize = 10;  // å°ä¸‰è§’/方块大小
    int  m_textGap = 6;
    void appendVisible(Node* n);
    CRect expanderRectForRow(int row) const; // é¦–列展开按钮区域
};
SourceCode/Bond/Servo/Servo.rc
Binary files differ
SourceCode/Bond/Servo/Servo.vcxproj
@@ -202,9 +202,11 @@
  <ItemGroup>
    <ClInclude Include="CBaseDlg.h" />
    <ClInclude Include="CControlJob.h" />
    <ClInclude Include="CControlJobDlg.h" />
    <ClInclude Include="CCustomCheckBox.h" />
    <ClInclude Include="CCollectionEvent.h" />
    <ClInclude Include="CEquipmentPage3.h" />
    <ClInclude Include="CExpandableListCtrl.h" />
    <ClInclude Include="CGlassPool.h" />
    <ClInclude Include="ChangePasswordDlg.h" />
    <ClInclude Include="CMyStatusbar.h" />
@@ -354,9 +356,11 @@
  <ItemGroup>
    <ClCompile Include="CBaseDlg.cpp" />
    <ClCompile Include="CControlJob.cpp" />
    <ClCompile Include="CControlJobDlg.cpp" />
    <ClCompile Include="CCustomCheckBox.cpp" />
    <ClCompile Include="CCollectionEvent.cpp" />
    <ClCompile Include="CEquipmentPage3.cpp" />
    <ClCompile Include="CExpandableListCtrl.cpp" />
    <ClCompile Include="CGlassPool.cpp" />
    <ClCompile Include="ChangePasswordDlg.cpp" />
    <ClCompile Include="CMyStatusbar.cpp" />
SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -178,6 +178,8 @@
    <ClCompile Include="CPageCollectionEvent.cpp" />
    <ClCompile Include="ProcessJob.cpp" />
    <ClCompile Include="CControlJob.cpp" />
    <ClCompile Include="CExpandableListCtrl.cpp" />
    <ClCompile Include="CControlJobDlg.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="AlarmManager.h" />
@@ -362,6 +364,8 @@
    <ClInclude Include="ProcessJob.h" />
    <ClInclude Include="CControlJob.h" />
    <ClInclude Include="SerializeUtil.h" />
    <ClInclude Include="CExpandableListCtrl.h" />
    <ClInclude Include="CControlJobDlg.h" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="Servo.rc" />
SourceCode/Bond/Servo/ServoDlg.cpp
@@ -26,6 +26,7 @@
#include "CPageVarialbles.h"
#include "CPageReport.h"
#include "CPageCollectionEvent.h"
#include "CControlJobDlg.h"
#ifdef _DEBUG
@@ -979,7 +980,8 @@
        }
    }
    else if (id == IDC_BUTTON_JOBS) {
        AfxMessageBox("IDC_BUTTON_CJOBS");
        CControlJobDlg dlg;
        dlg.DoModal();
    }
    else if (id == IDC_BUTTON_PORT_CONFIG) {
        CPortConfigurationDlg dlg;
SourceCode/Bond/Servo/resource.h
Binary files differ