如何使用 Win32/WTL 实现列表视图的缩放

How to implement zooming of a listview using Win32/WTL

本文关键字:列表 视图 缩放 实现 WTL 何使用 Win32      更新时间:2023-10-16

我不久前在WTL/C++中实现了一个轻量级日志查看器。最近决定扩展具有放大/缩小功能的 UI。我添加了来自CZoomScrollImpl的派生,如下所示:

class CLogView
    : public CWindowImpl<CLogView, CListViewCtrl>
    , public CZoomScrollImpl<CLogView>
    , public CCustomDraw<CLogView>
{
    CLogReader m_logReader;
public:
    DECLARE_WND_SUPERCLASS(_T("ZoomWTL"), CListViewCtrl::GetWndClassName())
    CLogView(){}
    BOOL PreTranslateMessage(MSG* /*pMsg*/){return FALSE;}
    BEGIN_MSG_MAP(CLogView)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
        CHAIN_MSG_MAP_ALT(CCustomDraw<CLogView>, 1)
        CHAIN_MSG_MAP(CZoomScrollImpl<CLogView>);
    END_MSG_MAP()
    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
    {        
        return CDRF_NOTIFYITEMDRAW;
    }
    void DoPaint(CDCHandle /*dc*/)
    {
/// TODO: Implement!
    }
    DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw);
    LRESULT OnGetdispinfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/);
};

然后问题来了:如何呈现列表视图?以前它是自定义绘制的,即我只为项目指定文本和颜色。从 DoPaint 调用默认渲染的正确方法是什么?还是我应该编写所有渲染代码?

这是 CPP 文件的片段:

// Columns definitions
LVCOLUMN SQLLOG_COLUMNS[] = {
    { LVCF_FMT|LVCF_WIDTH|LVCF_TEXT, LVCFMT_LEFT, 100, L"Time", -1, 0,0,0 },
    { LVCF_FMT|LVCF_WIDTH|LVCF_TEXT, LVCFMT_LEFT, 140, L"Type", -1, 1,0,0 },
    { LVCF_FMT|LVCF_WIDTH|LVCF_TEXT, LVCFMT_LEFT, 500, L"Message", -1, 2,0,0 },
    { LVCF_FMT|LVCF_WIDTH|LVCF_TEXT, LVCFMT_LEFT, 150, L"Application", -1, 3,0,0 },
};
const int SQLLOG_COLUMNS_CNT = _countof(SQLLOG_COLUMNS);
LRESULT CLogView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
    LRESULT lResult = DefWindowProc(uMsg, wParam, lParam);
    ModifyStyle(0, LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_ALIGNTOP | LVS_OWNERDATA);
    SetExtendedListViewStyle(LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT /*| LVS_EX_FLATSB | LVS_EX_TRACKSELECT | LVS_EX_GRIDLINES*/);
    while (DeleteColumn(0));
    int iCol = 0;
    for (int i = 0; i < SQLLOG_COLUMNS_CNT; i++)
    {
        LVCOLUMN lvc(SQLLOG_COLUMNS[i]);
        InsertColumn(iCol++, (LVCOLUMN*)&lvc);
    }
    //?not needed: SetScrollSize(1000, 10000, FALSE, false);
    return lResult;
}
LRESULT CLogView::OnGetdispinfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
    LV_ITEM* pItem = &((LV_DISPINFO*)pnmh)->item;
    if (pItem->mask & LVIF_TEXT)
    {
        const LOG_MESSAGE* msg = m_logReader.GetMessageData(pItem->iItem);
        switch (pItem->iSubItem)
        {
        case 0:
            switch (msg->msgType)
            {
            default:
            case 0:
                wcscpy(pItem->pszText, L"Normal");
                break;
            case 1:
                wcscpy(pItem->pszText, L"Error");
                break;
            case 2:
                wcscpy(pItem->pszText, L"Notification");
                break;
            case 3:
                wcscpy(pItem->pszText, L"Alert");
                break;
            case 4:
                wcscpy(pItem->pszText, L"Comment");
                break;
            }
            break;
        case 1:
        {
            SYSTEMTIME sysTime, locTime;
            FileTimeToSystemTime((FILETIME*)&msg->timestamp, &sysTime);
            SystemTimeToTzSpecificLocalTime(NULL, &sysTime, &locTime);
            swprintf(pItem->pszText, _CVTBUFSIZE, L"%04d/%02d/%02d %02d:%02d:%02d.%03d", locTime.wYear, locTime.wMonth, locTime.wDay, locTime.wHour, locTime.wMinute, locTime.wSecond, locTime.wMilliseconds);
            break;
        }
        case 2:
            wcscpy(pItem->pszText, msg->message);
            break;
        case 3:
            wcscpy(pItem->pszText, msg->source);
            break;
        }
    }
    return 0;
}
DWORD CLogView::OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
{
    NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(lpNMCustomDraw);
    const LOG_MESSAGE* msg = m_logReader.GetMessageData((size_t)pLVCD->nmcd.dwItemSpec);
    switch (msg->msgType)
    {
    default:
    case 0: pLVCD->clrTextBk = 0xFFFFFF/*White*/; break;
    case 1: pLVCD->clrTextBk = 0x0045FF/*OrangeRed*/; break;
    case 2: pLVCD->clrTextBk = 0xFFFF00/*Cyan*/; break;
    case 3: pLVCD->clrTextBk = 0x00FFFF/*Yellow*/; break;
    case 4: pLVCD->clrTextBk = 0xD3D3D3/*LightGray*/; break;
    }
    // Tell Windows to paint the control itself.
    return CDRF_DODEFAULT;
}
// Returns TRUE if number of messages changed in the ListView; otherwise - FALSE.
BOOL CLogView::ShowMessages()
{
    BOOL msgCountChanged = FALSE;
    size_t numMessages = m_logReader.GetNumMessages();
    if (m_numMessages == numMessages)
    {   // number of messages haven't changed - nothing to do.
        return FALSE;
    }
    if (numMessages == 0)
    {   // messages were cleared
        m_numMessages = 0;
        DeleteAllItems();
        return TRUE;
    }
    int nItemLast = GetItemCount() + numMessages - m_numMessages;
    SetItemCountEx(nItemLast, LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL);
    m_numMessages = numMessages;
    if (nItemLast > 0)
        EnsureVisible(nItemLast-1, FALSE);
    return TRUE;
}
LRESULT CLogView::OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
    if (MK_CONTROL & wParam)
    {
        int zDelta = (int)GET_WHEEL_DELTA_WPARAM(wParam);
        float scale = this->GetZoomScale();
        if (zDelta < 0)
            scale *= 0.91f;
        else
            scale *= 1.1f;
        this->Zoom(0, 0, scale);
        return 0;
    }
    bHandled = FALSE;
    return 1;
}
答案出

乎意料地来自一位不是程序员的亲戚。她告诉我更改列表视图字体!!简单易行...为什么我从一开始就没有考虑过?