Okay big brains here's something that's more of a challenge than a requirement. I am a bit stumped. I usually just need a prod in the right direction, so get your prodding sticks ready.
I have a tabcontrol covered in textboxes. I want to perform a check of the contents of all the textboxes during the SelectedIndexChanged event on a listview on the same form. If one of the textboxes has data different from a DataTable row - represented by the ListView Item - I want it to ask if the user would like to keep the change they just made. If nothing has changed I want it to just change the selection.
So obviously I'm comparing the contents of the text boxes against associated columns in the datarow.
I could just brute force the check and do each individual check one at a time. I'd prefer to come up with some clever algorithmic way of cycling through the tabcontrol textboxes and checking the values against the columnar values.
Any suggestions?
EDIT: I like the "cleverly named textboxes" solution below best, although both are good. If no one else has a better idea in the next 14 days the textbox answer gets the green.
Give the textboxes a clever name as in a portion of the name is the column/row name.
Group the textbox controls an loop through them. For each control, get the (portion)name and use that as a reference to your datatable. Check the values.
If I'm understanding you right, you want to avoid comparing every textbox on every change, in favour of just checking the textboxes that are changed, driven by the SelectedIndexChanged event of the ListView control. Is that right?
Well, DataRows and DataTables already have row versioning and rollbacks implemented, so if you bind the text boxes to the underlying row (either by writing events to write back on change/lose focus or by using an automated mechanism to accomplish the same task), then check the RowState property on SelectedIndexChanged. If the RowState is anything other than unchanged, prompt the user to save. If he saves, commit the changes, otherwise reject them.
So, for example, you'd want something like this in your SelectedIndexChanged event handler:
if (row.RowState == DataRowState.Modified) {
// prompt for user input
if (promptResult == PromptResult.Save) {
row.AcceptChanges();
}
else {
row.RejectChanges();
}
}
Related
I'm running into a problem making multiple selections programmatically in a DataGridView (DGV) based on data table values. I have a table with one column "ID", I need to pass these values to another form with a DGV for editing, ID is the first DGV column also, i.e. for every ID in the table, the appropriate DGV row should be selected.
I can loop through the table and DGV fine, but only the last value is selected even though the DGV properties MultiSelect=true ... when I use the form manually, multiple select works fine.
foreach(DataRow dtrow in dt.Rows)
{
string Selection = dtrow["ID"].ToString();
foreach(DataGridViewRow DGVrow in dgview.Rows)
{
if (DGVrow.Cells[0].Value.ToString().Equals(Selection))
{
dgview.CurrentCell = DGVrow.Cells[0];
int cellInx = dgview.CurrentCell.RowIndex;
dgview.Rows[cellInx].Selected = true;
break;
}
}
}
I'm answering my own question because I don't think this is possible on form load. My application has a form that's used to add selections to a project. When the user wants to 'Edit' the project, I need to call this form and make all the previous selections so the user can add/delete.
When using a DataGridView (DGV) with multi-select enabled, at run time, you can 'ctl-click' and select multiple items ... essentially I'm trying to recreate this behavior in code, on form load in 'edit' mode.
After the form is initialized I have a method that loads the DGV from OleDb tables, to test this out I tried putting the following lines after the load method call and as the last lines of the method call:
dgv1.Rows[0].Selected = true;
dgv1.Rows[1].Selected = true;
dgv1.Rows[2].Selected = true;
Obviously, on form load, I was expecting the first 3 rows to be 'selected', but that didn't happen.
However, I remembered a separate issue, I could never get the DGV to load without the first line selected by default ... so I put in a 'refresh' button, that simply calls the 'Load' method again ... the last line of which is: DGV1.Rows[0].Selected = False; With the refresh button the DGV loads without the first line selected.
I was curious if this was a similar situation, so when I added these 3 lines to the end of the load method, nothing happened on form load ... but ... when I hit the 'Refresh' button, the first 3 rows were 'selected'.
I've been researching this for over 3 days and found the following from the DataGridView Project Manager (2006):
https://social.msdn.microsoft.com/Forums/windows/en-US/cf351d44-4a9a-4c80-8d52-4fb349847908/multiple-select-is-not-working-for-datagridview?forum=winformsdatacontrols
Unfortunately there isn’t an easy solution to this as I didn’t look at this scenario and design the grid to handle this well. The main problem stems from the fact that when you mouse down on the grid, the grid sets the current cell property which clears the selection before selecting the next cell/row/column. So, while your line of code selects the row, when you go to select the next row or just move to the next row using the keyboard, the row you selected is cleared. The easiest way to do your scenario is to use custom painting via the RowPrePaint and paint the “selected” rows using the same SelectedBackground color. You can just query the value of the check box cell in the row that you are painting to know if that row is “selected”, but there isn’t any need to set the Selected property to true. The flip side to this is that you’ll have to keep track or enumerate all rows to know all the “selected” row at a given time.
-> mark -> DataGridView Program Manager - 2006
So my plan going forward is to rebuild the DGV to use check boxes, as recommended. I just wanted to put this out there for general information and/or if someone has a better idea. Thanks
Environment: Web Development in Visual Studio
Language: ASP.NET
I'm afraid I already know the answer, but it can't hurt to ask. I have an application that gets data from SAP and can return specific results or a list of possible results for each row of a DataGridView. So the user might enter "abcd" and get "1234" as a result or he might get "1234 or 2345 or 3456" as a result. For each row I would like to have each column where multiple results were returned as a combobox. The rest should stay fixed though, since having the table littered with comboboxes that only have one result would be annoying.
Is there any trick to do this? I know you can "convert" an entire column into a column of comboboxes using DataGridViewComboBoxColumn like in this blog:
http://mytactics.blogspot.de/2014/01/convert-datagridview-column-to-combo-box.html
.Is there any way to have a combobox only for specific cells though? I know I could put a combobox next to each row of the table and let the user choose from that one or I could have a combox in every single row and maybe lock the ones with only one result. I'm not happy about either of those solutions though.
You can try and add a ComboBox to yourDataGridViewCell.Controls and make the label.Visible = false; or remove it. It would be something like:
yourDataGridViewCell.Controls.Clear();
yourDataGridViewCell.Controls.Add(new ComboBox());
yourDataGridViewCell.Controls[0].DataSource = yourList;
//set the value member etc.
I have a data loaded from database into dataGridView, with effect looking like this:
(in final version I would prefer to hide the PatientID column)
What I'm trying to do, is return value of PatientID when user clicks ANYWHERE in the corresponding row (i.e. user clicks "Doe" and value returned is "2"). Could anyone give me a hint how to do this? I don't think there is valueMember property... I was trying Rowindex but that returns value of number of row counting from the top(D'uh?!)
Also, is there a way for user to highlight whole row when clicking on the single cell?
EDIT: Oh God, I've spent few hours late at night to find this, In the morning I gave up and posted here... just to find answer 5 minutes later:
string test = dataGridView1.Rows[e.RowIndex].Cells["PatientID"].FormattedValue.ToString();
Still, that leaves my second question about highlighting whole row.
If the datagridview is bound to some data source (DataView), you can use DataBoundItem property, for example
DGV.CurrentRow.DataBoundItem["PatientID"]
or
DGV.SelectedRows[0].DataBoundItem["PatientID"]
or
DGVUnderlyingBindingSource.Current["PatientID"]
If the DataGridView is bound to a strongly typed data source (e.g. BindingList) then you can use the above like this:
((PatientType)DGV.CurrentRow.DataBoundItem).PatientID
or
((PatientType)DGV.SelectedRows[0].DataBoundItem).PatientID
or
((PatientType)PatientTypeBindingSource.Current).PatientID
About the second part of the question, set the DataGridView's SelectionMode property to FullRowSelect
EDIT
You can't use the solution from your edit if you hide that column. In order to access the value by using .Cells[idx].FormattedValue that value must be visible. But you can use this one even if you hide the column.
For the second question, set selection mode to FullRowSelect and the complete row will be highlighted when the user clicks on it. You can set this attribute in the designer or code:
DataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
The DataGridView control allows you to click the row headers and select the rows (the whole row is highlighted), or use the mouse and click and drag to select multiple rows, etc. to select all of them.
Anyway, I need the rows to stay selected if the user decides to click on a cell somewhere in the DataGridView. So, if the user clicks on a cell in one of the selected rows, the selected rows should stay highlighted. If the user clicks a cell in a different, unselected row, all the currently selected cells should stay highlighted. As a matter of fact, I don't want the row selection to change at all unless they use the row headers.
Is this something that's easy to configure, or am I in for a few days of hacking?
Not easy to configure. Spend a few days with the DataGridView control, and you'll notice that nothing about it is easy to configure, unless the developers happened to decide to expose it as a property. And only the simplest of things are exposed this way, so don't waste too much time looking.
Of course, it is possible. I've spent way too much of my life subclassing the DataGridView control and overriding built-in behavior that strikes me as stupid. But I really don't recommend it to anyone.
More to the point, you should also seriously reconsider whether you even want to make this particular change. I would have no idea what was going on if software started to do that. I'd immediately suspect that my shift key or mouse button were stuck and try banging them unstuck. The next step would be restarting my computer. Overall, not a very positive user experience. This isn't the way that the control behaves for a reason. Do consider very carefully what it actually means semantically for a cell to be highlighted—generally, the implication is that you can change the value of or delete all selected cells at once. If that's not how your implementation is going to work, you probably shouldn't do it in the first place.
I would say its not a good idea to to this and its unwanted behavior from users perspective you will need to teach them that your rows get selected only by row headers not by clicking on them (believe me they will be frustrated). In a way you are refraining the user from selecting the rows at all.
That said, if you are keen to go on this design then you will need to do it yourself. GridView doesn't have any built behaviour for this. You can fiddle with RowChanged events and CanSelect properties & do some overriding.
And then there's a RowHeaderMouseClick event you can utilize for Row Selection from Row Header.
I managed to circumvent this using, er... DataGridView subclassing, sorry.
This can be done cleanly, adding a couple of delegate before and after the internal mechanism that unselect them:
class SimpleDataGridView : DataGridView {
public Action<DataGridViewCellMouseEventArgs> BeforeCellMouseDown;
public Action<DataGridViewCellMouseEventArgs> AfterCellMouseDown;
protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e) {
if(BeforeCellMouseDown != null)
BeforeCellMouseDown(e);
base.OnCellMouseDown(e);
if(AfterCellMouseDown != null)
AfterCellMouseDown(e);
}
}
Then, you can use it this way, in your constructor. Replace "yourCondition" by the way you want to determine wether the selection has to be kept or not.
IEnumerable<DataGridViewRow> sel = null;
dataGridView1.BeforeCellMouseDown =
e => {
if (yourCondition)
sel = dataGridView1.SelectedRows.OfType<DataGridViewRow>();
else
sel = null;
};
dataGridView1.AfterCellMouseDown =
e => {
if(sel != null) {
foreach(var row in sel)
row.Selected = true;
}
};
Hopefully simple, but can't find any such option.
I have a data table -- has say... 10 rows in it. Some fields on the form are bound to the table.columns respectively by name.
On another form that HAS a grid, as I scroll the grid, the detail fields are refreshed as expected since the grid does some magic to trigger the DataTable record changing event.
WITHOUT using a Data Grid, How can I direct the table to go to a specific row for load/display refresh on the form... ex:
DataTable MyTable = new DataTable();
MyTable = GetResultsFromSQL(); // returns the 10 rows
MyTable.LoadTheDataForRow(3);
MyTable.LoadTheDataForRow(7);
MyTable.LoadTheDataForRow(2);
I know I can use a foreach row in the table, but need explicit use as I don't want to go through all rows, but need specificity to specific ones.
I've looked at the LoadDataRow(), but that appears to be for pushing data back to a server. NOT what I want... I just want to have the "Current" row of the table to be of a specific one...
Thanks
After further research, I've found that a FORM based control "BindingSource" (or derivative) allows this, such as a grid. But, obviously, there's something the .Net engine is doing under the hood to ultimately "Load" a given row into something that ultimately triggers back to the "BindingSource"... The DataTable has RowChanging and RowChanged events which appear to be triggered by OnRowChanging / OnRowChanged delegates, but how can we tell the data table which "row" we want it as the active one.
The form controls can do this for their binding sources, but what is really happening under the hood to trigger these OnRowChanging events... I don't want to re-load a data table, rows, etc, just change what is considered the "Active" row, as in a grid, listbox, combobox, etc.
Have you tried:
MyDataTable.Rows[3];
MyDataTable.Rows[7];
MyDataTable.Rows[2];