场景
- 开发
WTL/Win32
的程序时,经常会用到表格控件CListViewCtrl
。这个控件需要设置列的宽度,当用完100%
的宽度来平均分配给列宽时,一加载数据多,就会出现垂直滚动条后,水平滚动条也会同时出现的问题。怎么设置才能让水平滚动条不出现?
图1
说明
-
WTL
的控件CListViewCtrl
实际上就是Win32
的listview
控件的封装。当行个数超过它的可见区域时,就会默认出现垂直滚动条。这个垂直滚动条也是占用了原本ListView
的客户端区域,导致ListView
的客户端区域宽度减少,也就是现在的客户端宽度 = 原来的客户端区域宽度-滑块的宽度
。 -
当然也可以在行数据超过可见区域时重新计算列宽度,只是这种做法需要在合适的时候进行判断行数据是否够多导致垂直滚动条出现。一般不建议做这个冗余的判断处理。
-
目前推荐的做法是布局
ListView
时,也就是设置它的列宽时,如果没有出现垂直滚动条,那么预留滚动条的宽度不分配给列宽。 以下是获取滑块宽度的做法: -
关于
WTL/Win32
开发请参考我的课程 使用WTL进行Windows桌面应用开发
void CView::layoutListView()
{
CRect rect;
listview_.GetClientRect(&rect);
auto width = rect.Width();
listview_.SetColumnWidth(0, width*0.2);
listview_.SetColumnWidth(1, width*0.3);
listview_.SetColumnWidth(2, width*0.2);
auto last = width * 0.3;
if (buttonIsLayoutWithThumb_.GetCheck()) {
auto thumbWidth = GetWindowVScrollBarThumbWidth(listview_, false);
// 如果默认没有滚动条,减去滑块宽度。
if (!thumbWidth) {
thumbWidth = GetWindowVScrollBarThumbWidth(listview_, true);
last = last - thumbWidth;
}
}
listview_.SetColumnWidth(3, last);
}
例子
View.h
// View.h : interface of the CView class
//
/
#pragma once
#include <utility>
#include <string>
#include <vector>
#include <memory>
#include <atlmisc.h>
#include <atlctrls.h>
#include <atlctrlx.h>
#include <GdiPlus.h>
using namespace std;
enum
{
kMyButtonId = WM_USER+1,
kMyButtonId2,
kMyButtonId3,
kMyButtonId4,
kMyListViewId
};
class Photo
{
public:
wstring name;
wstring createDate;
wstring path;
wstring format;
};
class CView : public CWindowImpl<CView>
{
public:
DECLARE_WND_CLASS(NULL)
BOOL PreTranslateMessage(MSG* pMsg);
BEGIN_MSG_MAP_EX(CView)
MSG_WM_CREATE(OnCreate)
MSG_WM_SIZE(OnSize)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
NOTIFY_HANDLER(kMyListViewId,NM_CLICK,OnNMClickListResult)
NOTIFY_HANDLER(kMyListViewId,LVN_GETDISPINFO,OnGetListViewData)
NOTIFY_HANDLER(kMyListViewId,LVN_ODCACHEHINT,OnPrepareListViewData)
NOTIFY_HANDLER(kMyListViewId,LVN_ODFINDITEM,OnFindListViewData)
COMMAND_RANGE_HANDLER_EX(kMyButtonId,kMyButtonId4,OnCommandIDHandlerEX)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
protected:
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
int OnCreate(LPCREATESTRUCT lpCreateStruct);
LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
void UpdateLayout();
LRESULT OnNMClickListResult(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
LRESULT OnGetListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
LRESULT OnPrepareListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
LRESULT OnFindListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl);
void OnSize(UINT nType, CSize size);
void ReloadMockData(int count);
void ReloadListView();
protected:
void layoutListView();
static int GetWindowVScrollBarThumbWidth(HWND hwnd, bool bAutoShow);
private:
std::wstring GetControlText(HWND hwnd,wchar_t* buf = NULL);
CListViewCtrl listview_;
vector<shared_ptr<Photo>> photos_;
CFont font_normal_;
CFont font_bold_;
CBrushHandle brush_white_;
CBrushHandle brush_hollow_;
CBrush brush_red_;
CButton buttonIsLayoutWithThumb_;
CButton buttonReloadMockData_;
CButton buttonReloadMockData10_;
CButton buttonReloadListView_;
};
View.cpp
// View.cpp : implementation of the CView class
//
/
#include "stdafx.h"
#include "resource.h"
#include <utility>
#include <sstream>
#include <stdint.h>
#include <assert.h>
#include <Strsafe.h>
#include "View.h"
#include <CommCtrl.h>
#include <string>
#include <regex>
using namespace std;
BOOL CView::PreTranslateMessage(MSG* pMsg)
{
return FALSE;
}
LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CPaintDC dc(m_hWnd);
CMemoryDC mdc(dc,dc.m_ps.rcPaint);
CRect rect_client;
GetClientRect(&rect_client);
mdc.FillSolidRect(rect_client,RGB(255,255,255));
//TODO: Add your drawing code here
return 0;
}
static HFONT GetFont(int pixel,bool bold,const wchar_t* font_name)
{
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT)); // zero out structure
lf.lfHeight = pixel; // request a 8-pixel-height font
if(bold)
{
lf.lfWeight = FW_BOLD;
}
lstrcpy(lf.lfFaceName, font_name); // request a face name "Arial"
HFONT font = ::CreateFontIndirect(&lf);
return font;
}
std::wstring CView::GetControlText(HWND hwnd,wchar_t* buf)
{
auto length = ::GetWindowTextLength(hwnd);
bool bufNull = false;
if(!buf){
buf = new wchar_t[length+1]();
bufNull = true;
}
::GetWindowText(hwnd,buf,length+1);
std::wstring str(buf);
if(bufNull)
delete []buf;
return str;
}
static std::wstring GetProductBinDir()
{
static wchar_t szbuf[MAX_PATH];
GetModuleFileName(NULL,szbuf,MAX_PATH);
PathRemoveFileSpec(szbuf);
int length = lstrlen(szbuf);
szbuf[length] = L'\\';
szbuf[length+1] = 0;
return std::wstring(szbuf);
}
LRESULT CView::OnGetListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
{
NMLVDISPINFO* plvdi = (NMLVDISPINFO*) pnmh;
auto iItem = plvdi->item.iItem;
if (-1 == iItem)
return 0;
auto count = photos_.size();
if(!count || count <= iItem)
return 0;
auto photo = photos_[iItem];
if(plvdi->item.mask & LVIF_TEXT){
switch(plvdi->item.iSubItem)
{
case 0:
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, to_wstring((int64_t)iItem+1).c_str());
break;
case 1:
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, photo->name.c_str());
break;
case 2:
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, photo->format.c_str());
break;
case 3:
StringCchCopy(plvdi->item.pszText, plvdi->item.cchTextMax, photo->createDate.c_str());
break;
}
}
return 0;
}
LRESULT CView::OnPrepareListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
{
return 0;
}
LRESULT CView::OnFindListViewData(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
{
LPNMLVFINDITEM pnmfi = (LPNMLVFINDITEM)pnmh;
auto iItem = pnmfi->iStart;
if (-1 == iItem)
return -1;
auto count = photos_.size();
if(!count || count <= iItem)
return -1;
return 0;
}
LRESULT CView::OnNMClickListResult(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
{
return 0;
}
void CView::OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
{
switch(nID)
{
case kMyButtonId3:
{
buttonIsLayoutWithThumb_.SetCheck(!buttonIsLayoutWithThumb_.GetCheck());
ReloadListView();
layoutListView();
break;
}
case kMyButtonId:
{
ReloadMockData(10000);
break;
}
case kMyButtonId4:
{
ReloadMockData(10);
break;
}
case kMyButtonId2:
{
ReloadListView();
break;
}
}
}
void CView::ReloadListView()
{
listview_.SetItemCount(0);
photos_.clear();
}
void CView::ReloadMockData(int count)
{
wchar_t buf[MAX_PATH] = {0};
LVCOLUMN co;
memset(&co,0,sizeof(co));
co.mask = LVCF_TEXT;
co.pszText = buf;
co.cchTextMax = MAX_PATH;
listview_.GetColumn(1,&co);
std::wstring c0Text(buf);
listview_.GetColumn(2,&co);
std::wstring c1Text(buf);
listview_.GetColumn(3,&co);
std::wstring c2Text(buf);
static int index = 0;
for(int i = 0; i< count;++i,++index){
auto photo = new Photo();
wsprintf(buf,(c0Text+L"-%d").c_str(),index);
photo->name = buf;
wsprintf(buf,(c1Text+L"-%d").c_str(),index);
photo->format = buf;
wsprintf(buf,(c2Text+L"-%d").c_str(),index);
photo->createDate = buf;
photos_.push_back(move(shared_ptr<Photo>(photo)));
}
listview_.SetItemCount(photos_.size());
}
int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
font_normal_ = ::GetFont(16,false,L"Arial");
font_bold_ = ::GetFont(16,true,L"Arial");
brush_hollow_ = AtlGetStockBrush(HOLLOW_BRUSH);
brush_white_ = AtlGetStockBrush(WHITE_BRUSH);
brush_red_.CreateSolidBrush(RGB(255,0,0));
// 1.创建CListViewCtrl
listview_.Create(m_hWnd,0,NULL,WS_CHILD | WS_TABSTOP |WS_VISIBLE
|LVS_ALIGNLEFT|LVS_REPORT|LVS_SHOWSELALWAYS|WS_BORDER|LVS_OWNERDATA,0,kMyListViewId);
listview_.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_DOUBLEBUFFER);
listview_.SetFont(font_normal_);
auto header = listview_.GetHeader();
header.SetFont(font_bold_);
listview_.SetBkColor(RGB(255,255,255));
listview_.InsertColumn(0,L"No.",LVCFMT_LEFT,0);
listview_.InsertColumn(1,L"Name",LVCFMT_LEFT,0);
listview_.InsertColumn(2,L"Format",LVCFMT_LEFT,0);
listview_.InsertColumn(3,L"Create Date",LVCFMT_LEFT,0);
// 2.创建按钮
buttonIsLayoutWithThumb_.Create(m_hWnd, 0, L"布局列宽是否计算滑块宽度", WS_CHILD | WS_VISIBLE | BS_CHECKBOX, 0, kMyButtonId3);
buttonIsLayoutWithThumb_.SetFont(font_normal_);
buttonReloadMockData_.Create(m_hWnd,0,L"加载新数据10000条",WS_CHILD|WS_VISIBLE,0,kMyButtonId);
buttonReloadMockData_.SetFont(font_normal_);
buttonReloadMockData10_.Create(m_hWnd, 0, L"加载新数据10条", WS_CHILD | WS_VISIBLE, 0, kMyButtonId4);
buttonReloadMockData10_.SetFont(font_normal_);
buttonReloadListView_.Create(m_hWnd,0,L"清空表格",WS_CHILD|WS_VISIBLE,0,kMyButtonId2);
buttonReloadListView_.SetFont(font_normal_);
return 0;
}
int CView::GetWindowVScrollBarThumbWidth(HWND hwnd, bool bAutoShow)
{
SCROLLBARINFO sb = { 0 };
sb.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwnd, OBJID_VSCROLL, &sb);
if (!bAutoShow)
return sb.dxyLineButton;
if (sb.dxyLineButton)
return sb.dxyLineButton;
::ShowScrollBar(hwnd, SB_VERT, TRUE);
sb.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwnd, OBJID_VSCROLL, &sb);
::ShowScrollBar(hwnd, SB_VERT, FALSE);
return sb.dxyLineButton;
}
void CView::layoutListView()
{
CRect rect;
listview_.GetClientRect(&rect);
auto width = rect.Width();
listview_.SetColumnWidth(0, width*0.2);
listview_.SetColumnWidth(1, width*0.3);
listview_.SetColumnWidth(2, width*0.2);
auto last = width * 0.3;
if (buttonIsLayoutWithThumb_.GetCheck()) {
auto thumbWidth = GetWindowVScrollBarThumbWidth(listview_, false);
if (!thumbWidth) {
// 如果默认没有滚动条
thumbWidth = GetWindowVScrollBarThumbWidth(listview_, true);
last = last - thumbWidth;
}
}
listview_.SetColumnWidth(3, last);
}
void CView::OnSize(UINT nType, CSize size)
{
if (!size.cx)
return;
UpdateLayout();
}
void CView::UpdateLayout()
{
CRect rect;
GetClientRect(&rect);
CClientDC dc(m_hWnd);
dc.SelectFont(font_normal_);
CSize sizeCheck;
buttonIsLayoutWithThumb_.GetIdealSize(&sizeCheck);
buttonIsLayoutWithThumb_.MoveWindow(CRect(20,2, sizeCheck.cx+20, sizeCheck.cy+2));
CSize size_control(rect.Width() - 50,300);
CRect rect_control = CRect(CPoint(20,20),size_control);
listview_.MoveWindow(rect_control);
layoutListView();
CSize size_button;
buttonReloadMockData_.GetIdealSize(&size_button);
rect_control = CRect(CPoint(rect_control.left,rect_control.bottom+10),size_button);
buttonReloadMockData_.MoveWindow(rect_control);
CSize size_button4;
buttonReloadMockData10_.GetIdealSize(&size_button4);
rect_control = CRect(CPoint(rect_control.right+10, rect_control.top), size_button);
buttonReloadMockData10_.MoveWindow(rect_control);
CSize sizeButton2;
buttonReloadListView_.GetIdealSize(&sizeButton2);
rect_control = CRect(CPoint(rect_control.right+10,rect_control.top),sizeButton2);
buttonReloadListView_.MoveWindow(rect_control);
}
图2
图3
项目下载地址
https://download.csdn.net/download/infoworld/89036594
参考
-
GetScrollBarInfo 函数 (winuser.h) - Win32 apps | Microsoft Learn
-
SCROLLBARINFO (winuser.h) - Win32 apps | Microsoft Learn
-
ShowScrollBar 函数 (winuser.h) - Win32 apps | Microsoft Learn