Listview with WS_EX_LAYOUTRTL and both scrollbars glitches when clicking on the horz scrollbar

Anonymous
2025-10-16T18:52:51.8433333+00:00

I'm reporting a bug with native scrollbars in common controls (specifically listview, but probably other scrollable controls). The bug is reproduced with the following characteristics:

Listview with BOTH horizontal and vertical scrollbars active.

WS_EX_LAYOUTRTL applied to the listview

Start with the horizontal scrollbar scrolled all the way to the right. Now click on the horizontal thumb one time. It immediately jumps left by what appears to be the vertical scrollbar width (30px).

If you mouse and drag the thumb, it also initially jumps left. If you start dragging from the right side of the thumb, as you drag left, it is possible that the mouse is over the channel due to the 30px jump.

If you hide the vertical scrollbar, the problem goes away.

Attached code to reproduce the error. This is a standard MFC template from visual studio, with a single listview added. Filled with some dummy data, and set to RTL, and the bug materializes.

// MFCApplication9Dlg.cpp : implementation file
//

#include "pch.h"
#include "framework.h"
#include "MFCApplication9.h"
#include "MFCApplication9Dlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CAboutDlg dialog used for App About

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

	// Dialog Data
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

	// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX) {
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CMFCApplication9Dlg dialog



CMFCApplication9Dlg::CMFCApplication9Dlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MFCAPPLICATION9_DIALOG, pParent) {
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMFCApplication9Dlg::DoDataExchange(CDataExchange* pDX) {
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, m_list);
}

BEGIN_MESSAGE_MAP(CMFCApplication9Dlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()


// CMFCApplication9Dlg message handlers

BOOL CMFCApplication9Dlg::OnInitDialog() {
	CDialogEx::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr) {
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty()) {
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here

	// Set list control to report view
	m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT);

	// Apply right-to-left layout
	m_list.ModifyStyleEx(0, WS_EX_LAYOUTRTL);

	// Set list control extended styles
	m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

	// Add 10 columns
	for (int col = 0; col < 10; col++) {
		CString strColumnName;
		strColumnName.Format(_T("Column %d"), col + 1);
		m_list.InsertColumn(col, strColumnName, LVCFMT_LEFT, 100);
	}

	// Add 100 rows with dummy data
	for (int row = 0; row < 100; row++) {
		CString strItem;
		strItem.Format(_T("Row %d, Col 1"), row + 1);
		int nItem = m_list.InsertItem(row, strItem);

		// Fill remaining columns
		for (int col = 1; col < 10; col++) {
			CString strSubItem;
			strSubItem.Format(_T("R%d-C%d"), row + 1, col + 1);
			m_list.SetItemText(nItem, col, strSubItem);
		}
	}

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CMFCApplication9Dlg::OnSysCommand(UINT nID, LPARAM lParam) {
	if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	} else {
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CMFCApplication9Dlg::OnPaint() {
	if (IsIconic()) {
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	} else {
		CDialogEx::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CMFCApplication9Dlg::OnQueryDragIcon() {
	return static_cast<HCURSOR>(m_hIcon);
}

Windows development | Windows API - Win32
{count} vote

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.