add row to a dynamic datagridview - c#

i have a windows form where i am trying to add the path of a file usnig folderbrowserDialog
i have this code on at form load
public FileMgmt()
{
InitializeComponent();
//
// Here we create a DataTable with four columns.
//
DataTable table = new DataTable();
table.Columns.Add("Check", typeof(bool));
table.Columns.Add("Path", typeof(string));
table.Columns.Add("Date", typeof(DateTime));
table.Rows.Add(false, "", DateTime.Now);
dataGridView2.DataSource = table;
}
this is the code when i click a button to search for folders and add the path to the above gridview which already has the above row
private void AddPubPath_Button_Click(object sender, EventArgs e)
{
folderBrowserDialog1.ShowDialog();
dataGridView2.Rows.Add(false, folderBrowserDialog1.SelectedPath, DateTime.Now);
}
but i get the following error..
Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound.
please, any suggestions

Since your DataGridView is bound to a DataTable, you'll need to update your DGV through your DT. That's what the error is telling you.
Update your button click code to the following:
private void button1_Click(object sender, EventArgs e) {
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) {
var table = (DataTable)dataGridView2.DataSource;
var newRow = table.NewRow();
newRow["Check"] = false;
newRow["Path"] = folderBrowserDialog1.SelectedPath;
newRow["Date"] = DateTime.Now;
table.Rows.Add(newRow);
}
}
This code gets the DataTable your DGV is bound to, creates an empty new row for the table, populates this new row with data and then finally adds the row to the DataTable.
I've also added code that makes sure the user actually selected a folder with your FolderBrowserDialog before attempting to add the row.
Edit in response to your question about making only the Check column editable
// Make all the columns you don't want editable read only.
table.Columns["Path"].ReadOnly = true;
table.Columns["Date"].ReadOnly = true;

Related

Empty datagridview when passed from anothe form

I am trying to pass my datagridview data in first form to another datagridview in second form. However, when I load the second form, the datagridview is empty. Below is my code:
First Form
public Newdatagridd(List<Newclass> records)
{
InitializeComponent();
Records = records;
foreach (var r in Records)
{
String[] row = { r.column1, r.column2, r.column3, r.column4};
dataGridView1.Rows.Add(row);
}
}
//copy data to second form
private void button1_Click(object sender, EventArgs e)
{
pressed = true;
Newdatagrid ndg = new Newdatagrid(dataGridView1.DataSource);
ndg.Show();
}
Second Form
public Newdatagrid(object dataSource)
{
InitializeComponent();
dataGridView2.DataSource = dataSource;
}
There is no data source for datagridView1 because you manually add rows here.
You need to use something like this
var source = new BindingSource();
source.DataSource = Records;
dataGridView1.DataSource = source;
If you are using VS, then there is a simple way to select the datasource from the object in design mode: Select right top coner DataGrid, than select Chose DataSource, select in Combo "Add Project data source" -> Object -> Next and than chose your object class. After it - use code before for binding

Unable to set Row.Readonly=false with databound DataGridView

