mirror of
https://github.com/yuanyuanxiang/SimpleRemoter.git
synced 2026-01-28 18:23:10 +08:00
Feature: Add CListCtrlEx with column show/hide support
- Create CListCtrlEx class derived from CListCtrl
- Support right-click header menu to toggle column visibility
- Save column visibility settings to registry (list\{ConfigKey})
- Settings persist by column name, not index (order-independent)
- Skip empty column titles in context menu
- Update CClientListDlg to use CListCtrlEx
- Update m_CList_Online in 2015RemoteDlg to use CListCtrlEx
This commit is contained in:
@@ -706,11 +706,12 @@ VOID CMy2015RemoteDlg::InitControl()
|
|||||||
rect.bottom+=20;
|
rect.bottom+=20;
|
||||||
MoveWindow(rect);
|
MoveWindow(rect);
|
||||||
auto style = LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP;
|
auto style = LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP;
|
||||||
|
m_CList_Online.SetConfigKey(_T("OnlineList"));
|
||||||
for (int i = 0; i<g_Column_Count_Online; ++i) {
|
for (int i = 0; i<g_Column_Count_Online; ++i) {
|
||||||
m_CList_Online.InsertColumn(i, g_Column_Data_Online[i].szTitle,LVCFMT_CENTER,g_Column_Data_Online[i].nWidth);
|
m_CList_Online.AddColumn(i, g_Column_Data_Online[i].szTitle, g_Column_Data_Online[i].nWidth, LVCFMT_CENTER);
|
||||||
|
|
||||||
g_Column_Online_Width+=g_Column_Data_Online[i].nWidth;
|
g_Column_Online_Width+=g_Column_Data_Online[i].nWidth;
|
||||||
}
|
}
|
||||||
|
m_CList_Online.InitColumns();
|
||||||
m_CList_Online.ModifyStyle(0, LVS_SHOWSELALWAYS);
|
m_CList_Online.ModifyStyle(0, LVS_SHOWSELALWAYS);
|
||||||
m_CList_Online.SetExtendedStyle(style);
|
m_CList_Online.SetExtendedStyle(style);
|
||||||
m_CList_Online.SetParent(&m_GroupTab);
|
m_CList_Online.SetParent(&m_GroupTab);
|
||||||
@@ -1469,15 +1470,7 @@ void CMy2015RemoteDlg::OnSize(UINT nType, int cx, int cy)
|
|||||||
m_GroupTab.AdjustRect(FALSE, &rcInside);
|
m_GroupTab.AdjustRect(FALSE, &rcInside);
|
||||||
rcInside.bottom -= 1;
|
rcInside.bottom -= 1;
|
||||||
m_CList_Online.MoveWindow(&rcInside);
|
m_CList_Online.MoveWindow(&rcInside);
|
||||||
|
m_CList_Online.AdjustColumnWidths();
|
||||||
auto total = rcInside.Width() - 24;
|
|
||||||
for(int i=0; i<g_Column_Count_Online; ++i) { //遍历每一个列
|
|
||||||
double Temp=g_Column_Data_Online[i].nWidth; //得到当前列的宽度 138
|
|
||||||
Temp/=g_Column_Online_Width; //看一看当前宽度占总长度的几分之几
|
|
||||||
Temp*=total; //用原来的长度乘以所占的几分之几得到当前的宽度
|
|
||||||
int lenth = Temp; //转换为int 类型
|
|
||||||
m_CList_Online.SetColumnWidth(i,(lenth)); //设置当前的宽度
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LeaveCriticalSection(&m_cs);
|
LeaveCriticalSection(&m_cs);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
#include "IOCPServer.h"
|
#include "IOCPServer.h"
|
||||||
#include <common/location.h>
|
#include <common/location.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include"file_server.h"
|
#include "file_server.h"
|
||||||
|
#include "CListCtrlEx.h"
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// 以下为特殊需求使用
|
// 以下为特殊需求使用
|
||||||
@@ -151,7 +152,7 @@ public:
|
|||||||
VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength);
|
VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength);
|
||||||
// 显示用户上线信息
|
// 显示用户上线信息
|
||||||
CWnd* m_pFloatingTip = nullptr;
|
CWnd* m_pFloatingTip = nullptr;
|
||||||
CListCtrl m_CList_Online;
|
CListCtrlEx m_CList_Online;
|
||||||
CListCtrl m_CList_Message;
|
CListCtrl m_CList_Message;
|
||||||
std::set<context*> m_HostList;
|
std::set<context*> m_HostList;
|
||||||
std::set<std::string> m_GroupList;
|
std::set<std::string> m_GroupList;
|
||||||
|
|||||||
@@ -287,6 +287,7 @@
|
|||||||
<ClInclude Include="CDrawingBoard.h" />
|
<ClInclude Include="CDrawingBoard.h" />
|
||||||
<ClInclude Include="CGridDialog.h" />
|
<ClInclude Include="CGridDialog.h" />
|
||||||
<ClInclude Include="Chat.h" />
|
<ClInclude Include="Chat.h" />
|
||||||
|
<ClInclude Include="CListCtrlEx.h" />
|
||||||
<ClInclude Include="context.h" />
|
<ClInclude Include="context.h" />
|
||||||
<ClInclude Include="CPasswordDlg.h" />
|
<ClInclude Include="CPasswordDlg.h" />
|
||||||
<ClInclude Include="CRcEditDlg.h" />
|
<ClInclude Include="CRcEditDlg.h" />
|
||||||
@@ -382,6 +383,7 @@
|
|||||||
<ClCompile Include="CDrawingBoard.cpp" />
|
<ClCompile Include="CDrawingBoard.cpp" />
|
||||||
<ClCompile Include="CGridDialog.cpp" />
|
<ClCompile Include="CGridDialog.cpp" />
|
||||||
<ClCompile Include="Chat.cpp" />
|
<ClCompile Include="Chat.cpp" />
|
||||||
|
<ClCompile Include="CListCtrlEx.cpp" />
|
||||||
<ClCompile Include="CPasswordDlg.cpp" />
|
<ClCompile Include="CPasswordDlg.cpp" />
|
||||||
<ClCompile Include="CRcEditDlg.cpp" />
|
<ClCompile Include="CRcEditDlg.cpp" />
|
||||||
<ClCompile Include="CTextDlg.cpp" />
|
<ClCompile Include="CTextDlg.cpp" />
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
<ClCompile Include="..\..\client\reg_startup.c" />
|
<ClCompile Include="..\..\client\reg_startup.c" />
|
||||||
<ClCompile Include="CClientListDlg.cpp" />
|
<ClCompile Include="CClientListDlg.cpp" />
|
||||||
<ClCompile Include="CUpdateDlg.cpp" />
|
<ClCompile Include="CUpdateDlg.cpp" />
|
||||||
|
<ClCompile Include="CListCtrlEx.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\client\Audio.h" />
|
<ClInclude Include="..\..\client\Audio.h" />
|
||||||
@@ -146,6 +147,7 @@
|
|||||||
<ClInclude Include="CClientListDlg.h" />
|
<ClInclude Include="CClientListDlg.h" />
|
||||||
<ClInclude Include="context.h" />
|
<ClInclude Include="context.h" />
|
||||||
<ClInclude Include="CUpdateDlg.h" />
|
<ClInclude Include="CUpdateDlg.h" />
|
||||||
|
<ClInclude Include="CListCtrlEx.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="2015Remote.rc" />
|
<ResourceCompile Include="2015Remote.rc" />
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ void CClientListDlg::DoDataExchange(CDataExchange* pDX)
|
|||||||
|
|
||||||
BEGIN_MESSAGE_MAP(CClientListDlg, CDialogEx)
|
BEGIN_MESSAGE_MAP(CClientListDlg, CDialogEx)
|
||||||
ON_WM_SIZE()
|
ON_WM_SIZE()
|
||||||
ON_WM_CONTEXTMENU()
|
|
||||||
ON_NOTIFY(LVN_COLUMNCLICK, IDC_CLIENT_LIST, &CClientListDlg::OnColumnClick)
|
ON_NOTIFY(LVN_COLUMNCLICK, IDC_CLIENT_LIST, &CClientListDlg::OnColumnClick)
|
||||||
ON_NOTIFY(NM_CLICK, IDC_CLIENT_LIST, &CClientListDlg::OnListClick)
|
ON_NOTIFY(NM_CLICK, IDC_CLIENT_LIST, &CClientListDlg::OnListClick)
|
||||||
END_MESSAGE_MAP()
|
END_MESSAGE_MAP()
|
||||||
@@ -172,22 +171,19 @@ BOOL CClientListDlg::OnInitDialog()
|
|||||||
m_ToolTip.SetDelayTime(TTDT_AUTOPOP, 10000);
|
m_ToolTip.SetDelayTime(TTDT_AUTOPOP, 10000);
|
||||||
m_ToolTip.Activate(TRUE);
|
m_ToolTip.Activate(TRUE);
|
||||||
|
|
||||||
// 初始化列可见性(从配置加载)
|
// 设置配置键名
|
||||||
m_ColumnVisible.resize(g_nColumnCount, TRUE);
|
m_ClientList.SetConfigKey(_T("ClientList"));
|
||||||
LoadColumnVisibility();
|
|
||||||
|
|
||||||
// 添加列
|
// 添加列(第一列序号不允许隐藏)
|
||||||
int totalWidth = 0;
|
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
for (int i = 0; i < g_nColumnCount; i++) {
|
||||||
totalWidth += g_ColumnInfos[i].Width;
|
BOOL bCanHide = (i != COL_NO); // 序号列不允许隐藏
|
||||||
}
|
m_ClientList.AddColumn(i, g_ColumnInfos[i].Name, g_ColumnInfos[i].Width, LVCFMT_LEFT, bCanHide);
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
|
||||||
g_ColumnInfos[i].Percent = (float)g_ColumnInfos[i].Width / totalWidth;
|
|
||||||
m_ClientList.InsertColumn(i, g_ColumnInfos[i].Name, LVCFMT_LEFT, g_ColumnInfos[i].Width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化列(计算百分比、加载配置、应用列宽)
|
||||||
|
m_ClientList.InitColumns();
|
||||||
|
|
||||||
// 首次加载数据
|
// 首次加载数据
|
||||||
AdjustColumnWidths();
|
|
||||||
RefreshClientList();
|
RefreshClientList();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -394,27 +390,7 @@ void CClientListDlg::SortByColumn(int /*nColumn*/, BOOL /*bAscending*/)
|
|||||||
|
|
||||||
void CClientListDlg::AdjustColumnWidths()
|
void CClientListDlg::AdjustColumnWidths()
|
||||||
{
|
{
|
||||||
CRect rect;
|
m_ClientList.AdjustColumnWidths();
|
||||||
m_ClientList.GetClientRect(&rect);
|
|
||||||
int totalWidth = rect.Width() - 20;
|
|
||||||
|
|
||||||
// 计算可见列的总百分比
|
|
||||||
float visiblePercent = 0.0f;
|
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
|
||||||
if (m_ColumnVisible[i]) {
|
|
||||||
visiblePercent += g_ColumnInfos[i].Percent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按比例分配宽度给可见列
|
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
|
||||||
if (m_ColumnVisible[i]) {
|
|
||||||
int width = (visiblePercent > 0) ? (int)(totalWidth * g_ColumnInfos[i].Percent / visiblePercent) : 0;
|
|
||||||
m_ClientList.SetColumnWidth(i, width);
|
|
||||||
} else {
|
|
||||||
m_ClientList.SetColumnWidth(i, 0); // 隐藏列
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClientListDlg::OnSize(UINT nType, int cx, int cy)
|
void CClientListDlg::OnSize(UINT nType, int cx, int cy)
|
||||||
@@ -540,103 +516,3 @@ void CClientListDlg::PostNcDestroy()
|
|||||||
void CClientListDlg::OnOK()
|
void CClientListDlg::OnOK()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClientListDlg::OnContextMenu(CWnd* pWnd, CPoint pt)
|
|
||||||
{
|
|
||||||
// 检查是否点击在表头区域
|
|
||||||
CHeaderCtrl* pHeader = m_ClientList.GetHeaderCtrl();
|
|
||||||
if (pHeader) {
|
|
||||||
CRect headerRect;
|
|
||||||
pHeader->GetWindowRect(&headerRect);
|
|
||||||
if (headerRect.PtInRect(pt)) {
|
|
||||||
ShowColumnContextMenu(pt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 非表头区域,调用默认处理
|
|
||||||
CDialogEx::OnContextMenu(pWnd, pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CClientListDlg::ShowColumnContextMenu(CPoint pt)
|
|
||||||
{
|
|
||||||
CMenu menu;
|
|
||||||
menu.CreatePopupMenu();
|
|
||||||
|
|
||||||
// 添加所有列到菜单
|
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
|
||||||
UINT flags = MF_STRING;
|
|
||||||
if (m_ColumnVisible[i]) {
|
|
||||||
flags |= MF_CHECKED;
|
|
||||||
}
|
|
||||||
// 序号列始终显示,不允许隐藏
|
|
||||||
if (i == COL_NO) {
|
|
||||||
flags |= MF_GRAYED;
|
|
||||||
}
|
|
||||||
menu.AppendMenu(flags, 1000 + i, g_ColumnInfos[i].Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示菜单并获取选择
|
|
||||||
int nCmd = menu.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
|
|
||||||
if (nCmd >= 1000 && nCmd < 1000 + g_nColumnCount) {
|
|
||||||
ToggleColumnVisibility(nCmd - 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CClientListDlg::ToggleColumnVisibility(int nColumn)
|
|
||||||
{
|
|
||||||
// 序号列不允许隐藏
|
|
||||||
if (nColumn == COL_NO) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换可见性
|
|
||||||
m_ColumnVisible[nColumn] = !m_ColumnVisible[nColumn];
|
|
||||||
|
|
||||||
// 保存到配置
|
|
||||||
SaveColumnVisibility();
|
|
||||||
|
|
||||||
// 重新调整列宽
|
|
||||||
AdjustColumnWidths();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CClientListDlg::LoadColumnVisibility()
|
|
||||||
{
|
|
||||||
// 格式:逗号分隔的隐藏列名,如 "备注,程序路径"
|
|
||||||
std::string strHidden = THIS_CFG.GetStr("ClientList", "HiddenColumns", "");
|
|
||||||
if (strHidden.empty()) {
|
|
||||||
return; // 使用默认值(全部显示)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析隐藏的列名
|
|
||||||
std::vector<std::string> hiddenNames = StringToVector(strHidden, ',');
|
|
||||||
for (const auto& name : hiddenNames) {
|
|
||||||
// 查找列名对应的索引
|
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
|
||||||
CString colName = g_ColumnInfos[i].Name;
|
|
||||||
CT2A colNameA(colName);
|
|
||||||
if (name == std::string(colNameA)) {
|
|
||||||
m_ColumnVisible[i] = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 序号列始终显示
|
|
||||||
m_ColumnVisible[COL_NO] = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CClientListDlg::SaveColumnVisibility()
|
|
||||||
{
|
|
||||||
// 只保存隐藏的列名
|
|
||||||
std::string strHidden;
|
|
||||||
for (int i = 0; i < g_nColumnCount; i++) {
|
|
||||||
if (!m_ColumnVisible[i]) {
|
|
||||||
if (!strHidden.empty()) strHidden += ",";
|
|
||||||
CString colName = g_ColumnInfos[i].Name;
|
|
||||||
CT2A colNameA(colName);
|
|
||||||
strHidden += std::string(colNameA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
THIS_CFG.SetStr("ClientList", "HiddenColumns", strHidden);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "2015RemoteDlg.h"
|
#include "2015RemoteDlg.h"
|
||||||
|
#include "CListCtrlEx.h"
|
||||||
|
|
||||||
// 分组键:支持任意字段组合
|
// 分组键:支持任意字段组合
|
||||||
struct GroupKey {
|
struct GroupKey {
|
||||||
@@ -53,19 +54,14 @@ protected:
|
|||||||
CToolTipCtrl m_ToolTip;
|
CToolTipCtrl m_ToolTip;
|
||||||
int m_nTipItem; // 当前提示的行
|
int m_nTipItem; // 当前提示的行
|
||||||
int m_nTipSubItem; // 当前提示的列
|
int m_nTipSubItem; // 当前提示的列
|
||||||
std::vector<BOOL> m_ColumnVisible; // 各列的显示状态
|
|
||||||
|
|
||||||
void BuildGroups(); // 构建分组数据
|
void BuildGroups(); // 构建分组数据
|
||||||
void ShowColumnContextMenu(CPoint pt); // 显示列选择菜单
|
|
||||||
void ToggleColumnVisibility(int nColumn); // 切换列的显示/隐藏
|
|
||||||
void LoadColumnVisibility(); // 从配置加载列可见性
|
|
||||||
void SaveColumnVisibility(); // 保存列可见性到配置
|
|
||||||
|
|
||||||
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
|
||||||
|
|
||||||
DECLARE_MESSAGE_MAP()
|
DECLARE_MESSAGE_MAP()
|
||||||
public:
|
public:
|
||||||
CListCtrl m_ClientList;
|
CListCtrlEx m_ClientList;
|
||||||
virtual BOOL OnInitDialog();
|
virtual BOOL OnInitDialog();
|
||||||
void RefreshClientList();
|
void RefreshClientList();
|
||||||
void DisplayClients();
|
void DisplayClients();
|
||||||
@@ -74,7 +70,6 @@ public:
|
|||||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||||
afx_msg void OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult);
|
afx_msg void OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult);
|
||||||
afx_msg void OnListClick(NMHDR* pNMHDR, LRESULT* pResult);
|
afx_msg void OnListClick(NMHDR* pNMHDR, LRESULT* pResult);
|
||||||
afx_msg void OnContextMenu(CWnd* pWnd, CPoint pt);
|
|
||||||
virtual BOOL PreTranslateMessage(MSG* pMsg);
|
virtual BOOL PreTranslateMessage(MSG* pMsg);
|
||||||
virtual void OnCancel();
|
virtual void OnCancel();
|
||||||
virtual void PostNcDestroy();
|
virtual void PostNcDestroy();
|
||||||
|
|||||||
270
server/2015Remote/CListCtrlEx.cpp
Normal file
270
server/2015Remote/CListCtrlEx.cpp
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "CListCtrlEx.h"
|
||||||
|
#include "2015Remote.h"
|
||||||
|
|
||||||
|
// CHeaderCtrlEx 实现
|
||||||
|
BEGIN_MESSAGE_MAP(CHeaderCtrlEx, CHeaderCtrl)
|
||||||
|
ON_NOTIFY_REFLECT(HDN_BEGINTRACKA, &CHeaderCtrlEx::OnBeginTrack)
|
||||||
|
ON_NOTIFY_REFLECT(HDN_BEGINTRACKW, &CHeaderCtrlEx::OnBeginTrack)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
void CHeaderCtrlEx::OnBeginTrack(NMHDR* pNMHDR, LRESULT* pResult)
|
||||||
|
{
|
||||||
|
LPNMHEADER pNMHeader = reinterpret_cast<LPNMHEADER>(pNMHDR);
|
||||||
|
int nCol = pNMHeader->iItem;
|
||||||
|
|
||||||
|
if (m_pListCtrl && nCol >= 0 && nCol < (int)m_pListCtrl->m_Columns.size()) {
|
||||||
|
if (!m_pListCtrl->m_Columns[nCol].Visible) {
|
||||||
|
*pResult = TRUE; // 阻止拖拽
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*pResult = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CListCtrlEx 实现
|
||||||
|
IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)
|
||||||
|
|
||||||
|
CListCtrlEx::CListCtrlEx()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CListCtrlEx::~CListCtrlEx()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
|
||||||
|
ON_WM_CONTEXTMENU()
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
void CListCtrlEx::SetConfigKey(const CString& strKey)
|
||||||
|
{
|
||||||
|
m_strConfigKey = strKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CListCtrlEx::AddColumn(int nCol, LPCTSTR lpszColumnHeading, int nWidth, int nFormat, BOOL bCanHide)
|
||||||
|
{
|
||||||
|
// 添加到列表控件
|
||||||
|
int nResult = InsertColumn(nCol, lpszColumnHeading, nFormat, nWidth);
|
||||||
|
|
||||||
|
if (nResult != -1) {
|
||||||
|
// 保存列信息
|
||||||
|
ColumnInfoEx info;
|
||||||
|
info.Name = lpszColumnHeading;
|
||||||
|
info.Width = nWidth;
|
||||||
|
info.Percent = 0.0f; // 稍后在 InitColumns 中计算
|
||||||
|
info.Visible = TRUE;
|
||||||
|
info.CanHide = bCanHide;
|
||||||
|
|
||||||
|
// 确保 vector 大小足够
|
||||||
|
if (nCol >= (int)m_Columns.size()) {
|
||||||
|
m_Columns.resize(nCol + 1);
|
||||||
|
}
|
||||||
|
m_Columns[nCol] = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::InitColumns()
|
||||||
|
{
|
||||||
|
if (m_Columns.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子类化 Header 控件
|
||||||
|
SubclassHeader();
|
||||||
|
|
||||||
|
// 计算总宽度和百分比
|
||||||
|
int totalWidth = 0;
|
||||||
|
for (const auto& col : m_Columns) {
|
||||||
|
totalWidth += col.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalWidth > 0) {
|
||||||
|
for (auto& col : m_Columns) {
|
||||||
|
col.Percent = (float)col.Width / totalWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从配置加载列可见性
|
||||||
|
LoadColumnVisibility();
|
||||||
|
|
||||||
|
// 应用列宽
|
||||||
|
AdjustColumnWidths();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::SubclassHeader()
|
||||||
|
{
|
||||||
|
CHeaderCtrl* pHeader = GetHeaderCtrl();
|
||||||
|
if (pHeader && !m_HeaderCtrl.GetSafeHwnd()) {
|
||||||
|
m_HeaderCtrl.SubclassWindow(pHeader->GetSafeHwnd());
|
||||||
|
m_HeaderCtrl.m_pListCtrl = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::AdjustColumnWidths()
|
||||||
|
{
|
||||||
|
if (m_Columns.empty() || GetSafeHwnd() == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRect rect;
|
||||||
|
GetClientRect(&rect);
|
||||||
|
int totalWidth = rect.Width() - 20; // 减去滚动条宽度
|
||||||
|
|
||||||
|
// 计算可见列的总百分比
|
||||||
|
float visiblePercent = 0.0f;
|
||||||
|
for (const auto& col : m_Columns) {
|
||||||
|
if (col.Visible) {
|
||||||
|
visiblePercent += col.Percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按比例分配宽度给可见列
|
||||||
|
for (int i = 0; i < (int)m_Columns.size(); i++) {
|
||||||
|
if (m_Columns[i].Visible) {
|
||||||
|
int width = (visiblePercent > 0) ? (int)(totalWidth * m_Columns[i].Percent / visiblePercent) : 0;
|
||||||
|
SetColumnWidth(i, width);
|
||||||
|
} else {
|
||||||
|
SetColumnWidth(i, 0); // 隐藏列
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CListCtrlEx::IsColumnVisible(int nCol) const
|
||||||
|
{
|
||||||
|
if (nCol >= 0 && nCol < (int)m_Columns.size()) {
|
||||||
|
return m_Columns[nCol].Visible;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::SetColumnVisible(int nCol, BOOL bVisible)
|
||||||
|
{
|
||||||
|
if (nCol >= 0 && nCol < (int)m_Columns.size()) {
|
||||||
|
if (m_Columns[nCol].CanHide || bVisible) { // 不允许隐藏的列只能设为可见
|
||||||
|
m_Columns[nCol].Visible = bVisible;
|
||||||
|
AdjustColumnWidths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::OnContextMenu(CWnd* pWnd, CPoint pt)
|
||||||
|
{
|
||||||
|
// 检查是否点击在表头区域
|
||||||
|
CHeaderCtrl* pHeader = GetHeaderCtrl();
|
||||||
|
if (pHeader) {
|
||||||
|
CRect headerRect;
|
||||||
|
pHeader->GetWindowRect(&headerRect);
|
||||||
|
if (headerRect.PtInRect(pt)) {
|
||||||
|
ShowColumnContextMenu(pt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非表头区域,调用父类处理
|
||||||
|
CListCtrl::OnContextMenu(pWnd, pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::ShowColumnContextMenu(CPoint pt)
|
||||||
|
{
|
||||||
|
CMenu menu;
|
||||||
|
menu.CreatePopupMenu();
|
||||||
|
|
||||||
|
// 添加所有列到菜单(跳过空标题的列)
|
||||||
|
for (int i = 0; i < (int)m_Columns.size(); i++) {
|
||||||
|
// 跳过空标题的列
|
||||||
|
if (m_Columns[i].Name.IsEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT flags = MF_STRING;
|
||||||
|
if (m_Columns[i].Visible) {
|
||||||
|
flags |= MF_CHECKED;
|
||||||
|
}
|
||||||
|
// 不允许隐藏的列显示为灰色
|
||||||
|
if (!m_Columns[i].CanHide) {
|
||||||
|
flags |= MF_GRAYED;
|
||||||
|
}
|
||||||
|
menu.AppendMenu(flags, 1000 + i, m_Columns[i].Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示菜单并获取选择
|
||||||
|
int nCmd = menu.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
|
||||||
|
if (nCmd >= 1000 && nCmd < 1000 + (int)m_Columns.size()) {
|
||||||
|
ToggleColumnVisibility(nCmd - 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::ToggleColumnVisibility(int nColumn)
|
||||||
|
{
|
||||||
|
if (nColumn < 0 || nColumn >= (int)m_Columns.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不允许隐藏的列不处理
|
||||||
|
if (!m_Columns[nColumn].CanHide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换可见性
|
||||||
|
m_Columns[nColumn].Visible = !m_Columns[nColumn].Visible;
|
||||||
|
|
||||||
|
// 保存到配置
|
||||||
|
SaveColumnVisibility();
|
||||||
|
|
||||||
|
// 重新调整列宽
|
||||||
|
AdjustColumnWidths();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::LoadColumnVisibility()
|
||||||
|
{
|
||||||
|
if (m_strConfigKey.IsEmpty()) {
|
||||||
|
return; // 没有设置配置键,不加载
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置结构:list\{ConfigKey},如 list\ClientList
|
||||||
|
// 格式:逗号分隔的隐藏列名,如 "备注,程序路径"
|
||||||
|
CT2A configKeyA(m_strConfigKey);
|
||||||
|
std::string strHidden = THIS_CFG.GetStr("list", std::string(configKeyA), "");
|
||||||
|
if (strHidden.empty()) {
|
||||||
|
return; // 使用默认值(全部显示)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析隐藏的列名
|
||||||
|
std::vector<std::string> hiddenNames = StringToVector(strHidden, ',');
|
||||||
|
for (const auto& name : hiddenNames) {
|
||||||
|
// 查找列名对应的索引
|
||||||
|
for (int i = 0; i < (int)m_Columns.size(); i++) {
|
||||||
|
CT2A colNameA(m_Columns[i].Name);
|
||||||
|
if (name == std::string(colNameA)) {
|
||||||
|
if (m_Columns[i].CanHide) {
|
||||||
|
m_Columns[i].Visible = FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CListCtrlEx::SaveColumnVisibility()
|
||||||
|
{
|
||||||
|
if (m_strConfigKey.IsEmpty()) {
|
||||||
|
return; // 没有设置配置键,不保存
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只保存隐藏的列名
|
||||||
|
std::string strHidden;
|
||||||
|
for (int i = 0; i < (int)m_Columns.size(); i++) {
|
||||||
|
if (!m_Columns[i].Visible) {
|
||||||
|
if (!strHidden.empty()) strHidden += ",";
|
||||||
|
CT2A colNameA(m_Columns[i].Name);
|
||||||
|
strHidden += std::string(colNameA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置结构:list\{ConfigKey},如 list\ClientList
|
||||||
|
CT2A configKeyA(m_strConfigKey);
|
||||||
|
THIS_CFG.SetStr("list", std::string(configKeyA), strHidden);
|
||||||
|
}
|
||||||
74
server/2015Remote/CListCtrlEx.h
Normal file
74
server/2015Remote/CListCtrlEx.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// 列信息结构
|
||||||
|
struct ColumnInfoEx {
|
||||||
|
CString Name; // 列名
|
||||||
|
int Width; // 初始宽度
|
||||||
|
float Percent; // 占比
|
||||||
|
BOOL Visible; // 是否可见
|
||||||
|
BOOL CanHide; // 是否允许隐藏(如序号列不允许)
|
||||||
|
};
|
||||||
|
|
||||||
|
class CListCtrlEx;
|
||||||
|
|
||||||
|
// 自定义 Header 控件,用于阻止隐藏列被拖拽
|
||||||
|
class CHeaderCtrlEx : public CHeaderCtrl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CListCtrlEx* m_pListCtrl = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
afx_msg void OnBeginTrack(NMHDR* pNMHDR, LRESULT* pResult);
|
||||||
|
};
|
||||||
|
|
||||||
|
// CListCtrlEx - 支持列显示/隐藏功能的列表控件
|
||||||
|
class CListCtrlEx : public CListCtrl
|
||||||
|
{
|
||||||
|
DECLARE_DYNAMIC(CListCtrlEx)
|
||||||
|
friend class CHeaderCtrlEx; // 允许 CHeaderCtrlEx 访问 protected 成员
|
||||||
|
|
||||||
|
public:
|
||||||
|
CListCtrlEx();
|
||||||
|
virtual ~CListCtrlEx();
|
||||||
|
|
||||||
|
// 设置配置键名(用于区分不同列表的配置,如 "ClientList", "FileList")
|
||||||
|
void SetConfigKey(const CString& strKey);
|
||||||
|
|
||||||
|
// 添加列(替代 InsertColumn)
|
||||||
|
// nCol: 列索引
|
||||||
|
// lpszColumnHeading: 列标题
|
||||||
|
// nFormat: 对齐方式,默认左对齐
|
||||||
|
// nWidth: 列宽
|
||||||
|
// bCanHide: 是否允许隐藏,默认允许
|
||||||
|
int AddColumn(int nCol, LPCTSTR lpszColumnHeading, int nWidth, int nFormat = LVCFMT_LEFT, BOOL bCanHide = TRUE);
|
||||||
|
|
||||||
|
// 初始化完成后调用,计算百分比并加载配置
|
||||||
|
void InitColumns();
|
||||||
|
|
||||||
|
// 调整列宽(窗口大小改变时调用)
|
||||||
|
void AdjustColumnWidths();
|
||||||
|
|
||||||
|
// 获取列是否可见
|
||||||
|
BOOL IsColumnVisible(int nCol) const;
|
||||||
|
|
||||||
|
// 设置列可见性
|
||||||
|
void SetColumnVisible(int nCol, BOOL bVisible);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<ColumnInfoEx> m_Columns; // 列信息
|
||||||
|
CString m_strConfigKey; // 配置键名
|
||||||
|
CHeaderCtrlEx m_HeaderCtrl; // 子类化的 Header 控件
|
||||||
|
|
||||||
|
void ShowColumnContextMenu(CPoint pt);
|
||||||
|
void ToggleColumnVisibility(int nColumn);
|
||||||
|
void LoadColumnVisibility();
|
||||||
|
void SaveColumnVisibility();
|
||||||
|
void SubclassHeader(); // 子类化 Header 控件
|
||||||
|
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
afx_msg void OnContextMenu(CWnd* pWnd, CPoint pt);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user