To get to my question, I need to do a little explanation first so bear with me.
This application has 2 forms. In the main form, I have a DataGridView. It is displaying data from a database table. Its DataSource is set to a DataTable object. Here is the code of the main form.
using System;
using System.Data;
using DataAccess;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
private SqlDataAccess _dataAccess = new SqlDataAccess(); //SqlDataAccess is a class written to handle database related operations
private DataTable _dataTable = null;
private void Form1_Load(object sender, EventArgs e)
{
string query = #"SELECT * FROM fEmployee";
_dataTable = _dataAccess.GetDataTable(query, null);
dgvEmployees.DataSource = _dataTable;
}
private void dataGridView1_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
//Convert the current selected row in the DataGridView to a DataRow
DataRowView currentDataRowView = (DataRowView)dgvEmployees.CurrentRow.DataBoundItem;
DataRow dataRow = currentDataRowView.Row;
Form2 f = new Form2(dataRow);
f.ShowDialog();
}
}
}
When clicked upon the row headers of the DataGridView, a sub form will appear. This sub form acts as a place to modify the selected rows' field values. A DataRow object containing the selected row's fields is sent to the sub form's overloaded constructor. And in that form's Load event, the data contained in that DataRow will be displayed in multiple Textboxes in the sub form.
The code of the sub form.
using System;
using System.Data;
namespace WindowsFormsApplication3
{
public partial class Form2 : Form
{
private DataRow _employeeDetails = null;
private bool _isDirty = false;
public Form2(DataRow empDetails)
{
InitializeComponent();
_employeeDetails = empDetails;
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form2_Load(object sender, EventArgs e)
{
txtFName.Text = _employeeDetails["FirstName"].ToString();
txtLName.Text = _employeeDetails["LastName"].ToString();
txtAddress.Text = _employeeDetails["Address"].ToString();
txtCity.Text = _employeeDetails["City"].ToString();
txtPostalCode.Text = _employeeDetails["PostalCode"].ToString();
txtCountry.Text = _employeeDetails["Country"].ToString();
dtpDOB.Value = Convert.ToDateTime(_employeeDetails["DOB"]);
txtPhone.Text = _employeeDetails["Phone"].ToString();
txtEmail.Text = _employeeDetails["Email"].ToString();
dtpDOJ.Value = Convert.ToDateTime(_employeeDetails["DOJ"]);
txtBasicSalary.Text = _employeeDetails["BasicSalary"].ToString();
}
private void btnUpdate_Click(object sender, EventArgs e)
{
}
}
}
In the sub form, the user is able to change the values through Textboxes.
Now to my question: How can I reflect the changes done to that particular row in the sub form, in the DataGridView in the main form?
Example - I click on one row header, it opens the sub form and loads the details. I change the First Name. And when I close the sub form, that modified value should be updated in the main DataGridview.
Can anyone give some suggestions on how to do this?
I tried passing the DataRow to the sub form as a reference but that didn't work.
Try to test this approach. You have to use the DataRow[] to update the data from DataRow selected. Please try to figure it out and get some idea.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public BindingSource bs = new BindingSource();
public DataRow[] mainDataRow;
private DataTable employee = new DataTable();
private void MainForm_Load(object sender, EventArgs e)
{
employee.Columns.Add("Id");
employee.Columns.Add("LastName");
employee.Columns.Add("FirstName");
employee.Columns.Add("MiddleName");
object[] emp1 = { "1", "Some1a", "Some1b", "Some1c" };
object[] emp2 = { "2", "Some2a", "Some2b", "Some2c" };
object[] emp3 = { "3", "Some3a", "Some3b", "Some3c" };
object[] emp4 = { "4", "Some4a", "Some4b", "Some4c" };
employee.Rows.Add(emp1);
employee.Rows.Add(emp2);
employee.Rows.Add(emp3);
employee.Rows.Add(emp4);
bs.DataSource = employee;
dataGridView1.DataSource = bs;
}
private void dataGridView1_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
//Convert the current selected row in the DataGridView to a DataRow
DataRowView currentDataRowView = (DataRowView)dataGridView1.CurrentRow.DataBoundItem;
mainDataRow = employee.Select("Id='"+ currentDataRowView[0].ToString() + "'"); //get the primary key id
using (var f = new Form2{ dataRow = mainDataRow, Owner = this })
{
f.ShowDialog();
}
}
}
Then in Form2:
public partial class Form2: Form
{
public Form2()
{
InitializeComponent();
}
public DataRow[] dataRow;
private void Form2_Load(object sender, EventArgs e)
{
var select = dataRow[0];
lastNameTextBox.Text = select[1].ToString();
firstNameTextBox.Text = select[2].ToString();
middleNameTextBox.Text = select[3].ToString();
}
private void button1_Click(object sender, EventArgs e)
{
MainForm m = this.Owner as MainForm;
var updated = dataRow[0];
updated[1] = lastNameTextBox.Text;
updated[2] = firstNameTextBox.Text;
updated[3] = middleNameTextBox.Text;
m.mainDataRow[0] = updated;
Close();
}
}
Related
I'm developing a basic Game Launcher and I'm running into an error as show below... the error states that "Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound."
what can i do to resolve this issue?
private void button1_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.Show();
}
CODE FROM ANOTHER FORM:
MainForm mainForm;
DataTable table;
public Form2(MainForm mf)
{
InitializeComponent();
this.mainForm = mf;
}
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Exe Files (.exe)|*.exe|All Files (*.*)|*.*";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
txtPath.Text = openFileDialog1.FileName;
}
}
private void button2_Click(object sender, EventArgs e)
{
DataTable dataTable = (DataTable)dataGridView1.DataSource;
DataRow drToAdd = dataTable.NewRow();
drToAdd["Game"] = txtTitle;
drToAdd["Path"] = txtPath;
mainForm.dataGridView1.Rows.Add(drToAdd);
dataTable.AcceptChanges();
}
private void AddGame_Load(object sender, EventArgs e)
{
table = new DataTable();
table.Columns.Add("Game", typeof(string));
table.Columns.Add("Path", typeof(string));
dataGridView1.DataSource = table;
dataGridView1.Columns["Path"].Visible = false;
dataGridView1.Columns["Game"].Width = 138;
dataGridView1.ScrollBars = ScrollBars.None;
}
}
Photo of Error
Consider defining an event in the child form that passes intended data to the main form. In the following sample there are two columns pre-defined in the DataGridView with DataPropertyName set for each.
MainForm
Setup a hardwired DataTable in load event which is set to the DataGridView via a BindingSource (which is optional but makes life easier in many way).
In a button click event show the child form along with subscribing to an event for adding rows to the DataGridView.
Code
public partial class MainForm : Form
{
private readonly BindingSource _bindingSource =
new BindingSource();
public MainForm()
{
InitializeComponent();
dataGridView1.AutoGenerateColumns = false;
DataTable table = new DataTable();
table.Columns.Add("FirstName", typeof(string));
table.Columns.Add("LastName", typeof(string));
_bindingSource.DataSource = table;
dataGridView1.DataSource = _bindingSource;
}
private void ShowChildFormButton_Click(object sender, EventArgs e)
{
var childForm = new ChildForm();
childForm.AddPerson += OnAddPerson;
childForm.ShowDialog();
childForm.Dispose();
}
private void OnAddPerson(string firstname, string lastname)
{
((DataTable)_bindingSource.DataSource)
.Rows
.Add(firstname, lastname);
}
}
Child form, pass values to MainForm if both TextBox controls are not empty.
public partial class ChildForm : Form
{
public delegate void OnAdd(string firstName, string lastName);
public event OnAdd AddPerson;
public ChildForm()
{
InitializeComponent();
}
private void PostButton_Click(object sender, EventArgs e)
{
if (
!string.IsNullOrWhiteSpace(FirstNameTextBox.Text) &&
!string.IsNullOrWhiteSpace(LastNameTextBox.Text))
{
AddPerson?.Invoke(FirstNameTextBox.Text, LastNameTextBox.Text);
}
}
}
In my C# windows application from a grid cell double click I am populating a Windows Form which is used to save the payment.So after payment the populated Form should pass the id to the grid and refresh the data in the grid.
Nutshell:I need to implement this by using delegates and events as the grid values are populating from the db.Each time after payment the grid need to be refreshed.ie,Button in the second form should trigger the parent form to refresh datagrid based on the click returned value.
Screenshot attached
Form1
private void dgvLoadBalance_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
long cellValue = long.Parse(dgvLoadBalance.Rows[e.RowIndex].Cells[2].Value.ToString());
OPPaymentDatatable= commonDL.GetOpPaymentDetails(opRegId, cellValue);
opDataTable = new DataTable();
opDataTable.Columns.Add("PatientName", typeof(string));
opDataTable.Columns.Add("DoctorName", typeof(string));
opDataTable.Columns.Add("BillTypeName", typeof(string));
opDataTable.Columns.Add("OPId", typeof(string));
opDataTable.Columns.Add("OPNumber", typeof(string));
opDataTable.Rows.Add(uctrlPatientSearch1.PatientName, uctrlPatientSearch1.DoctorName, uctrlPatientSearch1.BillTypeName, uctrlPatientSearch1.OPId,uctrlPatientSearch1.OPNumber);
var settingsdt = commonBL.GetSettings();
if (settingsdt.Rows.Count > 0 && settingsdt != null)
{
Address.Add(settingsdt.Rows[1][2]);
Address.Add(settingsdt.Rows[2][2]);
Address.Add(settingsdt.Rows[3][2]);
Address.Add(settingsdt.Rows[4][2]);
Address.Add(settingsdt.Rows[5][2]);
Address.Add(settingsdt.Rows[6][2]);
Address.Add(settingsdt.Rows[7][2]);
Address.Add(settingsdt.Rows[8][2]);
}
frmPayOPBalance frmbalancepay = new frmPayOPBalance(OPPaymentDatatable, opDataTable, Address);
frmbalancepay.ShowDialog();
}
Form2
private void btnPaynBill_Click(object sender, EventArgs e)
{
if (ValidateForm())
{
BindData();
outVal = commonBL.InsertOpBalanceHistoryPayment(opPaymentModel);
if (outVal > 0)
{
var dt = commonBL.GetOpBalanceBill(opPaymentModel);
if (dt != null && dt.Rows.Count > 0)
opPaymentModel.BillNumber = long.Parse(dt.Rows[0]["BillId"].ToString());
MessageBox.Show("Balance Payment made successfully", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
particularsDt = new DataTable();
particularsDt.Columns.Add("Particulars", typeof(string));
particularsDt.Columns.Add("Rate", typeof(string));
particularsDt.Columns.Add("Amount", typeof(decimal));
particularsDt.Rows.Add("OP Balance Payment for Reciept # " + opPaymentModel.OldBillId, string.Empty, opPaymentModel.BalAmount);
BalancePaymentBillPrint objBalaPayPrint = new BalancePaymentBillPrint(opPaymentModel, Address, particularsDt);
ClearBindings();
this.Hide();
}
else
MessageBox.Show("Error found in bill entry", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
A simple way is that you can use public properties and Application.OpenForms Property to achieve it.
Here is a demo. In this example, there is a DataGridView with two columns "ID" and "Number".
First, we need to define a property to access the DataGridView instance in Form1.
public DataGridView DGV
{
get { return dataGridView1; }
set { dataGridView1 = value; }
}
Then define properties to get LabelID and TextBoxNumber in Form2.
public Label LBID
{
get { return labelID; }
set { labelID = value; }
}
public TextBox TBNum
{
get { return textBoxNumber; }
set { textBoxNumber = value; }
}
Then pass values to Form2 via related properties.
// DataGridView in Form1
private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
Form2 form2 = new Form2();
form2.LBID.Text = dataGridView1.Rows[e.RowIndex].Cells["ID"].Value.ToString();
form2.TBNum.Text = dataGridView1.Rows[e.RowIndex].Cells["Number"].Value.ToString();
form2.Show();
}
Last, we can use Application.OpenForms Property in Form2 to get the Form1 instance to modify the DataGridview.
private void btnUpdate_Click(object sender, EventArgs e)
{
Form1 form1 = (Form1)Application.OpenForms["Form1"];
form1.DGV.Rows[Convert.ToInt32(labelID.Text) - 1].Cells["Number"].Value = textBoxNumber.Text;
this.Close();
}
Test result:
Update: using delegate and event
Form1.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//event handle method
void frm_TransfEvent(int rowindex, string value)
{
// set Number column value
dataGridView1.Rows[rowindex].Cells["Number"].Value = value;
}
private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
Form2 frm = new Form2();
// pass rowindex to form2
frm.ROWIndex = e.RowIndex;
//subscribe to event
frm.TransfEvent += frm_TransfEvent;
frm.ShowDialog();
}
}
Form2.cs
public delegate void TransfDelegate(int rowindex, string value);
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public event TransfDelegate TransfEvent;
public int ROWIndex { get; set; }
private void btnUpdate_Click(object sender, EventArgs e)
{
//trigger event
TransfEvent(ROWIndex, textBox1.Text);
this.Close();
}
}
I have a WFA, at the moment it has 2 forms.
The first form contains a DataGridView, with it's data source being a list.
When the user clicks a button, this opens up form2. Within form2 I have a bubblesort algorithm which sorts the list by one of it's properties. It then passes the updates list back to form1, and sets the new datasource to the bubbledsorted updated list from form2.
Now this works, the bubblesort works. HOWEVER, the DGV on form1 doesn't update UNLESS I click on individual rows/cells of the DGV. I have to click on each one individually for them to be in their new sorted positions.
Here's the code I have:
FORM1:
//This just shows Form2
private void sortByPriority_Click(object sender, EventArgs e)
{
fm2 = new Form2();
fm2.Show();
}
//This is called by form2 to set the new datasource
public void refreshDataGrid(DataGridView p)
{
dataGridView1.DataSource = null;
dataGridView1.DataSource = p.DataSource;
}
FORM2:
private void sortPriority_Click(object sender, EventArgs e)
{
int temp = 0;
bool tempforComp;
string tempforDate = "";
string tempforDesc = "";
string tempforName = "";
for (int write = 0; write < 10; write++)
{
for (int sort = 0; sort < toDoGen.task.Count - 1; sort++)
{
if (toDoGen.task[sort].Priority > toDoGen.task[sort + 1].Priority)
{
temp = toDoGen.task[sort + 1].Priority;
tempforComp = toDoGen.task[sort + 1].Completed;
tempforDate = toDoGen.task[sort + 1].DateOfCompletion;
tempforDesc = toDoGen.task[sort + 1].Description;
tempforName = toDoGen.task[sort + 1].Name;
toDoGen.task[sort + 1].Priority = toDoGen.task[sort].Priority;
toDoGen.task[sort + 1].Completed = toDoGen.task[sort].Completed;
toDoGen.task[sort + 1].DateOfCompletion = toDoGen.task[sort].DateOfCompletion;
toDoGen.task[sort + 1].Description = toDoGen.task[sort].Description;
toDoGen.task[sort + 1].Name = toDoGen.task[sort].Name;
toDoGen.task[sort].Priority = temp;
toDoGen.task[sort].Completed = tempforComp;
toDoGen.task[sort].DateOfCompletion = tempforDate;
toDoGen.task[sort].Description = tempforDesc;
toDoGen.task[sort].Name = tempforName;
}
}
DataGrid n = new DataGrid();
n.DataSource = toDoGen.task;
refresh();
}
}
private void refresh()
{
fm1 = new Form1();
fm1.refreshDataGrid(n);
}
EDIT: updated and just to be clear - I need to use multiple forms (University Assignment), and nonetheless, it didn't update even when I had the sorting algorithm in Form1.
I'll make a simplified example that doesn't use the BubbleSort algorithm, I hope it helps:
This is the code of Form1 class. I define a StringValue class because of this: How to bind a List<string> to a DataGridView control?
public class StringValue
{
public StringValue(string s)
{
_value = s;
}
public string Value { get { return _value; } set { _value = value; } }
string _value;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = new List<StringValue> {
new StringValue("string1"),
new StringValue("string3"),
new StringValue("string2")
};
}
private void button1_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2(this, dataGridView1.DataSource as List<StringValue>);
form2.Show();
}
public void RefreshGrid(List<StringValue> source)
{
dataGridView1.DataSource = source;
}
}
The button click event opens Form2 and passes to it the grid view datasource. Here's Form2:
public partial class Form2 : Form
{
Form1 _form1;
List<StringValue> _source;
public Form2(Form1 form1, List<StringValue> source)
{
_form1=form1;
_source=source;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_source = _source.OrderBy(x => x.Value).ToList();
_form1.RefreshGrid(_source);
this.Close();
}
}
After the click on the button in Form2 the DataGridView1's data source is refreshed with a sorted version, and Form2 is closed.
I have an event in my Form:
public void filterByType_TextChanged(object sender, EventArgs e)
{
dSearch = new D_Search(this);
dSearch.filterD(); }
which calls a function in another class. What I want to do is that I want to notice in my class which Textbox was altered and do something. So there are multiple functions like the one above and they all call the "filterD()" function in my DSearch class. I tried
if (sender == form1.filterByType_TextChanged)
{ sqlCmd = new SqlCommand("SELECT * FROM" } //SQL Statement
}
datTable = new DataTable();
sqlDatAdapter = new SqlDataAdapter(sqlCmd.CommandText,
connection);
sqlDatAdapter.Fill(datTable);
form1.setDataGrid = datTable;
but he can't find "sender" I also tried to create a new Button within the function in my Form and pass it but it doesn't seem to work.
try this -
In form 1
private void textBox1_TextChanged(object sender, EventArgs e)
{
var dSearch = new D_Search(this);
MessageBox.Show(dSearch.filterD(sender));
}
D_Search class
public class D_Search
{
Form1 frm = null;
public D_Search(Form1 frm1)
{
frm = frm1;
}
public string filterD(object sender)
{
string val = String.Empty;
if (sender == frm.textBox1)
{
val = (sender as TextBox).Text;
}
return val;
}
}
also if you want to access filterByType_TextChanged textbox in other class then change its modifier property to Internal
I'm a beginner in .NET and C#, so I'm stuck with a problem that might be pretty simple to solve(?).
I'm having a Xml-File as DataSource. I managed to display that data in a DataGridView. On double click I open a new Form that serves as edit mask. But how can I pass the DataSet-entry to the edit form and sync it back to the DataGridViews underlying DataSource when I hit the "OK"-Button in the Edit form?
This is what I have so far:
Main Form:
public partial class Main : Form
{
private DataSet dataSet;
private EditForm editForm;
public Main()
{
InitializeComponent();
}
private void readXmlFile(object sender, EventArgs e)
{
// bind DataGridView to Xml file data
this.dataSet = new DataSet();
this.dataSet.ReadXml("data.xml");
DataTableCollection tables = this.dataSet.Tables;
DataView view1 = new DataView(tables[0]);
BindingSource source1 = new BindingSource();
source1.DataSource = view1;
source1.Filter = "type = 'editable'";
gridView.DataSource = source1;
}
private void gridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
DataGridView grid = (DataGridView)sender;
DataRow row = ((DataRowView)grid.Rows[e.RowIndex].DataBoundItem).Row;
this.editForm = new EditForm(row);
editForm.ShowDialog();
}
}
and this is my edit form:
public partial class EditForm : Form
{
DataRow dataRow;
public EditForm(DataRow dataRow)
{
InitializeComponent();
this.dataRow = dataRow;
textBoxHeadline.DataBindings.Add("Text", this.dataRow, "headline");
}
private void buttonOk_Click(object sender, EventArgs e)
{
// update DataGridView and DataSet?
}
}