mirror of
https://github.com/yuanyuanxiang/SimpleRemoter.git
synced 2026-01-28 10:13:09 +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;
|
||||
MoveWindow(rect);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
m_CList_Online.InitColumns();
|
||||
m_CList_Online.ModifyStyle(0, LVS_SHOWSELALWAYS);
|
||||
m_CList_Online.SetExtendedStyle(style);
|
||||
m_CList_Online.SetParent(&m_GroupTab);
|
||||
@@ -1469,15 +1470,7 @@ void CMy2015RemoteDlg::OnSize(UINT nType, int cx, int cy)
|
||||
m_GroupTab.AdjustRect(FALSE, &rcInside);
|
||||
rcInside.bottom -= 1;
|
||||
m_CList_Online.MoveWindow(&rcInside);
|
||||
|
||||
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)); //设置当前的宽度
|
||||
}
|
||||
m_CList_Online.AdjustColumnWidths();
|
||||
}
|
||||
LeaveCriticalSection(&m_cs);
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#include "IOCPServer.h"
|
||||
#include <common/location.h>
|
||||
#include <map>
|
||||
#include"file_server.h"
|
||||
#include "file_server.h"
|
||||
#include "CListCtrlEx.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// 以下为特殊需求使用
|
||||
@@ -151,8 +152,8 @@ public:
|
||||
VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength);
|
||||
// 显示用户上线信息
|
||||
CWnd* m_pFloatingTip = nullptr;
|
||||
CListCtrl m_CList_Online;
|
||||
CListCtrl m_CList_Message;
|
||||
CListCtrlEx m_CList_Online;
|
||||
CListCtrl m_CList_Message;
|
||||
std::set<context*> m_HostList;
|
||||
std::set<std::string> m_GroupList;
|
||||
std::string m_selectedGroup;
|
||||
|
||||
@@ -287,6 +287,7 @@
|
||||
<ClInclude Include="CDrawingBoard.h" />
|
||||
<ClInclude Include="CGridDialog.h" />
|
||||
<ClInclude Include="Chat.h" />
|
||||
<ClInclude Include="CListCtrlEx.h" />
|
||||
<ClInclude Include="context.h" />
|
||||
<ClInclude Include="CPasswordDlg.h" />
|
||||
<ClInclude Include="CRcEditDlg.h" />
|
||||
@@ -382,6 +383,7 @@
|
||||
<ClCompile Include="CDrawingBoard.cpp" />
|
||||
<ClCompile Include="CGridDialog.cpp" />
|
||||
<ClCompile Include="Chat.cpp" />
|
||||
<ClCompile Include="CListCtrlEx.cpp" />
|
||||
<ClCompile Include="CPasswordDlg.cpp" />
|
||||
<ClCompile Include="CRcEditDlg.cpp" />
|
||||
<ClCompile Include="CTextDlg.cpp" />
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<ClCompile Include="..\..\client\reg_startup.c" />
|
||||
<ClCompile Include="CClientListDlg.cpp" />
|
||||
<ClCompile Include="CUpdateDlg.cpp" />
|
||||
<ClCompile Include="CListCtrlEx.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\client\Audio.h" />
|
||||
@@ -146,6 +147,7 @@
|
||||
<ClInclude Include="CClientListDlg.h" />
|
||||
<ClInclude Include="context.h" />
|
||||
<ClInclude Include="CUpdateDlg.h" />
|
||||
<ClInclude Include="CListCtrlEx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="2015Remote.rc" />
|
||||
|
||||
@@ -144,7 +144,6 @@ void CClientListDlg::DoDataExchange(CDataExchange* pDX)
|
||||
|
||||
BEGIN_MESSAGE_MAP(CClientListDlg, CDialogEx)
|
||||
ON_WM_SIZE()
|
||||
ON_WM_CONTEXTMENU()
|
||||
ON_NOTIFY(LVN_COLUMNCLICK, IDC_CLIENT_LIST, &CClientListDlg::OnColumnClick)
|
||||
ON_NOTIFY(NM_CLICK, IDC_CLIENT_LIST, &CClientListDlg::OnListClick)
|
||||
END_MESSAGE_MAP()
|
||||
@@ -172,22 +171,19 @@ BOOL CClientListDlg::OnInitDialog()
|
||||
m_ToolTip.SetDelayTime(TTDT_AUTOPOP, 10000);
|
||||
m_ToolTip.Activate(TRUE);
|
||||
|
||||
// 初始化列可见性(从配置加载)
|
||||
m_ColumnVisible.resize(g_nColumnCount, TRUE);
|
||||
LoadColumnVisibility();
|
||||
// 设置配置键名
|
||||
m_ClientList.SetConfigKey(_T("ClientList"));
|
||||
|
||||
// 添加列
|
||||
int totalWidth = 0;
|
||||
// 添加列(第一列序号不允许隐藏)
|
||||
for (int i = 0; i < g_nColumnCount; i++) {
|
||||
totalWidth += g_ColumnInfos[i].Width;
|
||||
}
|
||||
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);
|
||||
BOOL bCanHide = (i != COL_NO); // 序号列不允许隐藏
|
||||
m_ClientList.AddColumn(i, g_ColumnInfos[i].Name, g_ColumnInfos[i].Width, LVCFMT_LEFT, bCanHide);
|
||||
}
|
||||
|
||||
// 初始化列(计算百分比、加载配置、应用列宽)
|
||||
m_ClientList.InitColumns();
|
||||
|
||||
// 首次加载数据
|
||||
AdjustColumnWidths();
|
||||
RefreshClientList();
|
||||
|
||||
return TRUE;
|
||||
@@ -394,27 +390,7 @@ void CClientListDlg::SortByColumn(int /*nColumn*/, BOOL /*bAscending*/)
|
||||
|
||||
void CClientListDlg::AdjustColumnWidths()
|
||||
{
|
||||
CRect rect;
|
||||
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); // 隐藏列
|
||||
}
|
||||
}
|
||||
m_ClientList.AdjustColumnWidths();
|
||||
}
|
||||
|
||||
void CClientListDlg::OnSize(UINT nType, int cx, int cy)
|
||||
@@ -540,103 +516,3 @@ void CClientListDlg::PostNcDestroy()
|
||||
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 <map>
|
||||
#include "2015RemoteDlg.h"
|
||||
#include "CListCtrlEx.h"
|
||||
|
||||
// 分组键:支持任意字段组合
|
||||
struct GroupKey {
|
||||
@@ -53,19 +54,14 @@ protected:
|
||||
CToolTipCtrl m_ToolTip;
|
||||
int m_nTipItem; // 当前提示的行
|
||||
int m_nTipSubItem; // 当前提示的列
|
||||
std::vector<BOOL> m_ColumnVisible; // 各列的显示状态
|
||||
|
||||
void BuildGroups(); // 构建分组数据
|
||||
void ShowColumnContextMenu(CPoint pt); // 显示列选择菜单
|
||||
void ToggleColumnVisibility(int nColumn); // 切换列的显示/隐藏
|
||||
void LoadColumnVisibility(); // 从配置加载列可见性
|
||||
void SaveColumnVisibility(); // 保存列可见性到配置
|
||||
|
||||
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
public:
|
||||
CListCtrl m_ClientList;
|
||||
CListCtrlEx m_ClientList;
|
||||
virtual BOOL OnInitDialog();
|
||||
void RefreshClientList();
|
||||
void DisplayClients();
|
||||
@@ -74,7 +70,6 @@ public:
|
||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||
afx_msg void OnColumnClick(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 void OnCancel();
|
||||
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