I have the following code to load an bind data to master-detail window, where master data bound to textboxes and other controls and detail data bound to DataGridView:
ON FORM LOAD:
DataSet dsOrders = new DataSet("dsOrders");
SqlDataAdapter daOrderHeader;// = new SqlDataAdapter();
SqlDataAdapter daOrderDetail;// = new SqlDataAdapter();
BindingSource bsOrderHeader = new BindingSource();
BindingSource bsOrderDetail = new BindingSource();
DataTable dtOrderHeader = new DataTable("dtOrderHeader");
DataTable dtOrderDetail = new DataTable("dtOrderDetail");
dsOrders.Tables.Add(dtOrderHeader);
dsOrders.Tables.Add(dtOrderDetail);
daOrderHeader = DataAdapterOrderHeader();
daOrderDetail = DataAdapterOrderDetail();
daOrderHeader.Fill(dtOrderHeader);
daOrderDetail.Fill(dtOrderDetail);
////Set up a master-detail relationship between the DataTables
DataColumn keyOrderHeaderColumn = dsOrders.Tables["dtOrderHeader"].Columns["ID"];
DataColumn foreignKeyOrderDetailColumn = dsOrders.Tables["dtOrderDetail"].Columns["OrderId"];
dsOrders.Relations.Add("rOrders", keyOrderHeaderColumn, foreignKeyOrderDetailColumn);
bsOrderHeader.DataSource = dsOrders;
bsOrderHeader.DataMember = "dtOrderHeader";
bsOrderDetail.DataSource = bsOrderHeader;
bsOrderDetail.DataMember = "rOrders";
tbxOrderNo.DataBindings.Add("Text", bsOrderHeader, "ID");
tbxCustomer.DataBindings.Add("Text", bsOrderHeader, "Name");
tbxTaxRate.DataBindings.Add("Text", bsOrderHeader, "TaxRate");
tbxShipping.DataBindings.Add("Text", bsOrderHeader, "Shipping");
tbxExchangeRate.DataBindings.Add("Text", bsOrderHeader, "ExchangeRate");
dtpOrderDate.DataBindings.Add("Text", bsOrderHeader, "OrderDate");
cbxPriceCode.DataBindings.Add("SelectedIndex", bsOrderHeader, "PriceCode");
dgvItems.DataSource = bsOrderDetail;
enter code here
ON ADD NEW RECORD:
DataRow drOrderHeader = dsOrders.Tables["dtOrderHeader"].NewRow();
drOrderHeader["ID"] = (maxID + 1);
dsOrders.Tables["dtOrderHeader"].Rows.Add(drOrderHeader);
DataRow drOrderDetail = dsOrders.Tables["dtOrderDetail"].NewRow();
drOrderDetail["OrderId"] = (maxID + 1);
dsOrders.Tables["dtOrderDetail"].Rows.Add(drOrderDetail);
bsOrderHeader.DataSource = dsOrders.Tables["dtOrderHeader"];
bsOrderDetail.DataSource = dsOrders.Tables["dtOrderDetail"];
When I click add new row button, bindingsource.count property shows that new record has been added, but winform controls stay on the same record and I am not able scroll to newly created record, also bindingsource.movelast stoped working.
Any suggestion please? What I need is to scroll to newly added record/row with all winform textboxes cleared and ready to enter data for new record, except OrderId that I generate and in this code at the time of creation
UPDATE: Made changes to Add button, but unsuccessfull
newOrderId = Convert.ToInt32(dsOrders.Tables["dtOrderHeader"].Compute("max(ID)", string.Empty)) + 1;
DataRow drOrderHeader = dsOrders.Tables["dtOrderHeader"].NewRow();
drOrderHeader["ID"] = (newOrderId);
dsOrders.Tables["dtOrderHeader"].Rows.Add(drOrderHeader);
DataRow drOrderDetail = dsOrders.Tables["dtOrderDetail"].NewRow();
drOrderDetail["OrderId"] = (newOrderId);
dsOrders.Tables["dtOrderDetail"].Rows.Add(drOrderDetail);
bsOrderHeader.Position = bsOrderHeader.Find("ID", newOrderId);
As you already found, data binding detects the changes you've made in the underlying data sources, thus the following two lines:
bsOrderHeader.DataSource = dsOrders.Tables["dtOrderHeader"];
bsOrderDetail.DataSource = dsOrders.Tables["dtOrderDetail"];
are redundant. And not only that, but the second changes incorrectly the data source type of the bsOrderDetail. So simply remove them.
In order to rebind the UI to the newly added record, all you need is to set the Position property of the bsOrderHeader to the index of the new record like this:
bsOrderHeader.Position = bsOrderHeader.Find("ID", drOrderHeader["ID"]);
and the data binding infrastructure will automatically rebind both master controls and (via DataRelation) the detail grid view.
EDIT: Here is full working demo. If your code doesn't work after the update, it should be caused by some other code part not shown in the post.
using System;
using System.Data;
using System.Windows.Forms;
namespace Samples
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
DataSet dsOrders = new DataSet("dsOrders");
//SqlDataAdapter daOrderHeader;// = new SqlDataAdapter();
//SqlDataAdapter daOrderDetail;// = new SqlDataAdapter();
BindingSource bsOrderHeader = new BindingSource();
BindingSource bsOrderDetail = new BindingSource();
DataTable dtOrderHeader = new DataTable("dtOrderHeader");
DataTable dtOrderDetail = new DataTable("dtOrderDetail");
dsOrders.Tables.Add(dtOrderHeader);
dsOrders.Tables.Add(dtOrderDetail);
//daOrderHeader = DataAdapterOrderHeader();
//daOrderDetail = DataAdapterOrderDetail();
//daOrderHeader.Fill(dtOrderHeader);
//daOrderDetail.Fill(dtOrderDetail);
dtOrderHeader.Columns.Add("ID", typeof(int));
dtOrderHeader.Columns.Add("Name", typeof(string));
dtOrderHeader.Columns.Add("TaxRate", typeof(decimal));
dtOrderHeader.Columns.Add("Shipping", typeof(string));
dtOrderHeader.Columns.Add("ExchangeRate", typeof(decimal));
dtOrderHeader.Columns.Add("OrderDate", typeof(DateTime));
dtOrderHeader.Columns.Add("PriceCode", typeof(int));
dtOrderHeader.PrimaryKey = new[] { dtOrderHeader.Columns["ID"] };
dtOrderDetail.Columns.Add("OrderId", typeof(int));
dtOrderDetail.Columns.Add("Quantity", typeof(decimal));
////Set up a master-detail relationship between the DataTables
DataColumn keyOrderHeaderColumn = dsOrders.Tables["dtOrderHeader"].Columns["ID"];
DataColumn foreignKeyOrderDetailColumn = dsOrders.Tables["dtOrderDetail"].Columns["OrderId"];
var rel = dsOrders.Relations.Add("rOrders", keyOrderHeaderColumn, foreignKeyOrderDetailColumn);
bsOrderHeader.DataSource = dsOrders;
bsOrderHeader.DataMember = "dtOrderHeader";
bsOrderDetail.DataSource = bsOrderHeader;
bsOrderDetail.DataMember = "rOrders";
var splitView = new SplitContainer { Dock = DockStyle.Fill, Parent = form };
var tbxOrderNo = new TextBox();
var tbxCustomer = new TextBox();
var tbxTaxRate = new TextBox();
var tbxShipping = new TextBox();
var tbxExchangeRate = new TextBox();
var dtpOrderDate = new DateTimePicker();
int y = 8;
foreach (var c in new Control[] { tbxOrderNo, tbxCustomer, tbxTaxRate, tbxShipping, tbxExchangeRate, dtpOrderDate })
{
c.Top = y;
c.Left = 16;
splitView.Panel1.Controls.Add(c);
y = c.Bottom + 8;
}
var dgvItems = new DataGridView { Dock = DockStyle.Fill, Parent = splitView.Panel2 };
tbxOrderNo.DataBindings.Add("Text", bsOrderHeader, "ID");
tbxCustomer.DataBindings.Add("Text", bsOrderHeader, "Name");
tbxTaxRate.DataBindings.Add("Text", bsOrderHeader, "TaxRate");
tbxShipping.DataBindings.Add("Text", bsOrderHeader, "Shipping");
tbxExchangeRate.DataBindings.Add("Text", bsOrderHeader, "ExchangeRate");
dtpOrderDate.DataBindings.Add("Text", bsOrderHeader, "OrderDate");
//cbxPriceCode.DataBindings.Add("SelectedIndex", bsOrderHeader, "PriceCode");
dgvItems.DataSource = bsOrderDetail;
Func<DataRow> addOrder = () =>
{
var maxOrderId = dsOrders.Tables["dtOrderHeader"].Compute("max(ID)", string.Empty);
int newOrderId = (maxOrderId != null && maxOrderId != DBNull.Value ? Convert.ToInt32(maxOrderId) : 0) + 1;
DataRow drOrderHeader = dsOrders.Tables["dtOrderHeader"].NewRow();
drOrderHeader["ID"] = newOrderId;
dsOrders.Tables["dtOrderHeader"].Rows.Add(drOrderHeader);
DataRow drOrderDetail = dsOrders.Tables["dtOrderDetail"].NewRow();
drOrderDetail["OrderId"] = newOrderId;
dsOrders.Tables["dtOrderDetail"].Rows.Add(drOrderDetail);
return drOrderHeader;
};
for (int i = 0; i < 5; i++) addOrder();
var addButton = new Button { Dock = DockStyle.Bottom, Parent = form, Text = "Add" };
addButton.Click += (sender, e) =>
{
var drOrderHeader = addOrder();
bsOrderHeader.Position = bsOrderHeader.Find("ID", drOrderHeader["ID"]);
};
Application.Run(form);
}
}
}
Related
I'm trying to allow the user to upload existing information to a data table. Here's what the code is for the upload:
private void label30_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
StreamReader read = new StreamReader(File.OpenRead(ofd.FileName));
string[] lines = File.ReadAllLines(ofd.FileName);
string[] values;
for (int i = 31; i < lines.Length; i++)
{
values = lines[i].ToString().Split(',');
string[] row = new string[values.Length];
for (int j = 0; j < values.Length; j++)
{
row[j] = values[j].Trim();
}
stkSheetdgv.Rows.Add(row);
read.Close();
read.Dispose();
}
}
}
There is a lot of other information inside the text file, hence the reason int i = 31. I'm getting System.InvalidOperationException: 'Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound.'
Can someone tell me what is missing to make this work? I can add more code for the dt if necessary. Let me know.
stkSheetdgv.AutoGenerateColumns = false;
DataTable dt = new DataTable();
dt.Columns.Add("Wire", typeof(String));
dt.Columns.Add("Pole", typeof(String));
dt.Columns.Add("Primary Unit", typeof(String));
dt.Columns.Add("Down Guy", typeof(String));
dt.Columns.Add("Anchor", typeof(String));
dt.Columns.Add("Transformer", typeof(String));
dt.Columns.Add("Ground", typeof(String));
dt.Columns.Add("Riser/Disconnect", typeof(String));
dt.Columns.Add("Misc.", typeof(String));
DataGridViewComboBoxColumn wire = new DataGridViewComboBoxColumn();
var wirelist = new List<string>() {"AAC 1/0 POPPY", "AAC 477 COSMOS"};
wire.DataSource = wirelist;
wire.HeaderText = "Wire";
wire.DataPropertyName = "Wire";
DataGridViewComboBoxColumn pole = new DataGridViewComboBoxColumn();
var polelist = new List<string>() {"30C4 CCA", "30C5 CCA"};
pole.DataSource = polelist;
pole.HeaderText = "Pole";
pole.DataPropertyName = "Pole";
DataGridViewComboBoxColumn primary = new DataGridViewComboBoxColumn();
var primarylist = new List<string>() {"C1-LD", "C2-LD"};
primary.DataSource = primarylist;
primary.HeaderText = "Primary Unit";
primary.DataPropertyName = "Primary Unit";
DataGridViewComboBoxColumn down = new DataGridViewComboBoxColumn();
var downlist = new List<string>() {"E1-5-144", "E1-5-144P"};
down.DataSource = downlist;
down.HeaderText = "Down Guy";
down.DataPropertyName = "Down Guy";
DataGridViewComboBoxColumn anch = new DataGridViewComboBoxColumn();
var anchlist = new List<string>() {"F1-2 Expansion", "F1-2 Plate"};
anch.DataSource = anchlist;
anch.HeaderText = "Anchor";
anch.DataPropertyName = "Anchor";
DataGridViewComboBoxColumn trans = new DataGridViewComboBoxColumn();
var translist = new List<string>() { "G311-15kVA", "G311-25kVA"};
trans.DataSource = translist;
trans.HeaderText = "Transformer";
trans.DataPropertyName = "Transformer";
DataGridViewComboBoxColumn ground = new DataGridViewComboBoxColumn();
var groundlist = new List<string>() { "M2-12", "M2-11" };
ground.DataSource = groundlist;
ground.HeaderText = "Ground";
ground.DataPropertyName = "Ground";
DataGridViewComboBoxColumn rise = new DataGridViewComboBoxColumn();
var riserlist = new List<string>() { "M8-60-10'", "M8-100-10'"};
rise.DataSource = riserlist;
rise.HeaderText = "Riser/Disconnect";
rise.DataPropertyName = "Riser/Disconnect";
DataGridViewComboBoxColumn misc = new DataGridViewComboBoxColumn();
var misclist = new List<string>() { "M5-5LD", "M5-5HD"};
misc.DataSource = misclist;
misc.HeaderText = "Misc.";
misc.DataPropertyName = "Misc.";
stkSheetdgv.DataSource = dt;
stkSheetdgv.Columns.AddRange(wire, pole, primary, down, anch, trans, ground, rise, misc);
stkSheetdgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
There are two different ways to resolve this issue, either by removing the assignment of the DataSource (e.g. remove stkSheetdgv.DataSource = dt;) on the DataGridView, or adding to the DataSource explicitly:
Code
private void label30_Click(object sender, EventArgs e)
{
DataTable dataTable = (DataTable)stkSheetdgv.DataSource;
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
using (StreamReader read = new StreamReader(File.OpenRead(ofd.FileName)))
{
string[] lines = File.ReadAllLines(ofd.FileName);
string[] values;
for (int i = 31; i < lines.Length; i++)
{
values = lines[i].ToString().Split(',');
var dataRow = dataTable.NewRow();
for (int j = 0; j < values.Length; j++)
{
dataRow[j] = values[j].Trim();
}
dataTable.Rows.Add(dataRow);
dataTable.AcceptChanges();
}
}
}
}
Edit
The reason that you're receiving the error is due to the csv having more columns than specified on the DataTable. The CSV file that I used was:
AAC 1/0 POPPY,30C4 CCA,C1-LD,E1-5-144,F1-2 Expansion,G311-15kVA,M2-12,M8-60-10',M5-5LD
AAC 477 COSMOS,30C5 CCA,C2-LD,E1-5-144P,F1-2 Plate,G311-25kVA,M2-11,M8-100-10',M5-5HD
Which worked perfectly fine for me.
Hello I am searching for a graph that will allow me to make Z Line Graph control now I have X (Payment_Date) & Y (collected) line how to make Z (username) line
I want every username to have his line alone
Can someone help me?
I want to make the program on normal C# win form application
My Form
SqlDataReader reader = sqlcomm.ExecuteReader();
DataTable sd = new DataTable();
sd.Load(reader);
dataGridView1.DataSource = sd;
reader.Close();
chart1.Series["collected"].XValueMember = "Payment_Date";
chart1.Series["collected"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Date;
chart1.Series["collected"].YValueMembers = "collected";
chart1.Series["collected"].YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
chart1.DataSource = sd;
chart1.DataBind();
Step one: Create a separate BindingSource for each element you want to bind and set an appropriate Filter so it will only display the data of one user each.
Step two: Show and hide the Series you want to.
I have created a DataTable dt with 3 columns:
dt.Columns.Add("User", typeof(string));
dt.Columns.Add("Date", typeof(DateTime));
dt.Columns.Add("Value", typeof(double));
and filled it randomly. Next I pull out the distinct users:
var users = dt.Rows.Cast<DataRow>()
.Select(x => x.Field<string>("User"))
.Distinct()
.OrderBy(x => x)
.ToList();
Most likely you will reverse this and start with a list of users.
Now I create a Series with its own filtered BindingSource for each user:
for (int i = 0; i < users.Count; i++)
{
Series s = chart1.Series.Add(users[i]);
s.ChartType = SeriesChartType.Line;
BindingSource bs = new BindingSource();
bs.DataSource = dt;
bs.Filter = "User='" + users[i] + "'";
s.Points.DataBindXY(bs, "Date", bs, "Value");
}
Now I bind my DGV:
BindingSource bsdgv = new BindingSource();
bsdgv.DataSource = dt;
bsdgv.Filter = "";
dataGridView1.DataSource = bsdgv;
I have hidden the Series by making them Transparent. This way the names still show in the Legend. The trick is to keep the original colors in the Series' Tags..:
void hideAllSeries(Chart chart)
{
chart.ApplyPaletteColors();
foreach (Series s in chart.Series)
{
if (s.Color != Color.Transparent) s.Tag = s.Color;
s.Color = Color.Transparent;
}
}
To show or hide a Series I code the MouseClick event:
private void Chart1_MouseClick(object sender, MouseEventArgs e)
{
var hitt = chart1.HitTest(e.X, e.Y);
if (hitt.ChartElementType == ChartElementType.LegendItem)
{
Series s = hitt.Series;
if (s.Color == Color.Transparent)
{
s.Color = (Color)s.Tag;
}
else
{
s.Tag = s.Color;
s.Color = Color.Transparent;
}
}
}
Let's see it at work:
Instead of this solution you may want to add a User selection Checklist and set the DGV's BindingSource's Filter..
I've two comboboxes in a form, the first combobox display each "mark" of truck and second may display the models of each "mark". For example if mark is volvo the second combobox may display "FH, FH6 ...", otherwise if mark is "MAN" the second combobox may display "TGX, TGX 38" ...
I've try to do a cycle to refesh my second combobox, each time, that the first combobox is changed.
while (comboBox1.SelectedIndex == -1)
{
comboBox2.Items.Clear();
if (comboBox1.SelectedIndex == 1)
{
comboBox2.DisplayMember = "Modelo";
comboBox2.DataSource = d.Tables["Modelos-MAN"].DefaultView;
}
if (comboBox1.SelectedIndex == 2)
{
comboBox2.DisplayMember = "ModeloV";
comboBox2.DataSource = d.Tables["Modelos-VOLVO"].DefaultView;
comboBox2.Refresh();
}
}
if (comboBox1.SelectedIndex==1)
{
comboBox2.DisplayMember = "Modelo";
comboBox2.DataSource = d.Tables["Modelos-MAN"].DefaultView;
}
if(comboBox1.SelectedIndex == 2)
{
comboBox2.DisplayMember = "ModeloV";
comboBox2.DataSource = d.Tables["Modelos-VOLVO"].DefaultView;
comboBox2.Refresh();
}
Happens that if i select "MAN", it shows "MAN" models and then if i change, and select "Volvo", the second combobox show me "System.Data.DataRowView".
What is happening?How i can solve this?
Note: I've also, a method to generate truck "mark" and "models":
public DataSet LoadMarkAndModel()
{
var marcas = new string[6];
marcas[0] = "Marca ...";
marcas[1] = "MAN";
marcas[2] = "Volvo";//Linha 2 coluna 2
marcas[3] = "DAF";
marcas[4] = "Renault";//Linha 4 coluna 2
marcas[5] = "Mercedes-Benz";
var modelosMAN = new string[7];
var modelosVolvo = new string[5];
modelosMAN[0] = "TGX";
modelosMAN[1] = "TGX D38";
modelosMAN[2] = "TGX EfficientLine 2";
modelosMAN[3] = "TGS";
modelosMAN[4] = "TGA";
modelosMAN[5] = "TGA-WW";
modelosMAN[6] = "TGM";
modelosVolvo[0] = "FH16";
modelosVolvo[1] = "FH";
modelosVolvo[2] = "FM";
modelosVolvo[3] = "FE";
modelosVolvo[4] = "FL";
DataSet data = new DataSet("DataSetOfTrucks");
DataTable table = new DataTable("Marcas");
DataColumn colunaID = new DataColumn("ID", typeof(int));
DataColumn colunaMarca = new DataColumn("Marca", typeof(string));
//table.Columns.Add("Description", typeof(string));
table.Columns.Add(colunaID);
table.Columns.Add(colunaMarca);
for (int i = 0; i < marcas.Length; i++)
{
DataRow manyMark = table.NewRow();
manyMark["ID"] = i + 1;
manyMark["Marca"] = marcas[i];
table.Rows.Add(manyMark);
}
data.Tables.Add(table);
DataTable modelsTable = new DataTable("Modelos-MAN");
DataColumn modelsIDParent = new DataColumn("IDP", typeof(int));
DataColumn modelo = new DataColumn("Modelo", typeof(string));
modelsTable.Columns.Add(modelsIDParent);
modelsTable.Columns.Add(modelo);
int m = 0;
while (m<modelosMAN.Length)
{
DataRow manyModelsMAN = modelsTable.NewRow();
manyModelsMAN["IDP"] = 1;
manyModelsMAN["Modelo"] = modelosMAN[m];
modelsTable.Rows.Add(manyModelsMAN);
m++;
//if (m > modelosMAN.Length)
// break;
}
data.Tables.Add(modelsTable);
////vOLVO
DataTable modelsTableVOLVO = new DataTable("Modelos-VOLVO");
DataColumn modelsIDParentVOLVO = new DataColumn("IDPVolvo", typeof(int));
DataColumn modeloVolvo = new DataColumn("ModeloV", typeof(string));
modelsTableVOLVO.Columns.Add(modelsIDParentVOLVO);
modelsTableVOLVO.Columns.Add(modeloVolvo);
int count = 0;
while (count < modelosVolvo.Length)
{
DataRow manyModelsVolvo = modelsTableVOLVO.NewRow();
manyModelsVolvo["IDPVolvo"] = 2;
manyModelsVolvo["ModeloV"] = modelosVolvo[count];
modelsTableVOLVO.Rows.Add(manyModelsVolvo);
count++;
}
data.Tables.Add(modelsTableVOLVO);
return data;
}
comboBox1_SelectedIndexChanged()
{
comboBox2.DataSource =null;
if (comboBox1.SelectedIndex==1)
{
comboBox2.DataSource = d.Tables["Modelos-MAN"];
comboBox2.ValueMember="Modelo";
comboBox2.DisplayMember = "Modelo";
}
if(comboBox1.SelectedIndex == 2)
{
comboBox2.DataSource = d.Tables["Modelos-VOLVO"];
comboBox2.DisplayMember = "ModeloV";
comboBox2.ValueMember = "ModeloV";
}
}
Possibly a complex question - but here's hoping
I have a generic data grid on a single form (displays whatever table I want from a dataset) simply by swapping tables in the dataset.
I want to double-click on a given record and display a single-record view of the table data with the currently selected record displayed as default, but with the option to page through and edit/view/delete other records i.e.
I want to automatically create a details view form from a datagrid for a given table at runtime. The form should be dynamically created - displaying the dataset in details view with the option to page through single records using the binding source/binding source navigator.
My goal is to improve efficiency/maintainability of the application - rather than create and manage 10+ forms, I simply want to create and manage I generic details form in the same way as I manage I generic gridview form.
So far I have come up with:
public void CreateDetailsForm(BindingSource bs, int rowClicked)
{
Form detailsForm = new Form();
BindingSource newFormBS = new BindingSource();
//assign binding source for use on new detailsForm
newFormBS = bs;
//assign binding source to datatable
DataTable dt = (DataTable)bs.DataSource;
//create the form fields
CreateFormFields(dt); //not yet implemented
//assign form fields to form
//display form
}
Any help on the following appreciated
Generating and assigning the form fields to the form.
Thanks in advance.
it likes:
Form f=new Form();
TextBox t=new TextBox();//generate the controls u need
t.Text = "test";//set the actual value
t.Location=new Point(10,10);
f.Controls.Add(t);
DialogResult dr=f.ShowDialog();
So far I have got the col names generated on the form as follows
List colNames = GetColumnNames(dt);
int offset=25;
int xPos=50;
int yPos = 10;
foreach (string name in colNames)
{
Label l = new Label();
l.Name = name;
l.Width = 200;
l.Text = name;
TextBox t = new TextBox();
t.Name = name;
t.Width=200;
l.Location = new Point(xPos, yPos );
t.Location = new Point(xPos+250, yPos);
f.Controls.Add(l);
f.Controls.Add(t);
yPos = yPos + offset;
}
//TextBox t = new TextBox();//generate the controls u need
//t.Text = "test";//set the actual value
f.Width = 800;
f.Height = 600;
f.Show();
}
private List<string> GetColumnNames(DataTable table)
{
List<string> lstColNames=new List<string>();
if (table != null)
{
lstColNames=
(from DataColumn col in table.Columns
select col.ColumnName).ToList();
}
return lstColNames;
}
Now trying to work on getting the controls to bind to the binding source!
OK - got it working now - had to take a different approach
Created a single detailsView Form
Created a static class called PassBindingSource with a static property bst for passing binding source from gridview to details form
static class PassBindingSource
{
public static BindingSource bst { get; set; }
}
On the detailsView form added the following code
try{ bs.DataSource = PassBindingSource.bst;
DataTable dt = (DataTable)PassBindingSource.bst.DataSource;
List<string> colNames = GetColumnNames(dt);
int offset = 25;
int xPos = 50;
int yPos = 50;
foreach (string name in colNames)
{
Label l = new Label();
l.Name = name;
l.Width = 200;
l.Text = name;
TextBox t = new TextBox();
t.Name = name;
t.Width = 200;
// BindingOperations.SetBinding(t, t.TextProperty, bs);
//binding operation
t.DataBindings.Add(new Binding("Text", bs, t.Name, true));
l.Location = new Point(xPos, yPos);
t.Location = new Point(xPos + 250, yPos);
this.Controls.Add(l);
this.Controls.Add(t);
yPos = yPos + offset;
// dgDetailsView.DataSource = bs;
}
//move to correct record in binding source
bs.Position = PassBindingSource.currentRecord;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private List<string> GetColumnNames(DataTable table)
{
List<string> lstColNames=new List<string>();
if (table != null)
{
lstColNames=
(from DataColumn col in table.Columns
select col.ColumnName).ToList();
}
return lstColNames;
}
SUMMARY
Now it all works - the detailsView controls generated at run-time wired up correctly to the binding source and the datagrid can call this detailsView any time using any table from the dataset by wiring the double-click event of the gridview with the following code
PassBindingSource.bst= bs;
frmDetailsView nf = new frmDetailsView();
nf.Show();
Hope this helps others. Many thanks to User2354374 for the initial steer.
Design class which is generated by C# :
//
// usepurposeComboBox
//
this.usepurposeComboBox.DataSource = this.usepurposeBindingSource;
this.usepurposeComboBox.DisplayMember = "Name";
this.usepurposeComboBox.FormattingEnabled = true;
this.usepurposeComboBox.Location = new System.Drawing.Point(277, 53);
this.usepurposeComboBox.Name = "usepurposeComboBox";
this.usepurposeComboBox.Size = new System.Drawing.Size(218, 21);
this.usepurposeComboBox.TabIndex = 4;
this.usepurposeComboBox.ValueMember = "id";
//
// usepurposeBindingSource
//
this.usepurposeBindingSource.DataSource = typeof(mydatabaseEntities.usepurpose);
Then I bound the BindingSource (usepurposeBindingSource) to Entities :
usepurposeBindingSource.DataSource = mydatabaseEntities.usepurposes;
And I can not add a new row to usepurposeComboBox because it's been bound. Is there a workaround ?
The shortest way is to add a new row to your dataTable and then bind your comboBox to it something like this:
Company comps = new Company();
//pupulate dataTable with data
DataTable DT = comps.getCompaniesList();
//create a new row
DataRow DR = DT.NewRow();
DR["c_ID"] = 0;
DR["name"] = "Add new Company";
DR["country"] = "IR";
//add new row to data table
DT.Rows.Add(DR);
//Binding DataTable to cxbxCompany
cxbxCompany.DataSource = DT;
cxbxCompany.DisplayMember = "name";
cxbxCompany.ValueMember = "c_ID";
I'm assuming that you want to add for example a first item sometimes called "Choose One" as if you want to live data you should just see where the data comes from and add more items to that "table".
this.usepurposeBindingSource is an object ... why not adding into it before it binds?
if it's a List<T> this will be fine
this.usepurposeBindingSource.Insert(0, new T() {
Name = "Choose one",
Id = ""
});
Then Bind() it...
Remember to validate as it's a string and will not be the object you want
This is working:
List<MyObject> usepurposeBindingSource { get; set; }
private void FillUpData()
{
// Simulating an External Data
if (usepurposeBindingSource == null || usepurposeBindingSource.Count == 0)
{
this.usepurposeBindingSource = new List<MyObject>();
this.usepurposeBindingSource.Add(new MyObject() { Name = "A", ID = 1 });
this.usepurposeBindingSource.Add(new MyObject() { Name = "B", ID = 2 });
this.usepurposeBindingSource.Add(new MyObject() { Name = "C", ID = 3 });
}
}
private void FillUpCombo()
{
FillUpData();
// what you have from design
// comment out the first line
//this.usepurposeComboBox.DataSource = this.usepurposeBindingSource;
this.usepurposeComboBox.DisplayMember = "Name";
this.usepurposeComboBox.FormattingEnabled = true;
this.usepurposeComboBox.Location = new System.Drawing.Point(277, 53);
this.usepurposeComboBox.Name = "usepurposeComboBox";
this.usepurposeComboBox.Size = new System.Drawing.Size(218, 21);
this.usepurposeComboBox.TabIndex = 4;
this.usepurposeComboBox.ValueMember = "id";
// to do in code:
this.usepurposeBindingSource.Insert(0, new MyObject() { Name = "Choose One", ID = 0 });
// bind the data source
this.usepurposeComboBox.DataSource = this.usepurposeBindingSource;
}
The trick is to comment out the DataSource line and do it in your code, inserting one more element into your object that is from your Model
//this.usepurposeComboBox.DataSource = this.usepurposeBindingSource;
The simplest way to do this, is to wrap your BindingSource with some kind of "ViewModel". The new class will return a "complete" list of items - both those provided from the original binding source, as well as those "additional" items.
You can then bind the new wrapper to your combobox.
I wrote an article about this a while back... It's not my finest work, and it's probably a bit outdated, but it should get you there.
I resolved it on my own. I created a new unbound combobox then bind it to a datatable. Not sure if it's the best way but it works for me. Thanks for all of your suggestions. :)
private void FillCombobox()
{
using (mydatabaseEntities mydatabaseEntities = new mydatabaseEntities())
{
List<usepurpose> usepurposes = mydatabaseEntities.usepurposes.ToList();
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("Name");
dt.Rows.Add(-1, "test row");
foreach (usepurpose usepurpose in usepurposes)
{
dt.Rows.Add(usepurpose.id, usepurpose.Name);
}
usepurposeComboBox.ValueMember = dt.Columns[0].ColumnName;
usepurposeComboBox.DisplayMember = dt.Columns[1].ColumnName;
usepurposeComboBox.DataSource = dt;
}
}
Check out this link: http://forums.asp.net/t/1695728.aspx/1?
In asp you can add this to insert an empty line:
<asp:DropDownList ID="CustomerDropDownList" runat="server"
DataSourceID="CustomerEntityDataSource" DataTextField="CustomerId"
DataValueField="CustomerId" AppendDataBoundItems="true">
<asp:ListItem Text="Select All Customers" Value="" />
</asp:DropDownList>
Or in code behind:
DropDownList1.AppendDataBoundItems = true;
DropDownList1.Items.Add(new ListItem("Select All Customers", "-1"));