DataGridViewのDataSourceをAllowUserToAddRows=trueで運用するときの振る舞い
DataGridView
は、AllowUserToAddRows=true
(※このプロパティはデフォルトでtrueになっている)で運用すると、BindingSourceで紐づけている内部データの末尾要素に、ユーザー編集用の1行分のデータ(以下「仮のデータ」と呼ぶことにする)を勝手に追加したり取り消し削除したりと、やりたい放題してくれちゃう仕様のようです。
そのため、BindingSource
のAdd
メソッドで追加すると、「仮のデータ」の後ろにデータが追加されるため、内部的に要素数の不整合が発生するようです。
対策
ユーザーによる直接入力での行追加をさせないのであれば、AllowUserToAddRows=false
で使うのが一番手っ取り早い。
AllowUserToAddRows=true
で運用したいなら、以下のように、「仮のデータ」があるかどうかによって、挿入するインデックスを調整すれば、正しく追加できるようである。
ソースコード
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
class MainForm : Form
{
DataGridView dgv;
BindingList<ExampleItem> items;
BindingSource wrapper;
// DataGridViewに表示する項目
public class ExampleItem
{
// フィールドは表示されない。プロパティにする必要がある。
public string FileName {get;set;}
public ExampleItem()
{
Console.WriteLine("ExampleItem() called.");
FileName="xxx";
}
public static ExampleItem CreateItem(string fileName)
{
var item = new ExampleItem(){ FileName = fileName };
Console.WriteLine(" by CreateItem.");
return item;
}
}
MainForm(string fileName)
{
items = new BindingList<ExampleItem>();
Controls.Add(
dgv = new DataGridView() {
Dock = DockStyle.Fill,
//AllowUserToAddRows = false,
//AutoGenerateColumns = false,
AllowDrop = true,
}
);
wrapper = new BindingSource();
wrapper.AddingNew += (s,e)=>{Console.WriteLine("[BindingSource Event] AddingNew");};
wrapper.BindingComplete += (s,e)=>{Console.WriteLine("[BindingSource Event] BindingComplete");};
wrapper.DataSource = items;
dgv.DataSource = wrapper;
dgv.DragEnter += Control_DragEnter;
dgv.DragDrop += Control_DragDrop;
if ( fileName != null ) {
RegisterItem(fileName);
}
}
void Control_DragEnter(Object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
e.Effect = DragDropEffects.Copy;
}
else {
e.Effect = DragDropEffects.None;
}
}
void Control_DragDrop(Object sender, DragEventArgs e)
{
Console.WriteLine("[Event] DragDrop");
var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
foreach ( var s in fileNames ) {
RegisterItem(s);
}
}
void RegisterItem(string filePath)
{
var item = ExampleItem.CreateItem(filePath);
if ( item != null ) {
Console.Write("DataGridView's Row Count: ");
Console.WriteLine(dgv.Rows.Count);
Console.Write("Internal data's Row Count: ");
Console.WriteLine(items.Count);
Console.WriteLine("Add...");
if ( dgv.Rows.Count >= items.Count && items.Count >= 1 ) { // 対策コード
wrapper.Insert(items.Count-1, item); // 対策コード
} // 対策コード
else { // 対策コード
wrapper.Add(item);
} // 対策コード
Console.WriteLine("completed");
Console.Write("DataGridView's Row Count: ");
Console.WriteLine(dgv.Rows.Count);
Console.Write("Internal data's Row Count: ");
Console.WriteLine(items.Count);
}
}
[STAThread]
static void Main(string[] args)
{
Application.Run(new MainForm((args.Length==1)?args[0]:null));
}
}