From 52b626aa1918c58d2d27e82ac72db12536c13764 Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期三, 20 八月 2025 11:47:48 +0800
Subject: [PATCH] 1.能展开的ListCtrll
---
SourceCode/Bond/Servo/CExpandableListCtrl.h | 57 ++++++++
SourceCode/Bond/Servo/Servo.vcxproj | 4
SourceCode/Bond/Servo/CControlJobDlg.cpp | 73 ++++++++++
SourceCode/Bond/Servo/CControlJobDlg.h | 30 ++++
SourceCode/Bond/Servo/Servo.vcxproj.filters | 4
SourceCode/Bond/Servo/resource.h | 0
SourceCode/Bond/Servo/Servo.rc | 0
SourceCode/Bond/Servo/CExpandableListCtrl.cpp | 217 +++++++++++++++++++++++++++++++
SourceCode/Bond/Servo/ServoDlg.cpp | 4
9 files changed, 388 insertions(+), 1 deletions(-)
diff --git a/SourceCode/Bond/Servo/CControlJobDlg.cpp b/SourceCode/Bond/Servo/CControlJobDlg.cpp
new file mode 100644
index 0000000..ec9b31c
--- /dev/null
+++ b/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
+}
diff --git a/SourceCode/Bond/Servo/CControlJobDlg.h b/SourceCode/Bond/Servo/CControlJobDlg.h
new file mode 100644
index 0000000..83763f0
--- /dev/null
+++ b/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();
+};
diff --git a/SourceCode/Bond/Servo/CExpandableListCtrl.cpp b/SourceCode/Bond/Servo/CExpandableListCtrl.cpp
new file mode 100644
index 0000000..c15cf59
--- /dev/null
+++ b/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;
+}
+
diff --git a/SourceCode/Bond/Servo/CExpandableListCtrl.h b/SourceCode/Bond/Servo/CExpandableListCtrl.h
new file mode 100644
index 0000000..d4ecb51
--- /dev/null
+++ b/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; // 首列展开按钮区域
+};
+
+
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index 3e4c1c6..603b23b 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index b798ed7..ec95643 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/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" />
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index 1d8a259..1d90814 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/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" />
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index fed2ac6..91dc9ec 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/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;
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index a16ac88..87072ee 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ
--
Gitblit v1.9.3