为何在ListView的SizeChanged事件中修改列宽会导致控件异常?
WinForms ListView列宽调整异常问题

复现代码
using System; using System.Drawing; using System.Windows.Forms; namespace BuggyListView { public partial class Form1 : Form { private ListView _normalListView; private ListView _brokenListView; public Form1() { InitializeComponent(); } private void Form1_Shown(object sender, EventArgs e) { ClientSize = new Size(600, 300); _normalListView = new ListView() { View = View.Details, Dock = DockStyle.Left, Width = 300 }; _normalListView.Columns.Add("Normal ListView"); _brokenListView = new ListView() { View = View.Details, Dock = DockStyle.Right, Width = 300 }; _brokenListView.Columns.Add("Buggy ListView"); Controls.Add(_normalListView); Controls.Add(_brokenListView); for (int i = 0; i < 20; i++) { _normalListView.Items.Add($"{i} very loooooooooooooooooooooooooooooooooooooooooooooooooog line"); _brokenListView.Items.Add($"{i} very loooooooooooooooooooooooooooooooooooooooooooooooooog line"); } _normalListView.Columns[0].Width = _normalListView.ClientRectangle.Width; _brokenListView.Columns[0].Width = _brokenListView.ClientRectangle.Width; //_normalListView.SizeChanged += _normalListView_SizeChanged; _brokenListView.SizeChanged += _brokenListView_SizeChanged; } private void _normalListView_SizeChanged(object sender, EventArgs e) { _normalListView.Columns[0].Width = _normalListView.ClientRectangle.Width; } private void _brokenListView_SizeChanged(object sender, EventArgs e) { _brokenListView.Columns[0].Width = _brokenListView.ClientRectangle.Width; } } }
复现步骤
若上述GIF无法正常显示,可运行代码后按以下操作复现:
- 将右侧ListView的滚动条向下拖动,使第一项不再是“0”
- 调整窗口高度,即可触发列宽异常问题
问题成因分析
这是WinForms ListView控件的布局同步时序问题:
- 当ListView滚动位置不在顶部时,
SizeChanged事件的触发时机早于控件内部滚动区域、可见项布局的更新完成时机。 - 此时
ClientRectangle.Width的数值确实是正确的,但控件内部尚未完成滚动状态与布局的同步,设置列宽等于该值时,ListView会错误地将垂直滚动条的宽度纳入布局计算,导致列宽被挤压,出现内容显示异常。 - 改用Form的
SizeChanged事件能解决问题,是因为Form尺寸变化完成后,所有子控件的布局都已同步稳定,此时调整列宽不会出现时序偏差。
内容的提问来源于stack exchange,提问作者RadarNyan
相关产品推荐
相关产品推荐

