在C#中为网格中的不同级别创建不同的事件处理程序

时间:2014-08-28 18:05:00

标签: c# event-handling c1flexgrid

我正在使用ComponentOne的DataTree Grid。目前,DataTree网格有2个级别(Parent& Child)。每个网格中的每一行都有一个复选框列,用户可以在其中选择'排。当用户选择Parent Row时会触发事件flexgrid_CellChecked。选择子网格后,触发的事件是flexgrid_ChildCellChecked。我想在DataTree中添加第3级,并在选择最内层网格中的Checkbox时触发相应的事件。我们称之为flexgrid_ChildChildCellChecked。

此事件在DataTree类的Expand(int row)方法中实例化。问题是当扩展第一个子级(级别2)时,可以将事件ChildCellChecked和ChildChildCellChecked添加为处理程序。当扩展第二个子级(级别3)时,这两个事件都为空。

这是带有Expand方法的DataTree类:

public class C1FlexDataTree : C1FlexGrid, ISupportInitialize
{
    //--------------------------------------------------------------------------------
    #region ** fields

    // reference to hidden column that contains details rows for each master record
    //
    // this is created automatically by a DataSet based on its Relations.
    //
    // e.g. if the parent table is 'Orders', this could be an 'OrderDetails' table 
    // with the order details for each order on the master data table.
    //
    private Column _colChild = null;

    // child grid that displays the headers rows over the native header rows.
    //
    // this grid appears on top of all child controls and prevents children from
    // hiding the parent grid's header rows when they scroll.
    //
    private GridHeader _hdr = null; // <<1.1>>
// fire event to allow setting up child grids
    // the event sender is the child grid that was just bound
    public event EventHandler SetupColumns;
    public event RowColEventHandler ChildCellChecked;
    public event RowColEventHandler ChildChildCellChecked;

    protected virtual void OnSetupColumns(object sender)
    {
        if (SetupColumns != null)
            SetupColumns(sender, EventArgs.Empty);
    }

    protected virtual void OnChildCellChecked(object sender, RowColEventArgs e)
    {
        if (ChildCellChecked != null)
        {
            ChildCellChecked(sender, e);
        }
    }

