WinForms C# .NET8扩展ListView:监听列增改删事件并保留原有功能
WinForms扩展ListView监听列增删改事件的实现方案
核心思路
原生ColumnHeaderCollection不支持事件通知,我们可以通过继承原生集合类,重写修改集合的方法并插入事件逻辑,同时完整保留所有原生功能(包括ContainsKey、RemoveByKey等)。具体实现步骤如下:
1. 实现可观察的ColumnHeaderCollection子类
创建ObservableColumnHeaderCollection继承自ColumnHeaderCollection,添加列增删改事件,并在所有修改集合的方法中触发对应事件;同时监听列属性变化(如Text、Width)以触发修改事件。
using System.ComponentModel; using System.Reflection; using System.Windows.Forms; using System.Linq; public class ObservableColumnHeaderCollection : ColumnHeaderCollection { // 定义列操作事件 public event EventHandler<ColumnHeaderEventArgs>? ColumnAdded; public event EventHandler<ColumnHeaderEventArgs>? ColumnRemoved; public event EventHandler<ColumnHeaderChangedEventArgs>? ColumnChanged; private readonly ListView _owner; // 通过反射调用原生internal构造函数 public ObservableColumnHeaderCollection(ListView owner) : base(GetConstructor().Invoke(new object[] { owner }) as ColumnHeaderCollection) { _owner = owner; // 监听现有列的属性变化 foreach (ColumnHeader column in this) HookColumnProperties(column); } private static ConstructorInfo GetConstructor() { var ctor = typeof(ColumnHeaderCollection).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, new[] { typeof(ListView) }); return ctor ?? throw new InvalidOperationException("无法获取ColumnHeaderCollection构造函数"); } // 重写集合修改方法,插入事件逻辑 public override int Add(ColumnHeader value) { int index = base.Add(value); HookColumnProperties(value); ColumnAdded?.Invoke(this, new ColumnHeaderEventArgs(value)); return index; } public override void AddRange(ColumnHeader[] values) { base.AddRange(values); foreach (var column in values) { HookColumnProperties(column); ColumnAdded?.Invoke(this, new ColumnHeaderEventArgs(column)); } } public override void Insert(int index, ColumnHeader value) { base.Insert(index, value); HookColumnProperties(value); ColumnAdded?.Invoke(this, new ColumnHeaderEventArgs(value)); } public override void Remove(ColumnHeader value) { UnhookColumnProperties(value); base.Remove(value); ColumnRemoved?.Invoke(this, new ColumnHeaderEventArgs(value)); } public override void RemoveAt(int index) { var column = this[index]; UnhookColumnProperties(column); base.RemoveAt(index); ColumnRemoved?.Invoke(this, new ColumnHeaderEventArgs(column)); } public override void RemoveByKey(string key) { var column = this[key]; if (column != null) { UnhookColumnProperties(column); base.RemoveByKey(key); ColumnRemoved?.Invoke(this, new ColumnHeaderEventArgs(column)); } } public override void Clear() { var columns = this.Cast<ColumnHeader>().ToList(); foreach (var column in columns) { UnhookColumnProperties(column); ColumnRemoved?.Invoke(this, new ColumnHeaderEventArgs(column)); } base.Clear(); } // 监听ColumnHeader属性变化(WinForms标准方式) private void HookColumnProperties(ColumnHeader column) { TypeDescriptor.GetProperties(column)["Text"].AddValueChanged(column, OnColumnPropertyChanged); TypeDescriptor.GetProperties(column)["Width"].AddValueChanged(column, OnColumnPropertyChanged); // 可添加其他需监听的属性:Alignment、ImageIndex等 } private void UnhookColumnProperties(ColumnHeader column) { TypeDescriptor.GetProperties(column)["Text"].RemoveValueChanged(column, OnColumnPropertyChanged); TypeDescriptor.GetProperties(column)["Width"].RemoveValueChanged(column, OnColumnPropertyChanged); } private void OnColumnPropertyChanged(object? sender, EventArgs e) { if (sender is ColumnHeader column) ColumnChanged?.Invoke(this, new ColumnHeaderChangedEventArgs(column)); } } // 自定义事件参数类 public class ColumnHeaderEventArgs : EventArgs { public ColumnHeader Column { get; } public ColumnHeaderEventArgs(ColumnHeader column) => Column = column; } public class ColumnHeaderChangedEventArgs : ColumnHeaderEventArgs { public ColumnHeaderChangedEventArgs(ColumnHeader column) : base(column) { } }
2. 扩展ListView控件,重写Columns属性
创建ObservableListView继承自ListView,重写Columns属性返回我们的可观察集合,同时兼容设计器序列化:
using System.Windows.Forms; using System.ComponentModel; public class ObservableListView : ListView { private ObservableColumnHeaderCollection? _observableColumns; public new ObservableColumnHeaderCollection Columns { get { _observableColumns ??= new ObservableColumnHeaderCollection(this); return _observableColumns; } } // 兼容设计器序列化 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public override ColumnHeaderCollection Columns { get => Columns; set { if (_observableColumns != null) { _observableColumns.Clear(); _observableColumns.AddRange(value.Cast<ColumnHeader>().ToArray()); } } } }
3. 使用示例
var listView = new ObservableListView(); // 订阅列添加事件 listView.Columns.ColumnAdded += (s, e) => { MessageBox.Show($"列「{e.Column.Text}」已添加"); }; // 订阅列移除事件 listView.Columns.ColumnRemoved += (s, e) => { MessageBox.Show($"列「{e.Column.Text}」已移除"); }; // 订阅列修改事件 listView.Columns.ColumnChanged += (s, e) => { MessageBox.Show($"列「{e.Column.Text}」属性已修改,当前宽度:{e.Column.Width}"); }; // 测试操作 listView.Columns.Add("姓名"); listView.Columns[0].Width = 150; listView.Columns.RemoveByKey("姓名");
关键说明
- 保留原生功能:因为
ObservableColumnHeaderCollection直接继承自ColumnHeaderCollection,所有原生方法(ContainsKey、RemoveByKey、索引器等)都自动保留,无需重新实现。 - 反射构造:原生
ColumnHeaderCollection的构造函数是internal,因此通过反射获取并调用,确保集合能正确关联到ListView实例。 - 属性监听:使用
TypeDescriptor监听ColumnHeader的属性变化,这是WinForms中监听控件属性变更的标准方案,无需修改ColumnHeader源码。
内容的提问来源于stack exchange,提问作者totalZero
相关产品推荐
相关产品推荐

