编辑DataGridView数据会更改行顺序

时间:2015-06-11 15:55:48

标签: c# .net winforms sorting datagridview

我有DataGridViewdgvVolReport)绑定到DataTable。表格中有一列名为"group"colGroup上的DataGridView)。我正在尝试创建一个按钮,可以将所有选定的行组合在一起(即分配相同的组号)。

我的逻辑是分配组号并正确编辑其他组号,每行的新组存储在名为List<int>的{​​{1}}中。

然后我将这些号码转移到groupNumbersAll,如此:

DataGridView

不知何故,这似乎改变了行顺序或将数字分配给错误的行。

我在该循环之后调试了for (int r = 0; r < groupNumbersAll.Count; r++) { dgvVolReport.Rows[r].Cells["colGroup"].Value = groupNumbersAll[r]; } // A breakpoint is set on this line (i.e. values below are BEFORE this line has been run dgvVolReport.Sort(colGroup, ListSortDirection.Ascending); 的内容是正确的:

enter image description here

请注意,这两个固定的“六”对应于两个选定的行(它们之前是五个)。现在,groupNumbersAll列的内容如下所示:

enter image description here

您可以从行colGroup看到它们不匹配。他们为什么不匹配?为什么?

但更奇怪的是,如果我注释掉了最后一行(6),那个甚至还没有运行,因为那是断点所在的地方,那么突然他们确实匹配了!有谁知道这里发生了什么?

btw我还尝试了解绑定然后重新绑定我认为已经工作了一段时间的DataSource,但现在我发现它没有。

以下是所请求的组按钮的完整代码(从按钮单击)(请注意,最终工作代码为here):

dgvVolReport.Sort(colGroup, ListSortDirection.Ascending);

4 个答案:

答案 0 :(得分:1)

我认为你的groupNumbersAll - 或者它是如何使用的 - 应该受到指责。很难确定,因为我们无法看到它是如何使用的,更重要的是,它是为重复使用做好准备的。然而,这是不正确的:

for (int r = 0; r < groupNumbersAll.Count; r++)
{
    dgvVolReport.Rows[r].Cells["colGroup"].Value = groupNumbersAll[r];
}

如果DGV是数据绑定的,则不应更改DGV中的值。实际上,如果在新赋值后查询单元格,则应该看到它未更改:

Console.WriteLine(dgvGrouper.Rows[1].Cells[GrpColIndex].Value);
dgvGrouper.Rows[1].Cells[GrpColIndex].Value = 99;
Console.WriteLine(dgvGrouper.Rows[1].Cells[GrpColIndex].Value);

Mine之前打印相同的值。我不得不认为Group值没有显示在网格上,或者你会看到值没有改变。相反,您应该更改DataSource:

for (int n = 0; n < myDT.Rows.Count; n++)
{
    myDT.Rows[n][GrpColIndex] = rList[n];
} 

我的数据如下:
enter image description here

名称和值都代表原始数据顺序。

问题在于排序会改变显示顺序,而不会改变基础DataTable - DGV只是呈现数据视图(DataTable - &gt; DataView - &gt; DataGridView控件)。转储已排序的DGV,List和DataTable的Group值演示了这一点:

*** SORTED DGV Rows to List and DT  ***
DGV Grp: 1   List Val: 1   DT Val: 1  
DGV Grp: 1   List Val: 2   DT Val: 2  
DGV Grp: 1   List Val: 2   DT Val: 2  
DGV Grp: 1   List Val: 1   DT Val: 1  
DGV Grp: 2   List Val: 1   DT Val: 1  
DGV Grp: 2   List Val: 1   DT Val: 1  

我通过DataTable对第2行和第3行进行了分组。然后List和DT同步,但DGV不同步。下次为所选行迭代DGV时,这些行索引与List索引或DataTable行索引无关。

由于DGV选定的行是起点,因此您需要将DataGridView选定的行索引转换为DataTable行索引。如果DGV会发生其他事情(添加行,删除行,用户按列排序等),我每次都会重建List(无论如何我会这样做,因为它完全脱离了控件和数据) :

// rebuild the List from the Group value in the DataTable
rList = new List<int>();
for (Int32 n = 0; n < myDT.Rows.Count; n++)
{
   rList.Add((int)myDT.Rows[n][GrpColIndex]);
}

// loop thru the selected rows
foreach(DataGridViewRow dgvr in dgvGrouper.SelectedRows)
{
   // get at the underlying data item for this row
   // which is likely at a different index than the DGV row
   DataRowView dr = (DataRowView)dgvr.DataBoundItem;

   // use the DataView.Table to get the index of this DataRowView
   rList[dr.DataView.Table.Rows.IndexOf(dr.Row)] = newGrpVal;
}
newGrpVal += 1;

dr.DataView.Table.Rows.IndexOf(dr.Row)基本上将DGV视觉选择行索引转换为实际的DataTable行索引。只要组值没有内在含义,您就可以使用像显示的计数器那样简单的东西。如果您需要表中所选行的当前值以获得更健壮的组方法,dr[GrpColIndex]应包含您需要的值。

例:
贝克和查理已经重新集结。 DGV在底部显示它们,但我们看到它们保留在DataTable的第2和第3位。现在,在选择Delta和Echo之后,上面的代码运行了。应用更改前的指数视图:

