私のアプリケーションでは、組織内のさまざまな地域のデータを含むテーブルと、各地域に関する統計データを含む関連テーブルがあります。簡単にするために、それらを次のように表します。
____________ ________________ ________________
|Region | |RegionHasDemo | |DemoCategories|
| |1 *| |* 1| |
|-RegionID |----------|-RegionID |----------|-CatID |
|-City | |-CatID | |-SortOrder |
|-Zip | |-Population | |-Archive |
|-State | |______________| |______________|
|__________|
DemoCategories テーブルには、人口統計のタイプが含まれています。たとえば、RegionHasDemo が私の地域の年齢人口を表しているとしましょう。DemoCategories の CatID は、Age20to30、Age20to40 ... などの値になり、RegionHasDemo はすべての地域の年齢層人口を表します。
私のアプリケーションでは、すべての関連データとともに地域データを追加するフォームを作成したいと考えています。これを行うために、2 つのバインディング ソースを作成しました。1 つは RegionData 用で、もう 1 つは RegionData を DataSource として含む RegionHasDemo 用です。フォームのさまざまな理由から、個々のテキスト ボックスを RegionHasDemoBindingSource の List プロパティに含まれる DataRowViews にバインドして、RegionHasDemo のデータを入力したいと考えています。グリッド ビューを使用したくないことに注意してください。
RegionData の Current プロパティが変更されるたびに、テキスト ボックスにバインドするために使用するコードを次に示します。
注: テーブル名は私のサンプルとは異なります。RegionData = UNITS; RegionHasDemo = UnitExp; DemoCategories = CatExp.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace DBsample
{
public partial class Form1 : Form
{
private DataTable DataSource;
private string relatedTableName;
/// <summary>
/// Holsd the values needed to define my text box rows and
/// relate them to the table containing their human readable
/// names, sort order, and active record status.
/// </summary>
private struct textBoxRow
{
public string Key { get; set; }
public TextBox TBox { get; set; }
public Label NameLabel { get; set; }
public string Name { get; set; }
public int Value { get; set; }
}
textBoxRow[] rows;
public Form1()
{
InitializeComponent();
DataSource = injuryDB.UnitExp;
relatedTableName = "CatExpUnitExp";
}
private void Form1_Load(object sender, EventArgs e)
{
this.uNITSTableAdapter.Fill(this.injuryDB.UNITS);
this.catExpTableAdapter1.Fill(this.injuryDB.CatExp);
this.unitExpTableAdapter1.Fill(this.injuryDB.UnitExp);
// Fill data table
// Associate them in the struct in sorted order
// Get a list of categories
DataTable catTable = DataSource.ParentRelations[relatedTableName].ParentTable;
// Sort them while leaving out the ones we don't want
List<DataRow> tbr = (from r in catTable.AsEnumerable()
where r.Field<bool>("ActiveRecord")
orderby r.Field<int>("SortOrder")
select r).ToList();
// The rows we are going to show
rows = new textBoxRow[tbr.Count];
tableLayoutPanel1.RowStyles.Clear();
int rowIndex = 0;
// Create rows and add them to the form
foreach (DataRow r in tbr)
{
textBoxRow tRow = new textBoxRow();
Label lbl = new Label();
lbl.Text = r.Field<string>("CatName");
TextBox tb = new TextBox();
tRow.Key = r.Field<string>("Category");
tRow.Name = r.Field<string>("CatName");
tRow.TBox = tb;
tRow.NameLabel = lbl;
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tableLayoutPanel1.Controls.Add(tRow.NameLabel, 0, rowIndex);
tableLayoutPanel1.Controls.Add(tRow.TBox, 1, rowIndex);
rows[rowIndex] = tRow;
rowIndex++;
}
// Refresh the bindings in the text boxes when the current item changes
unitCatExpBindingSource.CurrentItemChanged += currentItemChanged;
currentItemChanged(null, null);
}
private void uNITSBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
bool validated = this.Validate();
if(validated == true)
Debug.WriteLine("Validated data to be saved");
else
Debug.WriteLine("Did not validate data to be saved");
this.uNITSBindingSource.EndEdit();
int recordsUpdated = this.tableAdapterManager.UpdateAll(this.injuryDB);
Debug.WriteLine(string.Format("{0} records were changed", recordsUpdated));
}
private void currentItemChanged(object sender, EventArgs e)
{
if (rows == null) return;
// For some reason I have to pass this into the bindingSource's find method instead of a
// straight string of the column name. It has something to do with the fact that we are
// binding to a data relation instead of a DataTable i think. Wadded through so many forums for this...
PropertyDescriptor pdc = unitCatExpBindingSource.CurrencyManager.GetItemProperties()["Cat"];
// Rebind each text box row
foreach (textBoxRow tBoxRow in rows)
{
tBoxRow.TBox.DataBindings.Clear();
// If the record doesn't exist then that means the population for that group is zero
tBoxRow.TBox.Text = "0";
//tbr.TBox.Leave -= existingRecordTBoxChanged;
//tbr.TBox.Leave -= nonExistingRecordTBoxChanged;
// Get the index of the source I want to bind to using the text
int bindingIndex = unitCatExpBindingSource.Find(pdc, tBoxRow.Key);
//object bs = unitCatExpBindingSource[bindingIndex];
if (bindingIndex >= 0)
{
Binding b = tBoxRow.TBox.DataBindings.Add("Text", unitCatExpBindingSource[bindingIndex], "Jumps", true);
}
else
{
// TODO: Create an event that adds a new to the demo table if number not 0
}
}
}
// TODO: for this to work the delete options of the relationships
// for Units -> category tables need to be set to cascade
private void existingRecordTBoxChanged(object sender, EventArgs e)
{
TextBox tBox = (TextBox)sender;
if (tBox.Text == "" || tBox.Text == "0")
{
//DataRow d = (DataRow)tBox.DataBindings[0].DataSource;
}
}
private void nonExistingRecordTBoxChanged(object sender, EventArgs e)
{
TextBox tBox = (TextBox)sender;
if (!(tBox.Text == "" || tBox.Text == "0"))
{
// TODO: Add record to the database
}
}
}
<code>
私の問題は、バインディングが正常に表示されているように見える場合、つまり、ユニットをナビゲートするとテキストボックスが変化することです。変更はデータベースに保存されません。テキスト ボックスで行った変更は DataSet に保持されます (レコードから離れて戻ってきても同じままです) が、dataSet を保存すると、フォームを閉じて再度開くと、変更は失われます。これらの値が変更されたことを dataSet に通知しなかったかのようです。さらに、関連するデータをカスタム テキスト ボックス リストと一緒にデータ グリッドに配置すると、DataGridView からデータを変更し、データを保存することができます。また、TextBoxes のデータを変更すると、新しい値が DataGridView に表示されますが、まだ保存されません...
テキストボックスをデータに正しくバインドするために使用する方法は疑問に思っています。つまり、Binding ソース内に含まれる DataRowView の形式でデータをバインドし、バインディング ソースを直接使用した場合と同じように動作することを期待できますか?
あなたの助けに感謝します。先に進むのに十分な情報を提供できたことを願っています。このコード サンプルはプロトタイプからのものであることに注意してください。それがベストプラクティスではないことを理解しています。とはいえ、建設的な批判をいただければ幸いです。