C#, WinForms, DataGridView, DataTable. My form allows the user to enter data into a new row (as opposed to, say, popping up a single record view of the record). When the user clicks my Save button, the click event looks like this:
DataTable dtAddRows = this.SomeFictitiouslyNamedDataSet.SomeFictitiouslyNamedDataTable.GetChanges(DataRowState.Added);
My assumption is that calling GetChanges with that enum is going to get me a set of the rows that were added to the grid at run time. However, it errors out because 1 of the columns "Last_Updated_DT" (which must be displayed and readonly) is a date column that will be populated when we write to the datatable. However, the DataSet has a rule that this column cannot be null.
The problem is, I get an error indicating that the Last_Updated_DT column is actually null (surprise surprise). Its a new row, and the column is readonly in the grid, so of course it is null.
How do I get around this error or stuff a value in that (or those) rows for that column before I actually try to get the added rows?
For the sake of posterity:
private void myGrid_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
{
e.Row.Cells["MyColumnNameGridViewTextBoxColumn1"].Value = String.Empty;
}
Adding that event to my code fixed my issue. I set the value to String.Empty so it wouldnt put a date/time value in the column until after the Save was clicked.
Then, in the Save_Click event, I get the added rows with this code:
DataTable dtAddedRows = this.dsMyDataSet.MyDataTable.GetChanges(DataRowState.Added);
Then, in the TableAdapter.Insert parameter list, I added DateTime.Now for the date parameter.
Eventually call MyTableAdapter.Update method using the DataSet overload.
All fixed.
Hat tip to #KiNeTiC for providing the necessary guidance.
Related
I am working on an application with which a .mdb database can be edited. This database represents part of a network. All of these tables have lots of rules and links with other tables and other files.
I have a TabControl with a tab for each table, each tab has a DataGridView for a table. They are not linked to the database, but rather load the data once and export it back later. My current problem has to do with a table from a group of 3 tables.
The first one contains entries of buildings.
The next one contains records of "Frames" (cabinets) in this building. One of the columns in this table is "Location", which refers to the first table. So I made this column a ComboBoxColumn (in design) with the DataSource set to the DataGridView.Datasource of the first table (in code) and the DisplayMember and ValueMember to the correct column.
The third table is for patchpanels in a frame. Two of the columns refer to the two previous tables. First is a "Location" column, which is equal to the Location column in the previous table. The second is a "Frame" column, which should refer to the previous table. I want this column to be a ComboBoxColumn as well, but this time they should not all be the same. If a patchpanel is in Location A, it can only be in a Frame that is also in Location A.
I made a class for each table and record, each table class contains a BindingList<Record>. I have a function UpdatePatchpanelFrames(int row) in the PatchpanelTable class that sets the ComboBox.DataSource of a certain row in the datagrid to a List<string>, which contains all frames in the location specified in the location column (see below). For now, I added this function to the records.ListChanged event of the binding list in the table's constructor. So this event causes an update of the ComboBox in the row with index e.NewIndex.
What I expect is that when the records are added from the .mdb database (in order, so first, the buildings are loaded, then frames, and then patchpanels), the UpdatePatchpanelFrames is called for each newly added record/row and that the ComboBox's DataSource is set correctly. If I print the ComboBox's items this indeed happens.
What I didn't expect is that the ComboBox will reset, at an for me unknown moment and unknown reason, to the column's default. The default list of items is empty, so it causes an error because the value in the cell is not part of the ComboBox's items.
What I tried is settings the ComboBoxes again using the DataGridView.VisibleChanged event, which works. By printing the items before and after calling UpdatePatchpanelFrames again, I confirmed that the ComboBoxes' lists of items are indeed reset to the default list of items. But this causes an update each time the datagrid becomes visible which would require an additional bool and check if it's the first time it's visible and I really dislike this workaround as it, imo, shouldn't be necessary. And I don't like having to change code in two places if want to change one column.
I also made my own DataGridViewCustomComboBoxColumn and -Cell. In the Cell I override the DataSource and only call base.set if the value is not equal to its OwningColumn DataSource which caused the base.set to be called exactly once for each row, but it still didn't work so it somehow reset without setting the Cell's DataSource.
In the program's constructor, InitializeComponent() is called first, then all table objects are constructed and after that, all data is loaded in order.
I'd like to know how I can either prevent the comboboxes from resetting or if a different approach would solve the problem.
Thanks in advance!
Here is the code of the UpdatePatchpanelFrames function(s):
private void UpdatePatchpanelFrames(object sender, ListChangedEventArgs e)
{
if (e.NewIndex >= 0 && e.NewIndex < records.Count)
UpdatePatchpanelFrames(e.NewIndex);
}
public void UpdatePatchpanelFrames(int row)
{
List<string> data = new List<string>();
PatchpanelRecord record = records[row];
if (!string.IsNullOrWhiteSpace(record.LOCATION))
data.AddRange(DatabaseController.Instance.FrameTable.Frames(record.LOCATION));
if (!data.Contains(record.FRAME))
record.FRAME = string.Empty;
(dataGridView.Rows[row].Cells[2] as DataGridViewComboBoxCell).DataSource = data;
}
How I checked (and reset) the comboboxes with the VisibleChanged event:
private void DataGridViewPatchpanel_VisibleChanged(object sender, EventArgs e)
{
if ((sender as DataGridView).Visible)
{
for (int i = 0; i < dataGridViewPatchpanel.RowCount - (dataGridViewPatchpanel.AllowUserToAddRows ? 1 : 0); ++i)
{
Debug.WriteLine("---");
foreach (string item in (dataGridViewPatchpanel.Rows[i].Cells[2] as DataGridViewComboBoxCell).Items)
Debug.WriteLine(item);
Debug.WriteLine(" | ");
DBC.PatchpanelTable.UpdatePatchpanelFrames(i);
Debug.WriteLine(" v ");
foreach (string item in (dataGridViewPatchpanel.Rows[i].Cells[2] as DataGridViewComboBoxCell).Items)
Debug.WriteLine(item);
Debug.WriteLine("---\n");
}
}
}
first time poster. Sorry if the code I post is poorly formatted or hard to interpret.
I am trying to figure out how to update the object property data I have in a class object "AnalogInput" located in a DataGridView that has it's DataSource property set to a DataTable. So, after I edit a cell in the DataGridView, I would like to send that updated value back to the DataTable and thus the AnalogInput property value.
I am trying to do this in a WinForms application.
Below is some code that sums up pretty much where I am at right now:
Create a DataGridView to display some select and editable AnalogInput properties
DataGridView dataGridView_AnalogPoints = new dataGridView();
Create a new instance of a class object "AnalogInput" with public string properties initialized as shown below
AnalogInput point = new AnalogInput() { Name = "Something", Address = "2", Minimum = "-32768", Maximum = "32767"};
Create a new DataTable to hold the desired properties of the object "AnalogInput"
DataTable analogPoints = new DataTable();
Add the columns to the DataTable
analogPoints.Columns.Add("Name");
analogPoints.Columns.Add("Address");
analogPoints.Columns.Add("Minimum");
analogPoints.Columns.Add("Maximum");
Add the object data into a new row in the DataTable analogPoints
analogPoints.Rows.Add(point.Name, point.Address, point.Minimum, point.Maximum);
Set the source of the dataGridView.
dataGridView_AnalogPoints.DataSource = analogPoints;
So, once I edit the DataGridView cell that contains the object data from the DataSource as DataTable, which gets it's data from the AnalogInput object, how can I update it to the newly entered value from the DataGridView? I feel like I'm pretty far off from achieving this.
I only have about 9 months worth of experience working with C# and WinForms so I apologize if my code looks messy. I'm also not sure what I should be searching for specifically to solve this.
I don't think you are very far from achieving your intended behaviour. The DataGridView class has a wonderful event called CellValueChanged. In case you don't know what events are, think of them like a case that a developer believes may happen (like value changed, control clicked, mouse hover etc.) that s/he wants to allow you to subscribe methods to be invoked whenever the case occurs.
What you wish to do is run a function whenever a value of a cell was changed. To subscribe a method to an event you need to write the following line of code:
objectInstanceName.EventName += MethodName; // subscribes a method to be invoked when event occurs
This is not necessarily relevant for your case here, but if you no longer want a method that you have previously subscribed to be invoked, you can unsubscribe it with the following line of code:
objectInstanceName.EventName -= MethodName; // unsubscribes a method to cancel it from being invoked when event occurs
You can also subscribe as many methods as you wish for an event. Note that every event expects the methods subscribed to it have a specified return type, amount of parameters and their type (those are specified by a thing called a delegate).
In order to create a method that matches the expected return type and parameters you can press TAB twice right after typing the += and visual studio will automatically generate a method for you, or write the method's name and then hover on it with the mouse, open the suggestions menu and click "Generate method".
The CellValueChanged event "wants" its subscribed methods to have the following parameters (their names don't matter, what matters is their order and type):
object sender, DataGridViewCellEventArgs e
The parameter sender is the object caused the method to be invoked (i.e your DataGridView) and e is of type DataGridViewCellEventArgs, which has some properties that'll be very helpful for you: RowIndex, and ColumnIndex. Using those parameters you can simply update the values of your DataTable and also your actual AnalogPoint instance in case you assigned it to some object.
I have a data grid view with combo box column in it, i have bind that column to datatable like this:
((grdItems.Columns[1]) as DataGridViewComboBoxColumn).DataSource = dt;
((grdItems.Columns[1]) as DataGridViewComboBoxColumn).DisplayMember = "LocationName";
((grdItems.Columns[1]) as DataGridViewComboBoxColumn).ValueMember = "Id";
now i can get the selected value like this:
var value = grdItems.Rows[0][1].Value;
but the issue is, when i manually add the rows in the gridview (without binding the grid), i cant get the value of the combobox cell.
i am adding rows like this:
grdItems.Rows.Add(1, 1, "Some Value");
when i use
var value = grdItems.Rows[0][1].Value;
this method to get the value, i returns me the text of the cell not the value i.e 1 in this case.
How can i solve this issue? since i want the value of the cell in case of adding rows manually, as well as data-bind rows.
Am assuming that by manually adding rows you mean from the UI , so one way to get the value of the newly added row is to use an event handler for the events raised when you add an row. You may use one of the following
1) RowsAdded https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rowsadded(v=vs.110).aspx 2) RowPrePaint - https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rowprepaint(v=vs.110).aspx - not used this much but is also an option
this.myGridView.RowsAdded += new DataGridViewRowsAddedEventHandler(myGridView_RowsAdded);
private void myGridView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
var newRow = this.myGridView.Rows[e.RowIndex];
}
Edit: Based on the actual requirement stated in the comments - actual requirement being retrieving the newly added row's cell value (Id) corresponding to the combox box column on button save clicked event
Assuming you have bound the combo box to a data source itself , meaning, your
combobox.DataSource=someList
so what you can do is write a linq query
someList.Where( v=>v.Text.Equals("row.Cells[1].Value")).FirstOrDefault().Id
I'm constructing a GUI that allows the user to manipulate XML data. A helpful fellow named Peter was able to point me to a direction leading me to question why my cell values were null even when the user selects a value in the combobox.
I've read up on a few things: the comboboxcolumn's value member, display member, data source, and datapropertyname. I've figured out what display and value members do, but decided to stick with a data source so I wouldn't have to specify a display member and value member; I believe it made things easier and plus the choices in the combobox are stored in a string array.
The premise is, the user enters in choices into a rich text box. Then, the textbox is read line by line and every line becomes a choice in the combo box. I then create a new combobox column in the datagridview and the choices are available for the user to choose. After the user is done, I have a button called "save with combo column..." which creates a new datacolumn in the datatable that the datagridview is displaying and I try to copy the values over via this code: (since I cannot directly merge the combobox column with a datatable)
int size = dataGridView1.Rows.Count - 1;
DataColumn column = new DataColumn(combo.HeaderText);
data_set_array[(int)IndexNumber.Value].Tables[(int)TableNumber.Value].Columns.Add(column);
for (q = 0; q < size; q++)
{
data_set_array[(int)IndexNumber.Value].Tables[(int)TableNumber.Value].Rows[q][combo.HeaderText] = dataGridView1["combo", q].Value;
}
when that is all said and done, the new column is created and a message is displayed. However, the datatable's values are null, which means the cell values are also null. This implies that the item that the user selected in the combo box wasn't copied over to the cell value. What am I doing wrong? I set the datasource to the string of arrays where the strings are the user's choices for the combobox, and yet when the I selected choices from the combobox and tried to save it, the values are null. I also read that I did not need to worry about value member or display member since setting the datasource would provide me valid display text and valid values.
If any additional information is needed please ask. Thanks in advance.
.NET 3.5 SP1, Visual Studio 2008 C#.
Sincerely,
tf.rz
I am not sure
However I think you will find that the datarows have been created using the old column definitions.
when you add a new column i doubt it is resizing the arrays for each row to add a column.
Although if this was true i would expect an index out of range exceptions.
you need to break that big line of code up so so that it is easier to debug.
IE when you leave the loop does the cell have the value you expect? Or has the assign failed?
It will tell you where things are going wrong.
If it is assigning then you lose the data later then you need to expand your search (this could happen if you have somehow assumed a value type was a reference type for instance)
you may finid you need to create a new data table with the correct column definitions and then copy the old data into the new one ... but first you need to establish where it is failing - you are currently doing too much in 1 line of code.
Are you sure the value syou are getting from the combo box are not null?
I have a DataGridView that contains a combo box (DataGridViewComboBoxColumn). This combo box is populated with a set of setup values. These setup values can be inactivated such that only active values are displayed in the combo box, however, existing entries(records) that use the inactive values must still be displayed.
I can successfully loop over the items in the grid and if a record has a value that is no longer active (i.e. part of the DataGridViewComboBoxCell items), I simply add it to the items for that DataGridViewComboBoxCell.
Problem:
I am unable to find a place to put this code such that I do not get the dreaded 'DataGridViewComboBoxCell value is not valid.'
For example, I can put this code into the Paint event of the DataGridView and the grid functions perfectly. No issues with editing/updating values nor do I have any issues in terms of display (inactive value is always shown)... However, it still throws the 'DataGridViewComboBoxCell value is not valid.' error.
Question:
Where can I add code (or how) to add an 'inactive' value after the active values have been added to the combo box (DataGridViewComboBoxColumn), but before the records are bound to avoid getting this error?
What about just catching the datagridview error and doing nothing with it.
void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.Cancel = true;
}
// Add code in user interface
dataGridView1.DataError +=
new DataGridViewDataErrorEventHandler(dataGridView1_DataError);