    protected virtual void OnChildChildCellChecked(object sender, RowColEventArgs e)
    {
        if (ChildChildCellChecked != null)
        {
            ChildChildCellChecked(sender, e);
        }
    }
    // get top-level grid (overall parent)
    public C1FlexDataTree ParentGrid
    {
        get
        {
            C1FlexDataTree parent = this;
            while (parent.Parent is C1FlexDataTree)
                parent = parent.Parent as C1FlexDataTree;
            return parent;
        }
    }
// expand row
    public bool ExpandRow(int row)
    {
        // sanity
        if (row < Rows.Fixed || row >= Rows.Count)
        {
            return false;
        }

        // check that the row is not already expanded
        C1FlexDataTree childGrid = Rows[row].UserData as C1FlexDataTree;
        if (childGrid != null)
        {
            return false;
        }

        // check that we have a data source for this row
        object dataSource = _colChild != null? _colChild[row] : null;
        if (!(dataSource is IBindingList))
        {
            return false;
        }

        // ** fire before collapse event
        var e = new RowColEventArgs(row, -1);
        OnBeforeCollapse(e);
        if (e.Cancel)
        {
            return false;
        }

        // add node row (unbound) to display child
        Rows.InsertNode(row + 1, -1);

        // make new row non-editable (it's just a placeholder)
        Rows[row + 1].AllowEditing = false;

        // create child grid
        childGrid = new C1FlexDataTree();
        childGrid.Visible = false;
        childGrid.ScrollBars = ScrollBars.Horizontal;



            // hook up event handlers
           //When there is only 2 levels this 'if' statement is not needed.
           //But with 3 levels, the ChildCellChecked is null
            if (ChildCellChecked != null)
            {
                childGrid.CellChecked += new RowColEventHandler(ChildCellChecked);

        }

        // attach child grid to parent, set data source
        Controls.Add(childGrid);
        childGrid.DataSource = dataSource;

        // save references: 
        // child grid Tag property contains a reference to the parent row
        // parent row UserData contains a reference to the child grid
        childGrid.Tag = Rows[row];
        Rows[row].UserData = childGrid;

        // make child grid visible, move it into position
        childGrid.Visible = true;
        childGrid.UpdatePosition();
        childGrid.Focus();
//When _colChild is null, the 3rd level is being expanded.
//ChildChildCellChecked is null
            if (childGrid._colChild == null)
            {             
                childGrid.CellChecked += new RowColEventHandler(ChildChildCellChecked);
            }

            OnAfterCollapse(e);

            // done
            return true;
        }

我想创建事件,ChildCellChecked用于第二级网格,ChildChildCellChecked用于第3级网格。

网格正确显示,可以展开和折叠。但是,当我单击第3级网格中的复选框时,不会触发任何事件。

如何将事件处理程序与每个级别的网格相关联?

更新

使用您的一个代码段来识别正在扩展的级别,当级别为2时,事件ChildChildCellChecked为空。

在第一级调用期间和第二级调用期间调用方法Expand()时,我附加了一个文档以显示调试模式下的屏幕截图。

在Expand()的第一级调用期间,创建了ChildCellChecked 在Expand()的第二级调用期间,ChildChildCellChecked为null。这导致例外。

在第二级调用Expand()期间,我无法弄清楚为什么委托,ChildChildCellChecked为null。

附加的屏幕截图可以更好地显示错误...... 在调试模式下拍摄2个屏幕截图。

第一个网格已扩展。 这是父网格扩展到第一个子网格的时候。 您可以看到定义了两个BOTH事件。仅创建了ChildCellCheck。 Delegate to ChildCellChecked event during first call Expand() Delegate to ChildChildCellChecked event during first call Expand()

第二个网格已扩展。 当第一个子网格展开以显示第二级网格时,这是错误: Error showing exception during second call Expand() to assign ChildChildCellChecked delegate

实际上,在第一次调用Expand()期间,两个代理都与各自的事件方法相关联。在第二次调用Expand()期间,两个委托都为空。

我原本以为在创建子网格时,无论级别如何,都会为该子网格创建一个事件处理程序。我不需要第二级网格的第二个事件处理程序。单击复选框时,将触发事件,并且在事件方法中,您可以确定触发事件的子网格级别。

格洛丽亚

2 个答案:

答案 0 :(得分:0)

正如我在您的原始帖子here上发布的那样: -

请参阅以下评论:

  • 展开节点时,会将子网格添加到父级的Controls集合中。同样,当您折叠一行时,子网格将从父级的Controls集合中删除。删除子网格时,将清除子项的HeaderGrid的UserData。因此,它将完全使事件处理程序无效。这就是为什么这个代码段无法直接使用的原因。

  • 如果在为每个网格添加事件之前展开所有行,则在每行只有一个子项的情况下,它将完美地工作。但是,这不适用于更一般的情况,因为当只有一个孩子的事件被提出时,所有孩子都会被召唤为孩子。

  • 正如您尝试实现的那样,最好的情况肯定是在DataTree类的ExpandRow方法中添加子事件。这样,无论何时展开行或创建网格,事件处理程序都将附加到网格。可以执行检查以查看当前网格是否应该与“孩子”或“孩子的孩子”事件相关联。请参阅以下代码段:

     int level = 0;
     C1FlexDataTree parent = new C1FlexDataTree();
     parent = childGrid;
     while (parent.Parent is C1FlexDataTree)
     {
     parent = parent.Parent as C1FlexDataTree;
     level++;
     }
     if (level ==  1)
     {
     childGrid.CellChecked += new RowColEventHandler(child_CellChecked);
     }
     else if (level == 2)
     {
     childGrid.CellChecked += new RowColEventHandler(childchild_CellChecked);
     }
    
  • 在您的代码段中,您正在检查childGrid.Rows [row]的UserData是否为空。但是,childGrid行的UserData始终为null。每当将子网格添加到网格时,子网格的每一行都会折叠或不可展开。

