You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:23:22