I have a dataGridView that is bound to a tableAdaptor, behind this table is a MS SQL server database.
In Form1_Load i have dataGridView1.ReadOnly = true; and in the dataGridView1 controls on the form i have Enable Editing selected.
I then fill the dataGridView at run time here: stockTableAdapter.Fill(sIMSDataSet.Stock);
What i am trying to do, is have a user select any cell within a row and click a button which then allows the user to edit that row.
I have tried various ways to get the row the user currently has selected to be set to ReadOnly=flase but i can't get it to work.
This is what i have tried:
private void button7_Click_1(object sender, EventArgs e)
{
if (dataGridView1.CurrentRow.ReadOnly == true)
{
dataGridView1.CurrentRow.ReadOnly = false;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
}
}
And i have also tried the answer is this question:
unable to set Row.Readonly=false in Datagridview in winforms
The user suggests in his testing he was not able to set the row to ReadOnly=flase on databound dataGridViews. But seeing as the answer is now 6 years old i was hoping there was a way to do it now.
Your question is somewhat confusing in a sense that it is unclear how the grid is initially set up. Looking at the link to the previous question, I am not sure “why” a data bound grid would make a difference. From an overall perspective, it appears odd to make individual rows read only. In other words, if the grid is read only when the form loads, then ALL rows are read only regardless of the data source.
Therefore, instead of setting an “individual” row to read only, I am guessing it “may” be easier to simply set the “whole” grid to read only. This will work if you wire up the grids RowLeave event as the link does.
Example, the whole grid is read only and the user cannot make changes to the cells, the user then clicks the “Edit” button in one of the rows. When this happens, we set the row color for that row the clicked “edit” button is in to visually show the user that the row is ready to edit. Then set the grids read only property to false. Obviously, this is going to allow the user to “edit” ANY cell in the grid at this point, however, wiring up the grids RowLeave event will prevent this if we set the grids read only property back to true.
The code for this is relatively straight forward... Note: the InEditMode1 variable is used to know if a row is being edited and to avoid unnecessarily re-setting the grids read only property when no row is currently being edited.
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) {
if (dataGridView1.Columns[e.ColumnIndex].Name == "Edit") {
dataGridView1.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.GreenYellow;
dataGridView1.ReadOnly = false;
InEditMode1 = true;
}
}
private void dataGridView1_RowLeave(object sender, DataGridViewCellEventArgs e) {
if (InEditMode1) {
dataGridView1.ReadOnly = true;
dataGridView1.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.White;
InEditMode1 = false;
}
}
Dropping a grid onto a new form and using the code below to add the columns and some data should allow for the testing of this.
public void CreateDataGridView() {
dataGridView1.Columns.Add("Id", "Id");
dataGridView1.Columns.Add("Lastname", "Lastname");
dataGridView1.Columns.Add("City", "City");
dataGridView1.Columns.Add(GetBtnColumn());
dataGridView1.Rows.Add("1", "Muller", "Seattle");
dataGridView1.Rows.Add("2", "Arkan", "Austin");
dataGridView1.Rows.Add( "3", "Cooper", "New York");
}
Next is an example using a DataSource for the grid. In this example a DataTable is used as a DataSource to the grid. In addition, the “Edit” Button column is added AFTER the grids DataSource has been set. The forms load event may look like below…
private void Form2_Load(object sender, EventArgs e) {
DataTable GridTable = GetDataTable();
FillDataTable(GridTable);
dataGridView2.DataSource = GridTable;
dataGridView2.Columns.Add(GetBtnColumn());
}
For testing…
public DataTable GetDataTable() {
DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(string));
dt.Columns.Add("LastName", typeof(string));
dt.Columns.Add("City", typeof(string));
return dt;
}
private void FillDataTable(DataTable dt) {
dt.Rows.Add("1", "Muller", "Seattle");
dt.Rows.Add("2", "Arkan", "Austin");
dt.Rows.Add("3", "Cooper", "New York");
}
public DataGridViewButtonColumn GetBtnColumn() {
DataGridViewButtonColumn btnColumn = new DataGridViewButtonColumn();
btnColumn.Name = "Edit";
btnColumn.HeaderText = "Edit";
btnColumn.Text = "Edit";
btnColumn.UseColumnTextForButtonValue = true;
return btnColumn;
}
Note the code for this is the same code in the NON-DataBound grid above.
private void dataGridView2_CellClick(object sender, DataGridViewCellEventArgs e) {
if (dataGridView2.Columns[e.ColumnIndex].Name == "Edit") {
dataGridView2.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.GreenYellow;
dataGridView2.ReadOnly = false;
InEditMode2 = true;
}
}
private void dataGridView2_RowLeave(object sender, DataGridViewCellEventArgs e) {
if (InEditMode2) {
dataGridView2.ReadOnly = true;
dataGridView2.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.White;
InEditMode2 = false;
}
}
I hope this will help.

C# How do I copy from one data grid to another without overwriting my previous selection?

