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?
}
}
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 would like the data from column 2 (all rows) in a DataGridView to be the input for a combobox in another form. The code below that I've tried contains 2 errors comboBox1 does not exist in current context and object reference is required for non-static field. Below is my code.
Form 1 (with a DataGridView and button)
// put as public string as the DataGridView rows will keep updating
public string data;
public Form1()
{
InitializeComponent();
}
//button to go Form 2 which contains the combobox
private void Button1_Click(object sender, EventArgs e)
{
string data = string.Empty;
int indexOfYourColumn = 2;
foreach (DataGridViewRow row in dataGridView1.Rows)
data = row.Cells[indexOfYourColumn].Value.ToString();
comboBox1.Items.Add(data);
this.Hide();
FormsCollection.Form2.Show();
}
Form2 (with combobox)
//put as public to obtain value from Form 1
public string data;
public Form 2()
{
InitializeComponent();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
comboBox1.Text = Form1.data;
//not to repeat the value entered if a particular value has been entered
String s = data;
if (!comboBox1.Items.Contains(s))
{
comboBox1.Items.Add(s);
}
}
When you want to pass a collection of informations you need to use an appropriate type. For example a List<string> not a simple string. Then you create or get the instance of the second form and only after you have an instance of your second form you can give it the collection of data to display
private void Button1_Click(object sender, EventArgs e)
{
// These is where you store the elements to pass to the Form2 instance
List<string> data = new List<string>();;
int indexOfYourColumn = 2;
// Build the collection from the selected column for each row
foreach (DataGridViewRow row in dataGridView1.Rows)
data.Add(row.Cells[indexOfYourColumn].Value.ToString());
this.Hide();
// pass your data to the public property of the Form2 instance
Form2 f = FormsCollection.Form2;
f.Data = data;
f.Show();
}
As you can see the data value is passed to the second instance through a public property and in the set accessor of that property you change the content of the internal combobox1
private List<string> _data;
public List<string> Data
{
get { return _data; }
set
{
_data = value;
// This code uses the DataSource property of the combobox
// combobox1.DataSource = null;
// combobox1.DataSource = value;
// This code works directly with the Items collection of the combo
combobox1.Items.Clear();
foreach(string s in _data)
combobox1.Items.Add(s);
}
};
public Form 2()
{
InitializeComponent();
}
...
First define public parameter for Form2
public String data;
Then in Form1 when opening Form2 set data value like this:
Form2 form2 = new Form2();
form2.data = your_form1_data;
form2.Show();
Now you have data value in Form2.
I am writing a user control. I want it to return a customer number when the user double clicks on the customer. I can't seem to get it to work. The user control is displayed and, on the double click, the data shows in the messagebox but I can't seem to get it to update the value on the main form.
Anytime I try to add a return value into FindCustomerControl_ItemHasBeenSelected, I get an error. It's like it hangs out in the user control and I can't leave it with a return value. So far, this is what I have:
In my main window:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// one close button on the form
Close();
}
private void ShowSelectFromListWidget()
{
// show the user control
var uc = new FindCustomerControl();
uc.ItemHasBeenSelected += uc_ItemHasBeenSelected;
MakeUserControlPrimaryWindow(uc);
}
void uc_ItemHasBeenSelected(object sender,
FindCustomerControl.SelectedItemEventArgs e)
{
// once it has been selected, change a label on the screen to show the customer number
var value = e.SelectedChoice;
lblCustomer.Text = value;
}
}
In my user control:
public partial class FindCustomerControl : UserControl
{
public class SelectedItemEventArgs : EventArgs
{ public string SelectedChoice { get; set; } }
public event EventHandler<SelectedItemEventArgs> ItemHasBeenSelected;
public FindCustomerControl()
{ InitializeComponent();}
DataTable dt = new DataTable();
public void btnFind_Click(object sender, EventArgs e)
{ var dt = GetData();
dataGridView1.DataSource = dt; }
//Query database
public DataTable GetData()
{
UtilitiesClass ru = new UtilitiesClass();
string connectionString = ru.getConnectionString();
DataTable dt = new DataTable();
SqlConnection myConnection = new SqlConnection(connectionString);
myConnection.Open();
SqlCommand cmd = new SqlCommand("FindCustomer", myConnection);
cmd.Parameters.AddWithValue("#customer", txtCustomer.Text.Trim());
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter ta = new SqlDataAdapter(cmd);
ta.Fill(dt);
myConnection.Close();
return (dt);
}
private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
var handler = ItemHasBeenSelected;
string choice = dataGridView1[0, e.RowIndex].Value.ToString();
// this shows it
MessageBox.Show("Chosen: " + e.SelectedChoice);
if (handler != null) handler(this, new SelectedItemEventArgs { SelectedChoice = choice });
// I WOULD LIKE TO RETURN A VALUE HERE
}
}
It seems as though this should be common enough but, in spite of my hours of research and debugging, I have been unable to come up with a solution. I do know that uc.ItemHasBeenSelected += uc_ItemHasBeenSelected; in TestForm doesn't seem to ever get executed because I put a breakpoint there.
As I understood I guess you could use application current resources, where you can save any value you wish - in UWP it is something like this:
Application.Current.Resources["Name"] = customerNumber
Then you can cast this value to desired type:
(int)Application.Current.Resources["Name"]
Now you can use this value wherever you want.
Hope than helped in any way.
There is nothing wrong with the user class. It works fine. The problem is that the TestForm needs to start without the FindCustomerControl on it and instantiate the control within the program. It returns the value into the label or wherever else it needs to. Thanks very much to Brad Rem and this post: Return value from usercontrol after user action
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
ShowSelectFromListWidget();
}
private void button1_Click(object sender, EventArgs e)
{
Close();
}
private void ShowSelectFromListWidget()
{
var uc = new FindCustomerControl();
uc.ItemHasBeenSelected += uc_ItemHasBeenSelected;
this.MakeUserControlPrimaryWindow(uc);
}
void uc_ItemHasBeenSelected(object sender, FindCustomerControl.SelectedItemEventArgs e)
{
var value = e.SelectedChoice;
lblCustomer.Text = value;
lblMerchant.Focus();
//ClosePrimaryUserControl();
}
private void MakeUserControlPrimaryWindow(UserControl uc)
{
// my example just puts in in a panel, but for you
// put your special code here to handle your user control management
panel1.Controls.Add(uc);
}
private void ClosePrimaryUserControl()
{
// put your special code here to handle your user control management
panel1.Controls.Clear();
}
}
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();
}
}