| | |
| | | |
| | | IMPLEMENT_DYNAMIC(CExpandableListCtrl, CListCtrl) |
| | | |
| | | CExpandableListCtrl::CExpandableListCtrl() {} |
| | | CExpandableListCtrl::CExpandableListCtrl() |
| | | { |
| | | m_popupCols = { }; |
| | | } |
| | | |
| | | CExpandableListCtrl::~CExpandableListCtrl() {} |
| | | |
| | | BEGIN_MESSAGE_MAP(CExpandableListCtrl, CListCtrl) |
| | |
| | | if (CListCtrl::OnCreate(lpCreateStruct) == -1) |
| | | return -1; |
| | | |
| | | // 报表风格列举例 |
| | | SetExtendedStyle(GetExtendedStyle() |
| | | | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER); |
| | | |
| | |
| | | |
| | | void CExpandableListCtrl::PreSubclassWindow() |
| | | { |
| | | // 报表风格列举例 |
| | | SetExtendedStyle(GetExtendedStyle() |
| | | | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER); |
| | | |
| | | CListCtrl::PreSubclassWindow(); |
| | | } |
| | | |
| | | // ===== 树 API ===== |
| | | CExpandableListCtrl::Node* CExpandableListCtrl::InsertRoot(const std::vector<CString>& cols) |
| | | { |
| | | auto n = std::make_unique<Node>((int)max(1, (int)cols.size())); |
| | |
| | | |
| | | 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.pszText = const_cast<LPTSTR>((LPCTSTR)(n->cols.empty() ? _T("") : n->cols[0])); |
| | | InsertItem(&lvi); |
| | | |
| | | for (int col = 1; col < GetHeaderCtrl()->GetItemCount(); ++col) { |
| | | const int colCount = GetHeaderCtrl() ? GetHeaderCtrl()->GetItemCount() : 1; |
| | | for (int col = 1; col < colCount; ++col) { |
| | | CString txt = (col < (int)n->cols.size()) ? n->cols[col] : _T(""); |
| | | SetItemText(i, col, txt); |
| | | } |
| | | } |
| | | |
| | | // 重建后,按行号颜色数组对齐 |
| | | m_rowColors.resize(GetItemCount()); |
| | | |
| | | SetRedraw(TRUE); |
| | | Invalidate(); |
| | | } |
| | |
| | | return m_visible[i]; |
| | | } |
| | | |
| | | // ===== 颜色 API ===== |
| | | void CExpandableListCtrl::SetNodeColor(Node* n, COLORREF text, COLORREF bk) |
| | | { |
| | | if (!n) return; |
| | | RowColor rc{}; |
| | | rc.text = text; rc.bk = bk; |
| | | rc.hasText = (text != CLR_DEFAULT); |
| | | rc.hasBk = (bk != CLR_DEFAULT); |
| | | m_colorByNode[n] = rc; |
| | | |
| | | for (int i = 0; i < (int)m_visible.size(); ++i) { |
| | | if (m_visible[i] == n) { |
| | | RedrawItems(i, i); |
| | | UpdateWindow(); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CExpandableListCtrl::ClearNodeColor(Node* n) |
| | | { |
| | | if (!n) return; |
| | | auto it = m_colorByNode.find(n); |
| | | if (it != m_colorByNode.end()) { |
| | | m_colorByNode.erase(it); |
| | | for (int i = 0; i < (int)m_visible.size(); ++i) { |
| | | if (m_visible[i] == n) { |
| | | RedrawItems(i, i); |
| | | UpdateWindow(); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CExpandableListCtrl::ClearAllColors() |
| | | { |
| | | m_colorByNode.clear(); |
| | | m_rowColors.clear(); |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | | // 兼容旧接口:按“可见行号”着色 |
| | | void CExpandableListCtrl::SetItemColor(DWORD_PTR iItem, COLORREF TextColor, COLORREF TextBkColor) |
| | | { |
| | | SetItemColorByVisibleIndex((int)iItem, TextColor, TextBkColor); |
| | | } |
| | | void CExpandableListCtrl::SetItemColorByVisibleIndex(int row, COLORREF text, COLORREF bk) |
| | | { |
| | | if (row < 0) return; |
| | | if (row >= (int)m_rowColors.size()) |
| | | m_rowColors.resize(row + 1); |
| | | |
| | | RowColor rc{}; |
| | | rc.text = text; rc.bk = bk; |
| | | rc.hasText = (text != CLR_DEFAULT); |
| | | rc.hasBk = (bk != CLR_DEFAULT); |
| | | m_rowColors[row] = rc; |
| | | |
| | | RedrawItems(row, row); |
| | | UpdateWindow(); |
| | | } |
| | | |
| | | CRect CExpandableListCtrl::expanderRectForRow(int row) const |
| | | { |
| | | CRect rcLabel; |
| | |
| | | |
| | | Node* n = const_cast<CExpandableListCtrl*>(this)->GetNodeByVisibleIndex(row); |
| | | if (!n || n->children.empty()) |
| | | return CRect(0, 0, 0, 0); // 叶子不占位,文本就不会被多推一格 |
| | | return CRect(0, 0, 0, 0); |
| | | |
| | | const int indent = n->level; |
| | | const int left = rcLabel.left + m_expanderPadding + indent * 16; |
| | |
| | | ); |
| | | } |
| | | |
| | | // 颜色计算:优先 Node*,其次行号;若需要则让系统高亮覆盖 |
| | | void CExpandableListCtrl::computeColorsForRow(int row, COLORREF& outText, COLORREF& outBk) const |
| | | { |
| | | outText = ListView_GetTextColor(const_cast<CExpandableListCtrl*>(this)->m_hWnd); |
| | | outBk = ListView_GetBkColor(const_cast<CExpandableListCtrl*>(this)->m_hWnd); |
| | | |
| | | const bool selected = (const_cast<CExpandableListCtrl*>(this)->GetItemState(row, LVIS_SELECTED) & LVIS_SELECTED) != 0; |
| | | const bool focusOnCtrl = (const_cast<CExpandableListCtrl*>(this)->GetSafeHwnd() == ::GetFocus()); |
| | | |
| | | if (m_preserveSelHighlight && selected) { |
| | | outBk = GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHT : COLOR_3DFACE); |
| | | outText = GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT); |
| | | return; |
| | | } |
| | | |
| | | // Node* 颜色 |
| | | if (Node* n = const_cast<CExpandableListCtrl*>(this)->GetNodeByVisibleIndex(row)) { |
| | | auto it = m_colorByNode.find(n); |
| | | if (it != m_colorByNode.end()) { |
| | | if (it->second.hasText) outText = it->second.text; |
| | | if (it->second.hasBk) outBk = it->second.bk; |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 行号颜色 |
| | | if (row >= 0 && row < (int)m_rowColors.size()) { |
| | | const RowColor& rc = m_rowColors[row]; |
| | | if (rc.hasText) outText = rc.text; |
| | | if (rc.hasBk) outBk = rc.bk; |
| | | } |
| | | } |
| | | |
| | | 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 (!m_popupCols.empty()) { |
| | | LPNMITEMACTIVATE pia = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); |
| | | |
| | | // 用 SubItemHitTest 更精准拿到列 |
| | | LVHITTESTINFO ht{}; |
| | | ht.pt = pia->ptAction; |
| | | int hit = SubItemHitTest(&ht); |
| | | if (hit >= 0 && ht.iItem >= 0 && ht.iSubItem >= 0) { |
| | | const int row = ht.iItem; |
| | | const int col = ht.iSubItem; |
| | | |
| | | if (m_popupCols.count(col)) { |
| | | CString full = GetItemText(row, col); |
| | | if (!full.IsEmpty() && _IsCellTruncated(row, col, full)) { |
| | | NMC_ELC_SHOWFULLTEXT nm{}; |
| | | nm.hdr.hwndFrom = m_hWnd; |
| | | nm.hdr.idFrom = GetDlgCtrlID(); |
| | | nm.hdr.code = ELCN_SHOWFULLTEXT; |
| | | nm.iItem = row; |
| | | nm.iSubItem = col; |
| | | nm.text = full; |
| | | |
| | | if (CWnd* pParent = GetParent()) { |
| | | pParent->SendMessage(WM_NOTIFY, nm.hdr.idFrom, reinterpret_cast<LPARAM>(&nm)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | *pResult = 0; |
| | | } |
| | | |
| | |
| | | return; |
| | | |
| | | case CDDS_ITEMPREPAINT: |
| | | { |
| | | const int row = (int)pCD->nmcd.dwItemSpec; |
| | | COLORREF txt, bk; |
| | | computeColorsForRow(row, txt, bk); |
| | | pCD->clrText = txt; |
| | | pCD->clrTextBk = bk; |
| | | *pResult = CDRF_NOTIFYSUBITEMDRAW; |
| | | return; |
| | | } |
| | | |
| | | case CDDS_ITEMPREPAINT | CDDS_SUBITEM: |
| | | { |
| | |
| | | const int col = pCD->iSubItem; |
| | | CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc); |
| | | |
| | | if (col == 0) |
| | | { |
| | | CRect rc; GetSubItemRect(row, 0, LVIR_LABEL, rc); |
| | | Node* n = GetNodeByVisibleIndex(row); |
| | | if (!n) { *pResult = CDRF_DODEFAULT; return; } |
| | | |
| | | // 1) 背景/前景颜色:按是否选中 |
| | | const bool selected = (GetItemState(row, LVIS_SELECTED) & LVIS_SELECTED) != 0; |
| | | const bool focusOnCtrl = (GetSafeHwnd() == ::GetFocus()); |
| | | COLORREF bk = selected ? GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHT : COLOR_3DFACE) |
| | | : ListView_GetBkColor(m_hWnd); |
| | | COLORREF txt = selected ? GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT) |
| | | : ListView_GetTextColor(m_hWnd); |
| | | |
| | | // 仅在需要时填充背景(避免“黑一片”) |
| | | CBrush bkBrush(bk); |
| | | pDC->FillRect(rc, &bkBrush); |
| | | |
| | | // 2) 展开/折叠指示(参考旧项目的右对齐坐标法,做像素对齐,纯GDI) |
| | | if (!n->children.empty()) |
| | | { |
| | | CRect box = expanderRectForRow(row); |
| | | |
| | | // ---- 可调参数:与旧代码命名一致 ---- |
| | | // 右侧留白(与文本间隙/网格线保持距离) |
| | | const int ROFFSET = 2; |
| | | // 闭合/展开的“宽度”设置:奇数更顺眼(9/11 都行) |
| | | const int WIDE = max(9, min(min(box.Width(), box.Height()), 13)); // ▶ 的边长 |
| | | const int WIDE2 = WIDE / 2; // 一半 |
| | | const int EXPANDED_WIDE = WIDE; // ▼ 的边长 |
| | | |
| | | // 轻微内缩,避免贴边(与你旧代码“按钮要刷一下”同效) |
| | | box.DeflateRect(1, 1); |
| | | |
| | | // 统一做偶数对齐,减少半像素锯齿 |
| | | auto even = [](int v) { return (v & 1) ? (v - 1) : v; }; |
| | | |
| | | // 计算“自下向上”的基准偏移,与旧 TreeCtrl 一致 |
| | | // 这里用 box 作为 pRect |
| | | POINT pt[3]; |
| | | if (n->expanded) { |
| | | // ▼ |
| | | int nBottomOffset = (box.Height() - EXPANDED_WIDE) / 2; |
| | | pt[0].x = box.right - ROFFSET - EXPANDED_WIDE; |
| | | pt[0].y = box.bottom - nBottomOffset; |
| | | pt[1].x = box.right - ROFFSET; |
| | | pt[1].y = box.bottom - nBottomOffset; |
| | | pt[2].x = box.right - ROFFSET; |
| | | pt[2].y = box.bottom - nBottomOffset - EXPANDED_WIDE; |
| | | } |
| | | else { |
| | | // ▶ |
| | | int nBottomOffset = (box.Height() - WIDE) / 2; |
| | | |
| | | pt[0].x = box.right - ROFFSET - WIDE2; |
| | | pt[0].y = box.bottom - nBottomOffset - WIDE; |
| | | pt[1].x = box.right - ROFFSET - WIDE2; |
| | | pt[1].y = box.bottom - nBottomOffset; |
| | | pt[2].x = box.right - ROFFSET; |
| | | pt[2].y = box.bottom - nBottomOffset - WIDE2; |
| | | } |
| | | |
| | | // 仅填充,不描边(描边会加重台阶感);颜色用 txt 与主题一致 |
| | | HGDIOBJ oldPen = pDC->SelectObject(GetStockObject(NULL_PEN)); |
| | | HBRUSH hBrush = CreateSolidBrush(txt); |
| | | HGDIOBJ oldBrush = pDC->SelectObject(hBrush); |
| | | |
| | | pDC->Polygon(pt, 3); |
| | | |
| | | pDC->SelectObject(oldPen); |
| | | pDC->SelectObject(oldBrush); |
| | | DeleteObject(hBrush); |
| | | } |
| | | |
| | | |
| | | |
| | | // 3) 文本:基于首列区域右移(区分是否有子节点) |
| | | const int indentPx = n->level * 14; |
| | | const int baseLeft = rc.left + m_expanderPadding + indentPx; |
| | | |
| | | CRect textRc = rc; |
| | | if (!n->children.empty()) { |
| | | // 有子项:预留按钮位 + 文本间隙 |
| | | textRc.left = baseLeft + m_expanderSize + m_textGap; |
| | | } |
| | | else { |
| | | // 叶子行:不预留按钮位,只给一点点叶子间隙(让层级缩进仍然生效) |
| | | constexpr int kLeafGap = 2; // 你可调 0~4 |
| | | textRc.left = baseLeft + kLeafGap; |
| | | } |
| | | |
| | | pDC->SetBkMode(TRANSPARENT); |
| | | pDC->SetTextColor(txt); |
| | | CString txt0 = n->cols.empty() ? _T("") : n->cols[0]; |
| | | pDC->DrawText(txt0, textRc, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS); |
| | | |
| | | |
| | | // —— 画完三角与文本之后,补一条该行的底部横向网格线 —— |
| | | // 仅当开启了 LVS_EX_GRIDLINES 才绘制 |
| | | if (GetExtendedStyle() & LVS_EX_GRIDLINES) |
| | | { |
| | | // 用整行 bounds,保证横线贯穿所有列的可见宽度 |
| | | CRect rcRow; |
| | | GetSubItemRect(row, 0, LVIR_BOUNDS, rcRow); |
| | | |
| | | // 底边 y 坐标(与系统网格线对齐) |
| | | const int y = rcRow.bottom - 1; |
| | | |
| | | // 颜色与系统风格接近;若觉得偏浅,可换 COLOR_3DSHADOW |
| | | CPen pen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)); |
| | | CPen* oldPen = pDC->SelectObject(&pen); |
| | | |
| | | // 横线从行左到行右(当前可见区域) |
| | | pDC->MoveTo(rcRow.left, y); |
| | | pDC->LineTo(rcRow.right, y); |
| | | |
| | | pDC->SelectObject(oldPen); |
| | | } |
| | | |
| | | *pResult = CDRF_SKIPDEFAULT; |
| | | // 如果没有树节点(纯平列表),首列也走默认绘制 |
| | | Node* n = GetNodeByVisibleIndex(row); |
| | | if (col != 0 || !n) { |
| | | *pResult = CDRF_DODEFAULT; |
| | | return; |
| | | } |
| | | |
| | | // 其他列默认绘制 |
| | | *pResult = CDRF_DODEFAULT; |
| | | // 首列自绘(树模式) |
| | | CRect rc; GetSubItemRect(row, 0, LVIR_LABEL, rc); |
| | | |
| | | COLORREF bk, txt; |
| | | computeColorsForRow(row, txt, bk); |
| | | |
| | | CBrush bkBrush(bk); |
| | | pDC->FillRect(rc, &bkBrush); |
| | | |
| | | if (!n->children.empty()) |
| | | { |
| | | CRect box = expanderRectForRow(row); |
| | | const int ROFFSET = 2; |
| | | const int WIDE = max(9, min(min(box.Width(), box.Height()), 13)); |
| | | const int WIDE2 = WIDE / 2; |
| | | const int EXPANDED_WIDE = WIDE; |
| | | |
| | | box.DeflateRect(1, 1); |
| | | |
| | | POINT pt[3]; |
| | | if (n->expanded) { |
| | | int nBottomOffset = (box.Height() - EXPANDED_WIDE) / 2; |
| | | pt[0].x = box.right - ROFFSET - EXPANDED_WIDE; |
| | | pt[0].y = box.bottom - nBottomOffset; |
| | | pt[1].x = box.right - ROFFSET; |
| | | pt[1].y = box.bottom - nBottomOffset; |
| | | pt[2].x = box.right - ROFFSET; |
| | | pt[2].y = box.bottom - nBottomOffset - EXPANDED_WIDE; |
| | | } |
| | | else { |
| | | int nBottomOffset = (box.Height() - WIDE) / 2; |
| | | pt[0].x = box.right - ROFFSET - WIDE2; |
| | | pt[0].y = box.bottom - nBottomOffset - WIDE; |
| | | pt[1].x = box.right - ROFFSET - WIDE2; |
| | | pt[1].y = box.bottom - nBottomOffset; |
| | | pt[2].x = box.right - ROFFSET; |
| | | pt[2].y = box.bottom - nBottomOffset - WIDE2; |
| | | } |
| | | |
| | | HGDIOBJ oldPen = pDC->SelectObject(GetStockObject(NULL_PEN)); |
| | | HBRUSH hBrush = CreateSolidBrush(txt); |
| | | HGDIOBJ oldBrush = pDC->SelectObject(hBrush); |
| | | pDC->Polygon(pt, 3); |
| | | pDC->SelectObject(oldPen); |
| | | pDC->SelectObject(oldBrush); |
| | | DeleteObject(hBrush); |
| | | } |
| | | |
| | | const int indentPx = n->level * 14; |
| | | const int baseLeft = rc.left + m_expanderPadding + indentPx; |
| | | |
| | | CRect textRc = rc; |
| | | if (!n->children.empty()) { |
| | | textRc.left = baseLeft + m_expanderSize + m_textGap; |
| | | } |
| | | else { |
| | | constexpr int kLeafGap = 2; |
| | | textRc.left = baseLeft + kLeafGap; |
| | | } |
| | | |
| | | pDC->SetBkMode(TRANSPARENT); |
| | | pDC->SetTextColor(txt); |
| | | CString txt0 = n->cols.empty() ? _T("") : n->cols[0]; |
| | | pDC->DrawText(txt0, textRc, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS); |
| | | |
| | | if (GetExtendedStyle() & LVS_EX_GRIDLINES) |
| | | { |
| | | CRect rcRow; GetSubItemRect(row, 0, LVIR_BOUNDS, rcRow); |
| | | const int y = rcRow.bottom - 1; |
| | | CPen pen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)); |
| | | CPen* oldPen = pDC->SelectObject(&pen); |
| | | pDC->MoveTo(rcRow.left, y); |
| | | pDC->LineTo(rcRow.right, y); |
| | | pDC->SelectObject(oldPen); |
| | | } |
| | | |
| | | *pResult = CDRF_SKIPDEFAULT; |
| | | return; |
| | | } |
| | | |
| | | } |
| | | |
| | | *pResult = CDRF_DODEFAULT; |
| | | } |
| | | |
| | | // 兼容行为:同步 SetItemText 到 Node->cols;维护行号颜色数组的插入/删除 |
| | | LRESULT CExpandableListCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) |
| | | { |
| | | // 同步 SetItemText 到 Node(A/W 兼容) |
| | | if (message == LVM_SETITEMTEXT |
| | | #ifdef LVM_SETITEMTEXTA |
| | | || message == LVM_SETITEMTEXTA |
| | | #endif |
| | | #ifdef LVM_SETITEMTEXTW |
| | | || message == LVM_SETITEMTEXTW |
| | | #endif |
| | | ) |
| | | { |
| | | int row = static_cast<int>(wParam); |
| | | LVITEM* p = reinterpret_cast<LVITEM*>(lParam); |
| | | if (p) { |
| | | Node* n = GetNodeByVisibleIndex(row); |
| | | if (n) { |
| | | int sub = p->iSubItem; |
| | | if (sub >= (int)n->cols.size()) |
| | | n->cols.resize(sub + 1); |
| | | CString newText = p->pszText ? p->pszText : _T(""); |
| | | n->cols[sub] = newText; |
| | | } |
| | | } |
| | | // 继续交给基类处理 |
| | | } |
| | | |
| | | LRESULT nRet = CListCtrl::WindowProc(message, wParam, lParam); |
| | | |
| | | // 维护行号颜色数组(兼容旧 SetItemColor) |
| | | if (message == LVM_INSERTITEM) { |
| | | if (nRet != -1) { |
| | | LVITEM* p = (LVITEM*)lParam; |
| | | int pos = p ? p->iItem : (int)nRet; |
| | | if (pos < 0) pos = (int)nRet; |
| | | if (pos > (int)m_rowColors.size()) pos = (int)m_rowColors.size(); |
| | | m_rowColors.insert(m_rowColors.begin() + pos, RowColor{}); // 默认色 |
| | | } |
| | | } |
| | | else if (message == LVM_DELETEITEM) { |
| | | if (nRet != 0) { |
| | | int pos = (int)wParam; |
| | | if (pos >= 0 && pos < (int)m_rowColors.size()) |
| | | m_rowColors.erase(m_rowColors.begin() + pos); |
| | | } |
| | | } |
| | | else if (message == LVM_DELETEALLITEMS) { |
| | | if (nRet != 0) { |
| | | m_rowColors.clear(); |
| | | } |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // CExpandableListCtrl.cpp 里 |
| | | void CExpandableListCtrl::ClearTree() |
| | | { |
| | | // 清数据 |
| | | m_roots.clear(); |
| | | m_visible.clear(); |
| | | |
| | | // 清可见项(务必!否则旧页的行会残留) |
| | | SetRedraw(FALSE); |
| | | DeleteAllItems(); |
| | | SetRedraw(TRUE); |
| | | |
| | | Invalidate(); |
| | | } |
| | | |
| | | void CExpandableListCtrl::SetPopupFullTextColumns(const std::vector<int>& cols) |
| | | { |
| | | m_popupCols.clear(); |
| | | for (int c : cols) m_popupCols.insert(c); |
| | | } |
| | | |
| | | bool CExpandableListCtrl::_IsCellTruncated(int row, int col, const CString& text) const |
| | | { |
| | | if (text.IsEmpty()) return false; |
| | | |
| | | // 单元格显示区域宽度 |
| | | CRect rcCell; |
| | | if (!const_cast<CExpandableListCtrl*>(this)->GetSubItemRect(row, col, LVIR_BOUNDS, rcCell)) |
| | | return false; |
| | | |
| | | // 用控件字体测量文本像素宽 |
| | | CClientDC dc(const_cast<CExpandableListCtrl*>(this)); |
| | | CFont* pOld = dc.SelectObject(const_cast<CExpandableListCtrl*>(this)->GetFont()); |
| | | CSize sz = dc.GetTextExtent(text); |
| | | dc.SelectObject(pOld); |
| | | |
| | | const int kPadding = 8; // 预留一点边距/省略号余量 |
| | | return sz.cx > (rcCell.Width() - kPadding); |
| | | } |
| | | |
| | | |