可以保存和恢复列布局的 CListCtrl





5.00/5 (1投票)
关于如何实现列宽和位置持久化的示例。
引言
Microsoft 的CListCtrl支持使用报表样式显示网格中的数据,但要使其记住其列状态(列顺序、宽度等),需要进行某些更改。
此实现支持以下功能:
- 可以备份和恢复列宽和列顺序。
- 可以处理从初始配置中添加/删除列的情况。
- 可以处理多个视图的列配置。
- 可以将列配置重置为初始配置。
- 可以在多个列状态配置文件之间切换。如果您需要额外的特殊列配置(例如,打印时),这很有用。
- 易于更改为另一个持久层。

背景
许多高级网格控件扩展了CListCtrl,使其能够持久保存列状态。但是,由于这些网格控件可能非常复杂,因此很难看出它们是如何实现的。
同时,加载和保存列状态以及选择的持久层(注册表、INI 文件、XML 文件)之间很少有分离。
本文是系列文章的一部分,最后一篇文章CGridListCtrlEx结合了所有文章的细节。
选择持久层
通常有三个不同的层可以选择:
- 注册表数据库 - 支持深层层次结构和二进制数据类型。
- INI 文件 - 支持一级层次结构和字符串数据类型。
- XML 文件 - 支持深层层次结构和字符串数据类型。
Microsoft 通过CWinApp对象及其方法GetProfileString()和WriteProfileString()简化了在注册表数据库或INI文件中存储设置的操作。
许多人都在努力创建保存/存储设置的最佳实现。
使用代码
唯一列标识符
CListCtrl中的每一列都必须具有唯一的标识符,否则列状态的备份和恢复将无法正常工作。此外,在删除过时的列时,其他列的唯一标识符应保持不变。
在调用CListCtrl::InsertColumn()时,我们可以指定列标识符(col_id)。
m_ListCtrl.InsertColumn(col_pos, _T("Column Title"), LVCFMT_LEFT, 100, col_id); 
在添加/删除列时,其他列的列标识符保持不变非常重要。
设置持久层
PersistViewConfigWinApp实现接口PersistViewConfig,并使通过CWinApp在INI文件或注册表中存储设置成为可能。
void CListCtrl_Column_Persist::LoadConfiguration(PersistViewConfigProfiles* pViewConfig)
{
    m_pViewConfig = pViewConfig;
    // Save the current configuration as default configuration
    if (!m_pViewConfig->HasDefaultConfig())
    {
        PullConfiguration(m_pViewConfig->GetDefaultConfig());
    }
    // Load the saved configuration
    PushConfiguration(*m_pViewConfig);
}
保存列状态
使用LVS_EX_HEADERDRAGDROP样式时,必须从CHeaderCtrl检索列顺序。我们根据列顺序(以及列宽)存储唯一的列标识符。
void CListCtrl_Column_Persist::PullConfiguration(PersistViewConfig& config)
{
    config.RemoveCurrentConfig(); // Reset the existing config
    int nColCount = GetHeaderCtrl()->GetItemCount();
    int* pOrderArray = new int[nColCount];
    GetColumnOrderArray(pOrderArray, nColCount);
    int nVisibleCols = 0;
    for(int i = 0 ; i < nColCount; ++i)
    {
        int nCol = pOrderArray[i];
        int nColData = GetColumnData(nCol);
        // In this example columns are visible when their width is larger than zero
        if (IsColumnVisible(nCol))
        {
            CString colSetting;
            colSetting.Format(_T("ColumnData_%d"), nVisibleCols);
            config.SetIntSetting(colSetting, nColData);
            colSetting.Format(_T("ColumnWidth_%d"), nVisibleCols);
            config.SetIntSetting(colSetting, GetColumnWidth(nCol));
            nVisibleCols++;
        }
    }
 
    config.SetIntSetting(_T("ColumnCount"), nVisibleCols);
    delete [] pOrderArray;
}
应用列状态
保存列状态的反向操作,但额外需要注意的是必须处理列不再存在或出现新列的情况。
void CListCtrl_Column_Persist::PushConfiguration(const PersistViewConfig& config)
{
    int nVisibleCols = config.GetIntSetting(_T("ColumnCount"));
    int nColCount = GetHeaderCtrl()->GetItemCount();
    int* pOrderArray = new int[nColCount];
    GetColumnOrderArray(pOrderArray, nColCount);
    SetRedraw(FALSE);
    // All invisible columns must be place in the begining of the order-array
    int nColOrder = nColCount;
    for(int i = nVisibleCols-1; i >= 0; --i)
    {
        CString colSetting;
        colSetting.Format(_T("ColumnData_%d"), i);
        int nColData = config.GetIntSetting(colSetting);
        for(int nCol = 0; nCol < nColCount; ++nCol)
        {
           if (nColData==GetColumnData(nCol))
           {
               // Column still exists
               colSetting.Format(_T("ColumnWidth_%d"), i);
               int width = config.GetIntSetting(colSetting);
               SetColumnWidth(nCol, width);
               pOrderArray[--nColOrder] = nCol;
               break;
           }
        }
    }
    // Did we find any visible columns in the saved configuration ?
    if (nColOrder < nColCount)
    {
        // All remaining columns are added to the beginning with size zero
        for(int nCol = nColCount-1; nCol >= 0; --nCol)
        {
            bool visible = false;
            for(int i = nColOrder; i < nColCount; ++i)
            {
                if (pOrderArray[i]==nCol)
                {
                    visible = true;
                    break;
                }
            }
            if (!visible)
            {
                pOrderArray[--nColOrder] = nCol;
                SetColumnWidth(nCol, 0);
            }
        }
        // Only update the column configuration if there are visible columns
        ASSERT(nColOrder==0); // All entries in the order-array must be set
        SetColumnOrderArray(nColCount, pOrderArray);
    }
    
    delete [] pOrderArray;
    SetRedraw(TRUE);
    Invalidate();
    UpdateWindow();
}
重置列配置
将当前配置重置为初始状态,然后加载当前配置。
void CListCtrl_Column_Persist::ResetConfiguration()
{
    if (m_pViewConfig->HasDefaultConfig())
    {
        m_pViewConfig->ResetConfigDefault();
        PushConfiguration(*m_pViewConfig);
    }
}
更改配置配置文件
在更改为新的配置配置文件之前,我们保存当前列状态。
void CListCtrl_Column_Persist::ChangeProfile(const CString& profile)
{
    PullConfiguration(*m_pViewConfig);
    m_pViewConfig->SetActiveProfile(profile);
    PushConfiguration(*m_pViewConfig);
}
关注点
目前还没有。
历史
- **2008-03-30** - 初始发布。