I have 2 datagrid views with one datatable. I am trying to have a button that when clicked it adds the rows from csv_datagridview to optimal_datagridview. The below works however whenever I deselect an entry in csv_datagridview and hit the button again it clears that selection. I would like to have the selection stick each time.
if (selectedRowCount <= 9)
{
List<object> destList = new List<object>();
foreach (DataGridViewRow row in csv_datagridview.SelectedRows)
destList.Add(row.DataBoundItem);
optimaldataGridView.DataSource = destList;
Thank you so much in advance :)
It is unclear what your exact problem is with the little code you show, but from your statement whenever I deselect an entry in csv_datagridview and hit the button again it clears that selection. I am guessing that if nothing is selected, the data in optimaldataGridView clears when you press the add selected button.
I will assume the csv_datagridview is bound to a table. Your posted code shows the creation of new List destList which you fill with the selected rows from the csv_datagridview. Then you set optimaldataGridView data source to the destList. One issue I see in this picture is that as soon as you leave the if (selectedRowCount <= 9) clause… destList will no longer exist. As a data source for a DataGridView on your form, I would think you would want to keep this List global as long as the form is open. Either way... you are not adding the selected rows, you are simply removing the existing rows and then adding what was selected in csv_datagridview.
I hope the code below will help. I created two DataTables, one for each DataGridView. The csv_datagridview data table is filled with some data, the second data table is left empty. Then simply add the selected rows from the csv_datagridview to the optimaldataGridView’s DataTable… Then refresh optimaldataGridView.
DataTable table1;
DataTable table2;
public Form1() {
InitializeComponent();
csv_datagridview.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
optimaldataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
table1 = GetTable("table1");
table2 = GetTable("table2");
FillTable(table1);
csv_datagridview.DataSource = table1;
optimaldataGridView.DataSource = table2;
}
private void button1_Click(object sender, EventArgs e) {
if (csv_datagridview.SelectedRows.Count > 0) {
foreach (DataGridViewRow row in csv_datagridview.SelectedRows) {
DataRowView dr = (DataRowView)row.DataBoundItem;
table2.Rows.Add(dr.Row.ItemArray[0], dr.Row.ItemArray[1], dr.Row.ItemArray[2]);
}
optimaldataGridView.Refresh();
}
}
private DataTable GetTable(string name) {
DataTable table = new DataTable(name);
table.Columns.Add("col1");
table.Columns.Add("col2");
table.Columns.Add("col3");
return table;
}
private void FillTable(DataTable table) {
for (int i = 0; i < 10; i++) {
table.Rows.Add("R" + i + "C0", "R" + i + "C1", "R" + i + "C2");
}
}
Hope this helps.
Your code is working on my side.
Created a DataTable for Datasource of csv_datagridview.
Selected some rows in this grid.
Clicked the button to copy the selected rows to the
optimaldataGridView
The selected rows are still selected.
public Form1()
{
InitializeComponent();
DataTable dt = new DataTable();
dt.ReadXml(Application.StartupPath + #"\test.xml");
csv_datagridview.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
csv_datagridview.DataSource = dt;
}
private void button1_Click(object sender, EventArgs e)
{
List<object> destList = new List<object>();
foreach (DataGridViewRow row in csv_datagridview.SelectedRows)
destList.Add(row.DataBoundItem);
optimaldataGridView.DataSource = destList;
}
Make sure you have no events attached to the grids that may affect the selection.

How to filter DataGridView in my application

I have two grids that I set up using the winforms DataGridView wizard. One is bound to a trips table which is my transactions table and the other is bound to my expenses table. I know a little bit of linq2sql and I now have my expense table inserting with the fk(TripId).
What I want to do is filter the expenses grid based on the tripId. I am already retrieving the TripId PK of the currently selected trip so that part is done. I am just not sure how I would do the filtering considering I am using linq but used the built wizards to bind the tables.
Any help would be appreciated!
Edit: I have gotten this far with bluefeet's help below. The problem now is when I do my filter it just clears the grid instead of filtering based on pk. Here is the full code
private void tripsBindingSource_PositionChanged(object sender, EventArgs e)
{
if (dgvTripGrid.CurrentRow != null)
{
//get selected row index
int index = this.dgvTripGrid.CurrentRow.Index;
//get pk of selected row using index
string cellValue = dgvTripGrid["pkTrips", index].Value.ToString();
//change pk string to int
int pKey = Int32.Parse(cellValue);
//int tripPrimKey = getPkRowTrips();
this.tripExpenseBindingSource.Filter = String.Format("tripNo = {0}",
pKey.ToString());
}
}
Sounds like you want to fill your second datagridview based on the selection in your first datagridview. This one way to do it:
On the Load or Search of my first datagridview, use the event
DataBindingComplete which then populates the second datagridview
based on the id of the record selected in the first datagridview.
Then if the selection in the first datagridview changes, I use the
event on the BindingSource_PositionChanged to repopulate the second
grid.
Code Sample
// this populates the grid.
private void SearchButton_Click(object sender, EventArgs e)
{
// your code to load your grid goes here
}
private void DataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
var drv = datagridview1bindingSource.Current as DataRowView;
if(drv != null)
// your method to load datagridview2 goes here if the selected row is not null
LoadDataGridView2();
}
private void LoadDataGridView2()
{
//populate datagridview2 using the selected row id from datagridview1
}
// finally when the position is changed on the datagridview1 binding source, then re-populate // the datagridview2
private void datagridview2BindingSource_PositionChanged(object sender, EventArgs e)
{
LoadDataGridView2();
}
this is a basic way to populate a second grid based on the selection in the first.
Edit:
Your comment says that you are filling the datagridview with all expenses, so to filter you will want to use the Filter property on the BindingSource for the datagridview. The Filter property allows you to view a subset of the DataSource.
Example from MSDN:
private void PopulateDataViewAndFilter()
{
DataSet set1 = new DataSet();
// Some xml data to populate the DataSet with.
string musicXml =
"<?xml version='1.0' encoding='UTF-8'?>" +
"<music>" +
"<recording><artist>Coldplay</artist><cd>X&Y</cd></recording>" +
"<recording><artist>Dave Matthews</artist><cd>Under the Table and Dreaming</cd></recording>" +
"<recording><artist>Dave Matthews</artist><cd>Live at Red Rocks</cd></recording>" +
"<recording><artist>Natalie Merchant</artist><cd>Tigerlily</cd></recording>" +
"<recording><artist>U2</artist><cd>How to Dismantle an Atomic Bomb</cd></recording>" +
"</music>";
// Read the xml.
StringReader reader = new StringReader(musicXml);
set1.ReadXml(reader);
// Get a DataView of the table contained in the dataset.
DataTableCollection tables = set1.Tables;
DataView view1 = new DataView(tables[0]);
// Create a DataGridView control and add it to the form.
DataGridView datagridview1 = new DataGridView();
datagridview1.AutoGenerateColumns = true;
this.Controls.Add(datagridview1);
// Create a BindingSource and set its DataSource property to
// the DataView.
BindingSource source1 = new BindingSource();
source1.DataSource = view1;
// Set the data source for the DataGridView.
datagridview1.DataSource = source1;
//The Filter string can include Boolean expressions.
source1.Filter = "artist = 'Dave Matthews' OR cd = 'Tigerlily'";
}
I use this type of Filter to show data based on account. For an account, I have a textbox when the user places the account number and I use the TextChanged Event to apply the filter. Then I have a button that is used to remove the Filter from the binding source.
You can apply the same thing to your expense datagridview using the tripid from your first datagridview.

