在我的应用程序中,我有一个表格,其中包含组织内各个区域的数据,相关表格包含有关每个区域的统计数据。为简单起见,我将它们表示为:
____________ ________________ ________________
|Region | |RegionHasDemo | |DemoCategories|
| |1 *| |* 1| |
|-RegionID |----------|-RegionID |----------|-CatID |
|-City | |-CatID | |-SortOrder |
|-Zip | |-Population | |-Archive |
|-State | |______________| |______________|
|__________|
DemoCategories 表包含人口统计类型。例如,假设 RegionHasDemo 代表我所在地区的年龄人口。DemoCategories 中的 CatID 将是 Age20to30、Age20to40 等值,因此 RegionHasDemo 代表所有地区的年龄组人口。
在我的应用程序中,我想创建一个表单来添加区域数据以及所有相关数据。为此,我创建了两个绑定源,一个用于 RegionData,一个用于 RegionHasDemo,其中包含 RegionData 作为 DataSource。由于各种原因,我想通过将单个文本框绑定到 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 时关闭表单并再次打开它,更改就消失了。就好像没有通知数据集这些值已更改。此外,如果我将相关数据放在自定义文本框列表旁边的数据网格中,我可以从 DataGridView 修改数据,并且可以保存数据。此外,当我更改 TextBoxes 中的数据时,新值将显示在 DataGridView 中,但仍不会保存...
我想知道我用来将文本框绑定到数据的方法是否正确。意思是,我可以以包含在 Binding 源中的 DataRowView 的形式绑定数据,并期望它的行为与我直接使用绑定源的行为相同吗?
非常感谢您的帮助。我希望我已经提供了足够的信息让你继续。请记住,此代码示例来自原型。我知道这不是最佳做法。也就是说,我将不胜感激任何建设性的批评。