  • 提示:如果某行的UserData为null,则表示该行已折叠或该行没有任何childGrid

    在第三点,我们为“孩子”和“孩子的孩子”创建了活动。这不会为父网格创建事件。因此,您可能必须在DataTree类的构造函数中添加一个片段

     if (this.ParentGrid == this)
     {
          this.CellChecked+=new RowColEventHandler(Parent_CellChecked);
     }
    

<强> 重要

父网格中的所有子网格都是DataTree。这就是为什么扩展每个子网格的一行也会触发ExpandRow方法并最终尝试添加事件处理程序。请采取正确的措施以确保ChildGrid的ExpandRow方法不会添加父网格之类的事件。这可以通过检查childGrid的级别来确保。它在步骤3中执行。

答案 1 :(得分:0)

请注意,每次创建 DataTree 时,所有嵌入的网格(父对象的子网)都会实现相同的事件。因此,在同一 DataTree 类中创建 ParentCheck ChildCheck ChildChildCheck 事件是不对的。这就是我们在 DataTree 类中创建 ParentCheck ChildCheck ChildChildCheck 事件的原因,即使是子项执行< strong> ChilChildCheck 活动。

DataTree 类中创建 CellChecked 事件时,会发现所有网格都会继承 CellChecked 事件。这是一个非常有利的情况,我们所要做的就是确定网格的层次级别。

复选框的实现是放置事件中最不可靠的部分。由于复选框只是布尔列而不是字形嵌入 控件(因为它在某些在线示例中实现),因此复选框不需要与事件单独链接,因此可以通过覆盖基本 CellChecked 事件来实现 CellChecked 事件。

请参阅以下代码段:

protected override void OnCellChecked(RowColEventArgs e)
{
    base.OnCellChecked(e);
    // Access childs from a flex data tree
    if (this.Rows[e.Row].UserData != null)
    {
        C1FlexDataTree child = this.Rows[e.Row].UserData as C1FlexDataTree;
        foreach (Row childRow in child.Rows)
        {
            if (childRow.Index > 0)
            {
                childRow[1] = this.Rows[e.Row][1];
                if (childRow.UserData != null)
                {
                    C1FlexDataTree childchild = childRow.UserData as C1FlexDataTree;
                    foreach (Row childchildRow in childchild.Rows)
                    {
                        if (childchildRow.Index > 0)
                        childchildRow[1] = this.Rows[e.Row][1];
                    }
                }
            }
        }
    }
    // Check the level of grid
    C1FlexDataTree parent = this;
    int level = 0;
    while (parent.Parent is C1FlexDataTree)
    {
        level++;
        parent = parent.Parent as C1FlexDataTree;
    }

    if (level == 0)
    {
        MessageBox.Show("I am parent");
        // Place your code for ParentCellChecked here
    }
    else if (level == 1)
    {
        MessageBox.Show("I am parent’s child");
        // Place your code for ChildCellChecked here
    }
    else if (level == 2)
    {
        MessageBox.Show("I am parent’s child’s child");
        // Place your code for ChildChildCellChecked here
    }

}

请注意上面的代码段中必须实现 ParentCellChecked ChildCellChecked ChildChildCellChecked

如果我们有父网格的访问权限,也可以访问父网格的孩子的孩子......依此类推。这在下面的代码段中描述。请注意,子网格不能来自创建父网格的类。在DataTree类本身中首选使用此代码段。

if (this.Rows[XthRow].UserData != null)
{
    C1FlexDataTree child = this.Rows[e.Row].UserData as C1FlexDataTree;
    foreach (Row childRow in child.Rows)
    {
        if (childRow.Index > 0)
        {
            // ALL THE CHILD’S ROW
            if (childRow.UserData != null)
            {
                C1FlexDataTree childchild = childRow.UserData as C1FlexDataTree;
                foreach (Row childchildRow in childchild.Rows)
                {
                    if (childchildRow.Index > 0)
                         // ALL THE CHILD’S CHILD’S ROW
                }
            }
        }
    }
}