Adding columns to a DataTable bound to a DataGridView does not update the view

I have a DataTable which is populated from a CSV file then, using a DataGridView the data is edited in memory. As far as I understand the programmatic editing of the data should be done on the DataTable where the user editing is done via. the DataGridView.
However when I add columns programmatically to the DataTable, it is not reflected automatically in the DataGridView and I suspect the converse is also true.
How do you keep the two concurrent? I thought the idea of data binding was that this was automatic...
Also adding rows works fine.
SOLVED:
The AutoGeneratedColumns was set to false in the designer code despite being true in the properties dialog (and set explicitly in the code). The initial columns were generated programmatically and so should not have appeared however this was not picked up on since the designer code also continued to generate 'designed in' columns that were originally used for debugging.
Moral: Check the autogenerated code!
In addition to this, see this post and this post
This doesn't sound right. To test it out, I wrote a simple app, that creates a DataTable and adds some data to it.
On the button1.Click it binds the table to the DataGridView.
Then, I added a second button, which when clicked, adds another column to the underlying DataTable.
When I tested it, and I clicked the second button, the grid immedialtey reflected the update.
To test the reverse, I added a third button which pops up a dialog with a DataGridView that gets bound to the same DataTable. At runtime, I then added some values to the first DataGridView, and when I clicked the button to bring up the dialog, the changes were reflected.
My point is, they are supposed to stay concurrent. Mark may be right when he suggested you check if AutoGenerateColumns is set to true. You don't need to call DataBind though, that's only for a DataGridView on the web. Maybe you can post of what you're doing, because this SHOULD work.
How I tested it:
DataTable table = new DataTable();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
table.Columns.Add("Name");
table.Columns.Add("Age", typeof(int));
table.Rows.Add("Alex", 26);
table.Rows.Add("Jim", 36);
table.Rows.Add("Bob", 34);
table.Rows.Add("Mike", 47);
table.Rows.Add("Joe", 61);
this.dataGridView1.DataSource = table;
}
private void button2_Click(object sender, EventArgs e)
{
table.Columns.Add("Height", typeof(int));
foreach (DataRow row in table.Rows)
{
row["Height"] = 100;
}
}
private void button3_Click(object sender, EventArgs e)
{
GridViewer g = new GridViewer { DataSource = table };
g.ShowDialog();
}
public partial class GridViewer : Form //just has a DataGridView on it
{
public GridViewer()
{
InitializeComponent();
}
public object DataSource
{
get { return this.dataGridView1.DataSource; }
set { this.dataGridView1.DataSource = value; }
}
}
Is AutoGenerateColumns set to true? If you are making changes after the initial Binding you'll also have to call DataBind() to rebind the changed datasource. I know this is true for an AJAX callback, I think it is true for a WinForms control PostBack.
Here is another solution which you dont need to assign a "name" to the data grid, so that the data grid can be a template element.
(In my case, data grid is a template element inside TabControl.ContentTemplate)
The key to show the new column (added programmatically after the initial binding) is forcing the datagrid to refresh.
From the answer in Force WPF DataGrid to regenerate itself, Andres suggested setting AutoGenerateColumns from false to true will force datagrid to refresh.
Which means I simply need to:
Bind the AutoGenerateColumns to a property of my object
Set the propery to false before adding column
Set the propery to true after added column.
Here is the code:
XAML:
<DataGrid AutoGenerateColumns="{Binding toggleToRefresh}"
ItemsSource="{Binding dataTable}"
/>
C#:
public class MyTabItem : ObservableObject
{
private DataTable _dataTable = new DataTable();
public DataTable dataTable
{
get { return _dataTable; }
}
private bool _toggleToRefresh = true;
public bool toggleToRefresh
{
get { return _toggleToRefresh; }
set
{
if (_toggleToRefresh != value)
{
_toggleToRefresh = value;
RaisePropertyChanged("toggleToRefresh");
}
}
}
public void addDTColumn()
{
toggleToRefresh = false;
string newColumnName = "x" + dataTable.Columns.Count.ToString();
dataTable.Columns.Add(newColumnName, typeof(double));
foreach (DataRow row in dataTable.Rows)
{
row[newColumnName] = 0.0;
}
toggleToRefresh = true;
}
public void addDTRow()
{
var row = dataTable.NewRow();
foreach (DataColumn col in dataTable.Columns)
{
row[col.ColumnName] = 0.0;
}
dataTable.Rows.Add(row);
}
}
Hope this help :)
I had the same issue and I issued a DataBind(). It's not the silver bullet for everything, but it's what helped me in a few cases. I had to put it in before capturing information through a DataView, after the EditCommand and UpdateCommand events immediately after the EditItemIndex statement,
protected void datalistUWSolutions_EditCommand(object source, DataListCommandEventArgs e)
{
datalistUWSolutions.EditItemIndex = e.Item.ItemIndex;
datalistUWSolutions.DataBind(); // refresh the grid.
}
and
protected void datalistUWSolutions_UpdateCommand(object source, DataListCommandEventArgs e)
{
objDSSolutions.UpdateParameters["Name"].DefaultValue = ((Label)e.Item.FindControl("lblSolutionName")).Text;
objDSSolutions.UpdateParameters["PriorityOrder"].DefaultValue = ((Label)e.Item.FindControl("lblOrder")).Text;
objDSSolutions.UpdateParameters["Value"].DefaultValue = ((TextBox)e.Item.FindControl("txtSolutionValue")).Text;
objDSSolutions.Update();
datalistUWSolutions.EditItemIndex = -1; // Release the edited record
datalistUWSolutions.DataBind(); // Redind the records for refesh the control
}

Categories

Resources