检测列重新排序-ColumnDisplayIndexChanged引发多次

时间:2018-12-19 10:37:10

标签: c# .net winforms datagridview

我正在开发一个.net 4.6.1 C#winforms项目,该项目具有一个datagridview,用户可以在其中更改列的顺序。

我想将新订单存储在db表中,但是很难找到正确的事件来检测用户何时更改了列的顺序。

在这里搜索后,我被告知发生了DataGridView.ColumnDisplayIndexChanged事件in this thread。但这并不能解决我的问题。 (仅当您填充数据网格视图时,它才提供针对多个事件的解决方案,但这可以通过在设置数据源后添加处理程序来轻松解决)

这种方式有效,但是在用户更改列顺序时会被多次触发(fe看起来就像将A,B,C,D列更改为D,A,B,C时一样,事件被触发了3次(可能适用于A,B,D,C-A,D,B,C-D,A,B,C)

我很难确定如何确定事件是否是最后一个(因为我不想存储所有这些新订单,所以只存储最后一个)

我的问题是:

  1. 此事件是我的案例中使用的“最佳”事件吗?

  2. 如果是这样,我如何检测到最终的ColumnDisplayIndexChanged事件(D,A,B,C)?

2 个答案:

答案 0 :(得分:1)

当您对列重新排序时,ColumnDisplayIndexChanged将针对其显示索引已更改的所有列升高。例如,如果您将A列移动到C列之后的位置,则事件将针对所有这三列引发。

有一种解决方案可以捕获最后一个。 DataGridViewColumn有一个称为DisplayIndexHasChanged的内部属性,如果应为该列触发事件,则为true。引发事件的私有方法,调查列的列表,如果该属性为true,则对每一列,首先将其设置为false,然后引发事件。您可以阅读内部实现here

您可以检查是否没有具有真实值的DisplayIndexHasChanged列,可以说这是序列中的最后一个事件:

private void dgv_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
    var g = (DataGridView)sender;
    var property = typeof(DataGridViewColumn).GetProperty("DisplayIndexHasChanged",
        System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    if (g.Columns.Cast<DataGridViewColumn>().Any(x => (bool)property.GetValue(x)))
        return;
    else
        MessageBox.Show("Changed");
}

请记住,添加列时应禁用捕获该事件:

private void f_Load(object sender, EventArgs e)
{
    LoadData();
}
void LoadData()
{
    dgv.ColumnDisplayIndexChanged -= dgv_ColumnDisplayIndexChanged;
    dgv.DataSource = null;
    var dt = new DataTable();
    dt.Columns.Add("A");
    dt.Columns.Add("B");
    dt.Columns.Add("C");
    dgv.DataSource = dt;
    dgv.ColumnDisplayIndexChanged += dgv_ColumnDisplayIndexChanged;
}

答案 1 :(得分:0)

我的建议是不要做任何自定义逻辑来找出它是否是最后一个或类似的东西。最好的方法是在每次事件发生后进行保存,但是您可以将其删除。

如果您希望在两次呼叫之间允许一段时间,则在新事件触发后立即使用去抖动方法可以取消旧事件。

Ex:仅在说1秒或5秒之后(视您的应用可接受的条件而定)之后,没有新事件时才写入存储空间

假设我们决定以1秒的反跳来保存

第一个事件发生,您触发了要执行1秒的动作

如果触发了另一个事件,则旧动作将被忽略,新动作现在将有1秒的执行时间,依此类推,对于其他顺序动作

public static Action Debounce(this Action func, int milliseconds = 300)
{
    var last = 0;
    return arg =>
    {
        var current = Interlocked.Increment(ref last);
        Task.Delay(milliseconds).ContinueWith(task =>
        {
            if (current == last) func(arg);
            task.Dispose();
        });
    };
}

假设执行以下操作以保存数据 动作a =(arg)=> {    在这里保存我的数据 };

首先为您的操作分配防抖器

var debouncedWrapper = a.Debounce(1000); //1 sec debounce

然后您可以按以下方式使用它

public void datagridchangeevent(object sender, Event e)
{
  debouncedWrapper()
}

这将忽略顺序调用,并且只有在一秒钟内什么都没有调用时,才执行密码子

相关问题