I have a form (Form1) which contains a grid, bound to a DataSource via a BindingSource.
I then have a button, which once clicked opens another form (Form2) that should let a user enter new data.
I pass the BindingSource from Form1 to Form2, and the goal is that once the user "saves" his input in Form2, it'll be automatically added to Form1.
Is there a way to do this, w/o directly accessing the UI controls?
I.E -
public partial class Form2 : Form
{
BindingSource bs = new BindingSource();
public Form2(BindingSource bindingSourceFromForm1)
{
InitializeComponent();
this.bs = bindingSourceFromForm1;
}
private void button1_Click(object sender, EventArgs e)
{
DataRow dr = (this.bs.DataSource as DataTable).NewRow();
dr["Col1"] = this.textBox1.Text;
dr["Col2"] = this.textBox2.Text;
this.bs.Add(dr);
}
}
Is there a way to bind the controls on Form2 (in the example above textBox1/2) to the BindingSource, and then have it automatically add the values in textBox1 & 2?
Something like calling this.bs.Add(), where Add() knows where to take it's values without me explicitly telling it to go to the textboxes, since it's bound to the aforementioned controls?
Thanks!
If you add a BindingSource to the form designer as you would normally do, set the DataSource to the same source so you can bind the textboxes.
In the specialized constructor the following code adds a new record to the DataSource of form1, assigns the DataSource to the DataSource of the BindingeSource instance of this form and sets the position. Your new Form will enable the user the enter the values in that new object.
public Form2(BindingSource bindingSourceFromForm1)
: this()
{
bindingSourceFromForm1.AddNew();
this.bindingSource1.DataSource = bindingSourceFromForm1.DataSource;
this.bindingSource1.Position = bindingSourceFromForm1.Position;
}
If your user can cancel the operation you have to compensate for that by calling RemoveCurrent on the bindingSourceFromForm1 but I leave that as excersice as it is not clear if you want/need that.
Related
I'd like my button, See Records, when clicked, will open a new form and display my database table through a DataGridView. However, what happens is that when I click See Records button, the new form will appear but the DataGridView won't display my database table.
Here's the code of my form4:
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
SqlConnection con;
con = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Kim\Documents\SMEMCO.mdf;Integrated Security=True");
con.Open();
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM LoanRecord", con);
DataTable dtbl = new DataTable();
da.Fill(dtbl);
dataGridView1.DataSource = dtbl;
}
The See Records button was from form2. Here's my code for See Records:
private void button3_Click(object sender, EventArgs e)
{
Form4 f4 = new Form4();
f4.Show();
}
This is the DataGridView
This is my form2
I would suggest that you shift the data loading into the constructor of Form4. Like this:
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
InitDataGrid();
}
private void InitDataGrid()
{
var con = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Kim\Documents\SMEMCO.mdf;Integrated Security=True");
con.Open();
var da = new SqlDataAdapter("SELECT * FROM LoanRecord", con);
var dtbl = new DataTable();
da.Fill(dtbl);
dataGridView1.DataSource = dtbl;
}
}
The event of your DataGridView1 won't be raised because you don't have any cells. So the data loading can't be triggered.
There are a few ways to make your life so much easier than it has been so far.. chiefly swapping out how you get your data out of the db and onto a form.
Personally for this I would:
Start a whole new project (these instructions will take less time to follow than it will me to write them, the new project means you don't wreck anything existing and you learn how it's done so you can apply the knowledge to your current app)
Add a new DataSet type item to the project
open it and right click anywhere, choose Add TableAdapter
fill out the connection string, choose "query that returns rows"
write SELECT * FROM LoanRecord
call it FillAll
finish
go to form 1's designer
open the data sources window (view menu, other windows)
expand all the nodes of the DataSet
drag the LoanRecords node onto the form
run the app; should see all the table contents in the grid
quit the app, make a new form
open the designer for the new form
in the data sources window drop the LoanRecords combo down and change the setting to Details
drag the node to the form, note a bunch of textboxes etc have appeared
click the bindingsource on form2 and set its modifier to public
switch to code view of form 1, and add a class level variable for a form 2 eg private Form2 x;
switch to the code view of form 2
modify the constructor so that it accepts a DataSet parameter called ds
after the initcomponent call in form2's constructor, set the loanRecordBindingsource.DataSource = ds equals the name of the DataSet
after the initcomponent in form1's constructor create a new form2 passing the DataSet that form1 uses: x = new Form2(this.yourdatasetnamehere); and also add a call in somewhere to show this form2
form1 and 2 now share the same data model
double click the bindingsource on form1 and in its CurrentChanged event handler write: `x.loanRecordBindingSource.Filter = $"[ID] = '{(this.loanRecordBindingSource.Current as DataRowView)["ID"]}'";
Now whenever you choose a record in the grid of form 1, form 2's controls will automatically refine to show the selected record on form 1 and typing anything into the controls on form2 will cause the change to be reflected on form 1
You can take the same approach with your current code if you want to; the datatable you fill and set as the DataSource for your grid, you can pass that to your second form and your second form can create a dataview that sits on top of it and you can bind all your controls to the view, then when you change the current record in the main form you can set the rowfilter of the dataview in form2.. but agh, just having to manually write out all the binding code is sooo tedious the thought of it makes me want to stick pins in my eyes! :)
Life is much easier if you use a typed DataSet, a pair of bindingsources (can even use one if you want to pass it between the forms so they share, but it's a lot more tedious wiring the controls up to a passed in bs than using the filter) looking at the same data model and then you can move them independently, or filter one so it only sees a subset of the other
In the form1 i create a datagridview that fill rows with database.
in the form2 i want to show the user the specific row of datagridview from form1.
specific row is chosen by user !!!
i set my datagridview in public modifier and that is my form 2 code:
form1 parentsell = new form1();
string selecteduser = parentsell.propertydatagrid.Rows[selectedrowindex].Cells[72].Value.ToString();
MessageBox.Show(selecteduser);
thats no error when program is build but after run that give me an Err.
So, you probably using lookup Form, you need to pass value by reference, and easiest way is to crate class User like this:
public Class User
{
public string name {get; set;}
}
Then you can make instance of that object in main Form, like this
User user = new User();
form1 parentsell = new form1(user);
parentsell.ShowDialog(); // This will open another form and wait you to finish work
MessageBox.Show(user.name);
In form1 you need to create another constructor and instance of User
User user;
public form1(User user)
{
InitializeComponent();
this.user = user;
}
Now you need some button that will say "Ok, I found this user, now take him and close this form" like this:
private void button1_Click(object Sender, EventArgs e)
{
user.name = ...; //find your user, DGV.selectedRows[0].Cells[somehing].Value.ToString(); I think it's like this...
this.Close();
}
And you should get your selected user in Main form
A very simple approach:
Add this line to the other form(e.g. Form2) where you want to access the datagridview:
public static DataGridView view { get; set; }
Then go to the form(e.g. Form1) that contains the DataGridView(e.g. dataGridView1) that you want to access.
Add this line to that form:
foreach(DataGridViewRow row in dataGridView1.Rows)
{
Form2.view.Rows.Add(row.Cells["Column1"].Value);
Form2.view.Rows.Add(row.Cells["Column2"].Value);
}
Add a textbox(e.g. textBox1) for the user to add the index of the required row and get it in a new datagridview(e.g. dataGridView2) in this way:
dataGridView2.Rows.Clear();
dataGridView2.Rows.Add(view.Rows[Convert.ToInt32(textBox1.Text)].Cells["Column1"].Value, view.Rows[Convert.ToInt32(textBox1.Text)].Cells["Column2"].Value)
Remember that a datagridview row index starts from '0'.
Also remember to add two columns to the dataGridView2 before running the code.
You can also directly call data from the database to the required form instead of fetching it from another datagridview.
How can I add rows to a data grid view using a separate form? When I'm trying to pass it like this
private void btnDodajPrzychod_Click(object sender, EventArgs e)
{
formMain.gridPrzychody.Rows.Add("123");
this.Close();
}
it says that an object reference is required for the non-static field method or property "formMain.gridPrzychody".
How can I fix it? Thanks
When you calling the new Form, you must pass a handler to access the gridView from previous form. the handler could be the first form object or the gridview object.
for example:
FormNew fm = new FormNew(this.gridPrzychody);
then in the new form get and store the handler. when ever you want, you can use it then.
GridView gridView;
public FormNew(GridView gridView)
{
this.gridView = gridView;
}
I have a DataGridView binded with data from a database. I need to create a Form, which has input fields for data from a single grid row.
The Form has 30+ input controls - TextBoxes, Checkboxes and NumericUpDowns.
For now I went with this approach:
Retrieve current row from the DataGridView and load values from the cells to class instance
Pass the instance to the form and manually fill the input controls
Update the database from the form, update the DataGridView
I want to improve some things:
Is there any way to quickly fill all input controls from a class instance?
Is there any way to determine which input controls have changed their values besides manually subscribing every control on an event handler?
Is there any way to improve this whole thing, e.g. do something more efficiently?
If you are already passing in a DataRow, then you could instead pass in the DataTable and something that identifies the row in that table. And maybe optionally an adapter, if you want to commit the changes immediately on form exit. Then you can create a DataView of that table. And bind each edit control to a field in that view. Something like this:
public partial class EditForm : Form
{
DataRow row = null;
DataView view;
SqlDataAdapter adapter;
public EditForm(SqlDataAdapter adapter, DataTable table, int rowId)
{
InitializeComponent();
this.adapter = adapter;
view = table.DefaultView;
view.RowFilter = $"ID = {rowId}";
if (view.Count == 0) throw new Exception("no such row");
DataRowView dvr = view[0];
row = dvr.Row;
datebox.DataBindings.Add(new Binding("Value", view, "DATE"));
stringbox.DataBindings.Add(new Binding("Text", view, "O_STRING"));
this.FormClosing += EditForm_FormClosing;
}
private void EditForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (row.RowState == DataRowState.Modified) adapter.Update(new DataRow[] { row });
}
}
The above assuming that your table has key column named ID and fields DATE and O_STRING.
This will save you the trouble of creating an intermediate custom class instance, based on that row, moving values to and from various objects and automatically sets the RowStatae in the original table.
Re: value changed indicators. Not sure if there is a really elegant way to do that. Firstly, if I had to, I would change the background (or foreground) color rather than font boldness. Setting font to bold would change the width of the content and that is usually quite annoying. Then, I would add handlers to the TextChanged events (or ValueChange events, for controls that are not text-based). You dont need to write custom handlers to each and every edit control - in the event handler, you get the object sender parameter that points to the control object. Then you can get the field name binded to that control with something like this:
private void stringbox_TextChanged(object sender, EventArgs e)
{
Control ctrl = (Control)sender;
string fieldName = ctrl.DataBindings[0].BindingMemberInfo.BindingMember;
if ((string)view[0].Row[fieldName] != ctrl.Text) ctrl.BackColor = Color.Pink;
}
That way you will only need to add a TextChanged handler once (per edit contol class), not one per every edit box you have.
I am using two grid view in the different form, I want to pass the form1 all cells values
another Form[form2]. form2 data view in one cell. then click the form2 cell load the all data view in form1 cells.
this form1 ->
private void button1_Click(object sender, EventArgs e)
{
string[] colB = new string[dataGridView1.Rows.Count];
for (int i = 0; i < dataGridView1.Rows.Count -1; i++)
{
colB[i] = Convert.ToString(dataGridView1.Rows[i].Cells[0].Value);
}
}
how to get data form2->
?
Forms are classes like any other. However, they have window-specific behaviors they inherit from the Form class.
This means you can specify your own constructor and pass along any information you would like. For instance, let's say we have a Person class we want to show in a new Form. We could define a new PersonForm to show the details (this is just an example. You will be able to adapt it to your needs).
public class PersonForm : Form
{
PersonForm(Person model)
{
InitializeComponents(); //I think this is the name of the method.
//Do whatever is needed here with the model.
}
}
So when a user double clicks the right cell, you create a new instance of the PersonForm and you show it.