DGV Grp: 1   List Val: 1   DT Val: 1
DGV Grp: 1   List Val: 2   DT Val: 2
DGV Grp: 1   List Val: 2   DT Val: 2
DGV Grp: 1   List Val: 3   DT Val: 1
DGV Grp: 2   List Val: 3   DT Val: 1
DGV Grp: 2   List Val: 1   DT Val: 1

计划的变更是现场;项目4和5将被设置为组#3并排序到底部。只需将列表复制到数据源:

for (int n = 0; n < myDT.Rows.Count; n++)
{
   myDT.Rows[n][GrpColIndex] = rList[n];
}   

enter image description here

如果像Dictionary或Tuple这样的东西,或许可以使用行中的哈希码可以更好地将事物联系在一起,那将是很好的,但我无法找到比重新启动列表更好的任何东西。

我会尝试修改任何显着的信息,因为时间允许再次提供有关列表和/或分组的信息。

答案 1 :(得分:0)

基于Plutonix's answer,这是最终的工作代码:

        private void btnGroup_Click(object sender, EventArgs e)
        {
            //See: https://stackoverflow.com/questions/30785736/editing-datagridview-data-changes-row-orders

            List<int> groupNumbersAll = new List<int>();
            List<int> groupNumbersNotSelected = new List<int>();
            List<int> groupNumbersSelected = new List<int>();
            List<int> rowNumbersSelected = new List<int>();
            List<int> groupNumbersOfEntirelySelectedGroups = new List<int>();

            DataTable dt = (DataTable)dgvVolReport.DataSource;

            // Populate groups (All, Selected and NotSelected)
            for (int r = 0; r < dt.Rows.Count; r++)
            {
                int group = Convert.ToInt16(dt.Rows[r]["Group"]);
                groupNumbersAll.Add(group);

                bool selected = false;
                foreach (DataGridViewRow gridrow in dgvVolReport.SelectedRows)
                {
                    DataRowView dr = (DataRowView)gridrow.DataBoundItem;
                    selected |= dr.DataView.Table.Rows.IndexOf(dr.Row) == r;
                }

                if (selected)
                {
                    groupNumbersSelected.Add(group);
                    rowNumbersSelected.Add(r);
                }
                else
                {
                    groupNumbersNotSelected.Add(group);
                }
            }

            int smallestSelectedGroupNumber = groupNumbersSelected.Min();
            int newGroupNumber = smallestSelectedGroupNumber;
            bool newGroupFlag = false;
            // If the selected rows do not contain all rows with group number equal to the smallest selected group number,
            // then we need to create a new group whose group number is the smallest selected group number plus 1.
            // This then implies that we need to add 1 to the group number of every row with a group number larger than the
            // lowest selected group number (that is the original lowest selected number before we added 1).
            if (groupNumbersNotSelected.Contains(smallestSelectedGroupNumber))
            {
                newGroupNumber++;
                newGroupFlag = true;
            }

            // Find which groups have been selected entirely, but ignore the smallest number. 
            // If a group has been entirely selected it means that that group number will no longer exist. Thus we will have to 
            // subtract 1 from each group number that is larger than a group that has been entirely selected. This process is 
            // cumulative, so if a number is higher than 2 entirely selected groups (excluding the smallest selected group) then
            // we need to subtract 2 from the group number.
            foreach (int group in groupNumbersSelected.Distinct())
            {
                if (!groupNumbersNotSelected.Contains(group) && !(group == smallestSelectedGroupNumber))
                {
                    groupNumbersOfEntirelySelectedGroups.Add(group);
                }
            }

            // Find the new group numbers
            for (int r = 0; r < groupNumbersAll.Count; r++)
            {
                int groupNum = groupNumbersAll[r];

                if (rowNumbersSelected.Contains(r))
                {
                    groupNumbersAll[r] = newGroupNumber;
                }
                else
                {
                    int subtract = groupNumbersOfEntirelySelectedGroups.Where(num => num < groupNum).Count();

                    if (newGroupFlag && groupNum >= newGroupNumber)
                    {
                        groupNum++;
                    }

                    groupNumbersAll[r] = groupNum - subtract;
                }
            }

            for (int n = 0; n < dt.Rows.Count; n++)
            {
                dt.Rows[n]["Group"] = groupNumbersAll[n];
            }   

            dgvVolReport.Sort(colGroup, ListSortDirection.Ascending);
        }

答案 2 :(得分:0)

我猜这个值尚未提交给基础数据源

直接修改基础数据源

SELECT * FROM `records` WHERE `timestamp` BETWEEN "' . $posts['from'] . '" AND "' . $posts['to'] . '"'

答案 3 :(得分:0)

我也会尝试直接编辑DataTable。暂时使用groupNumbersAll并使用增量值进行测试,而不是:

DataTable dt = (DataTable)dgvVolReport.DataSource;
dgvVolReport.DataSource = null;

for (int i = 0; i < dt.Rows.Count; i++) {
    dt.Rows[i]["Group"] = i;
}

dgvVolReport.DataSource = dt;
//dgvVolReport.Sort(colGroup, ListSortDirection.Ascending);

如果"Group"中的每个行和列DataGridView都没有显示唯一的整数,则问题出在DataGridView的属性中。您还应该检查"Group" - 列的属性(值类型等)。