EDN Admin
Well-known member
In my application I have a table containing data for various regions within an organization with related tables containing statistical data about each region. For simplicity Ill represent them as:
<img alt="" src="http://social.msdn.microsoft.com/Forums/getfile/180293 The DemoCategories table contains the types of demographic. For instance, lets say RegionHasDemo represented age populations for my regions. The CatIDs in DemoCategories would be values
like Age20to30, Age20to40 ... etc. so that RegionHasDemo represents age group populations for all the regions.
In my application I want to create a form to add region data along with all of the related data. To do this Ive created the two binding sources, one for RegionData and one for RegionHasDemo that contains RegionData as its DataSource. For various reasons
on my form I would like to enter the data for RegionHasDemo by binding individual text boxes to the DataRowViews contained in the List property of the RegionHasDemoBindingSource. Note, I do not want to use a grid view.
Here is the code that I use to bind to the text boxes every time the Current property of the RegionData changes:
Note: the table names are different from my sample, RegionData = UNITS; RegionHasDemo = UnitExp; DemoCategories = CatExp.
<pre class="prettyprint" style=" 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 dont 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 bindingSources 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 doesnt 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]
<br/>
My problem is that although when my binding seems to be working okay for viewing, i.e. the textboxes change when i navigate through the units. The changes dont save to the database. The changes I make in the text boxes will persist in the DataSet (when
i move away from the record and come back they stay the same) but when I save the dataSet close the form and open it back up again the changes are gone. Its as if the dataSet wasnt notified that those values were changed. Furthermore, if I put the related
data in a data grid along side my custom text box list I am able to modify the data from the DataGridView and I am able to save the data. Also, when I change the data in my TextBoxes the new values will show up in the DataGridView but still wont be saved...
Im wondering is the method that I use to bind the text boxes to the data correct. Meaning, can I bind data in the form of a DataRowView contained inside a Binding source and expect it to behave the same as if I used the binding source directly?
Your help is much appreciated. I hope Ive given enough information for you to go on. Keep in mind that this code sample is from a prototype. I understand that that it is not best practice. That said I would appreciate any constructive criticism.
<br/>
<br/>
View the full article
<img alt="" src="http://social.msdn.microsoft.com/Forums/getfile/180293 The DemoCategories table contains the types of demographic. For instance, lets say RegionHasDemo represented age populations for my regions. The CatIDs in DemoCategories would be values
like Age20to30, Age20to40 ... etc. so that RegionHasDemo represents age group populations for all the regions.
In my application I want to create a form to add region data along with all of the related data. To do this Ive created the two binding sources, one for RegionData and one for RegionHasDemo that contains RegionData as its DataSource. For various reasons
on my form I would like to enter the data for RegionHasDemo by binding individual text boxes to the DataRowViews contained in the List property of the RegionHasDemoBindingSource. Note, I do not want to use a grid view.
Here is the code that I use to bind to the text boxes every time the Current property of the RegionData changes:
Note: the table names are different from my sample, RegionData = UNITS; RegionHasDemo = UnitExp; DemoCategories = CatExp.
<pre class="prettyprint" style=" 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 dont 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 bindingSources 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 doesnt 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]
<br/>
My problem is that although when my binding seems to be working okay for viewing, i.e. the textboxes change when i navigate through the units. The changes dont save to the database. The changes I make in the text boxes will persist in the DataSet (when
i move away from the record and come back they stay the same) but when I save the dataSet close the form and open it back up again the changes are gone. Its as if the dataSet wasnt notified that those values were changed. Furthermore, if I put the related
data in a data grid along side my custom text box list I am able to modify the data from the DataGridView and I am able to save the data. Also, when I change the data in my TextBoxes the new values will show up in the DataGridView but still wont be saved...
Im wondering is the method that I use to bind the text boxes to the data correct. Meaning, can I bind data in the form of a DataRowView contained inside a Binding source and expect it to behave the same as if I used the binding source directly?
Your help is much appreciated. I hope Ive given enough information for you to go on. Keep in mind that this code sample is from a prototype. I understand that that it is not best practice. That said I would appreciate any constructive criticism.
<br/>
<br/>
View the full article