我有DataGridView
(dgvVolReport
)绑定到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);
的内容是正确的:
请注意,这两个固定的“六”对应于两个选定的行(它们之前是五个)。现在,groupNumbersAll
列的内容如下所示:
您可以从行colGroup
看到它们不匹配。他们为什么不匹配?为什么?
但更奇怪的是,如果我注释掉了最后一行(6
),那个甚至还没有运行,因为那是断点所在的地方,那么突然他们确实匹配了!有谁知道这里发生了什么?
btw我还尝试了解绑定然后重新绑定我认为已经工作了一段时间的DataSource,但现在我发现它没有。
以下是所请求的组按钮的完整代码(从按钮单击)(请注意,最终工作代码为here):
dgvVolReport.Sort(colGroup, ListSortDirection.Ascending);
答案 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];
}
我的数据如下:
名称和值都代表原始数据顺序。
问题在于排序会改变显示顺序,而不会改变基础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];
}
如果像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"
- 列的属性(值类型等)。