I have a DataGridView where the first column is a DataGridViewCheckBoxColumn. The user checks some checkboxes to indicate which items are to be deleted.
When I hide the form and reload it, I need the DataGridView to remember which checkboxes were checked.
You need to save the changes at least at the point where you close your Form (if you just hide it, why reload then?).
The way I usually work this out is listening to the Dgv's CellEndEdit event:
SomeDataGridView.CellEndEdit += ObjectPropertyChanged
then in the Callback you can get the Object back by using the "DataBoundItem" prop of the Dgv and process / save it however you need:
protected virtual void ObjectPropertyChanged(object sender, DataGridViewVellEventArgs e)
{
var selectedObject = ((DataGridView)sender).Rows[e.RowIndex].DataBoundItem;
//Assuming you stored in a List and each Object has an ID as prop:
var indx = _Objects.IndexOf(_Objects.Where(o => o.ID.Equals(selectedObject.ID)))
_Objects.Remove(indx)
_Objects.Insert(indx, selectedObject)
}
You could also do database update, write to a text-file, save in configuration, ...
Related
I have two datagridviews which display two seperate stored procedures at the same time, both on the same form. I have their "SelectionMode" properties set to "FullRowSelect".
However, I want only one row to be selectable at a time between the two dgvs. So if someone selects a row on dgv A, I want the selected row in dgv B to unhighlight or deactivate. And if someone selects a row in dgv B, I'd like the highlighted row in dgv A to unselect or deactivate. Is there a way to essentially share a
private void datagridview1_SelectionChanged(object sender, EventArgs e)
between two separate datagridviews?
I'm not sure where to begin with this, so I have no code examples. Any assistance with this is appreciated. Thanks!!
When selecting in dataGridView1, call dataGridView2.ClearSelection()
You mentioned a single event handler to handle both, which you can do. And you can write some code to find all other DataGridViews besides the one you clicked, and clear their selections.
private void dataGridView_SelectionChanged(object sender, EventArgs e)
{
var s = (DataGridView)sender;
if (s.SelectedRows.Count > 0)
{
var otherDataGridViews = this.Controls.OfType<DataGridView>().Except(new[] { s });
foreach (var dgv in otherDataGridViews)
{
dgv.ClearSelection();
}
}
}
That will work if the DataGridViews are inside the same container i.e. same form, not in different Panels etc.
You must specify the same handler in the designer or designer code i.e.
//
// dataGridView1
//
...
this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView_SelectionChanged);
//
// dataGridView2
//
...
this.dataGridView2.SelectionChanged += new System.EventHandler(this.dataGridView_SelectionChanged);
How to get the currently edited value when handling the:
public class GridEX // ...
{
// ...
public event ColumnActionEventHandler CellValueChanged;
// ...
};
Trying to obtain the value using:
GridEXCell valueChangedCell = _gridView.CurrentRow.Cells[<desired_column_index>];
object rawValue = valueChangedCell.Value;
// or even with
string rawValue = valueChangedCell.Text;
The only moment at which the valueChangedCell has its value changed is when either the CellUpdated or the UpdatingCell event is fired. But the latter two are fired only in case the user has changed the keyboard input focus to another cell probably for the sake of applying the edited cell's new value. The cell I want to lookup the value of is one containing only a checkbox. I want to perform a given action immediately after the checkbox of a given cell is toggled, not as soon as the user changes the focus e.g. moves to another cell in the table. Saw that in the descriptions of the events some row buffer is mentioned:
[Description("Occurs after changes in a cell are copied into the row's buffer.")]
public event ColumnActionEventHandler CellUpdated;
[Description("Occurs before updating the changes in a cell to the row's buffer")]
public event UpdatingCellEventHandler UpdatingCell;
I presume that the current value of the checkbox is probably kept in some buffer and upon changing the focus the new value is applied to the cell.
Any ideas how to fetch the currently set value of the checkbox upon handling the Janus' GridEX.CellValueChanged?
I fixed the problem adding a method which is firing within the event below: private void
CloseEditMode()
{
gridRubrica.AllowEdit = InheritableBoolean.False;
gridRubrica.AllowEdit = InheritableBoolean.True;
}
private void gridRubrica_CellValueUpdated(object sender, ColumnActionEventArgs e)
{
if (e.Column.Key.Equals("Selected")) { CloseEditMode(); }
}
A little bit late to the party, but since I was having the same difficulty I decided to just flush the pending changes when the Cell Changed is a CheckBox
private void gridEX1_CellChanged(object sender, ColumnActionEventArgs e)
{
if (e.Column.ColumnType == ColumnType.CheckBox)
{
gridEX1.UpdateData(); // Flush any pending changes
}
}
This will trigger the other handler that deals with validation (in my case)
I have two DataGridViews with editable by the user.
When the user select a row in DataGridViewA then DatagridViewB is populated with different data accordingly.
Once edited the current row in DataGridViewA, the user can click on the save button. In case he didn't save and select another row in DataGridViewA, a message for save will be prompted to the user asking him if he want to save before moving to another row. (I have a method that check if there are changes and if yes, prompt a message to the user if he wants to save - yes: Save, No: Restore Data).
The method that checks if there are changes is triggered on DataGridViewA_Leave(). The reason is that I have the information of the DataGridViewA.CurrentRow before leaving the row so I can Save if needed.
The problem is if I am clicking on any other control on my form such as Save button the Save message to the user prompt (because the DataGridViewA.CurrentRow is left). In this case, I do not want the user to get a message.
I found a workaround to fix it but it is not the best way to do it I presume. Before prompting the message, I am checking if the Save button has focus. This check need to be done for all the controls that I don't want to get a Save message when clicking on them.
Is there any better solution to solve this issue?
My code, the record state is Added (user add a row) or Edited (user update some data) :
private void AlternateDGV_RowLeave(object sender, DataGridViewCellEventArgs e)
{
if (IsSaveNeeded)
{
if (SaveButton.Focused) // This is the work around
{
return;
}
if (!WasSavedPerUserRequest())
{
var recordstate = AlternateDGV.CurrentRow.Cells[Glossary.RecordStateName].Value.ToString();
if (recordstate == Glossary.AddedRecordState) // if it was a new row added and the user doesn't want to save
{
AlternateDGV.CurrentRow.Delete();
}
else if (recordstate == Glossary.EditedRecordState) // if it was a row from DB and the user doesn't want to save, refresh with saved data
{
AlternateDGV.CurrentRow.Refresh(_items);
}
}
}
}
In my opinion, you should change something in the method you use to load data and manage the grid.
If you load the data in DataGridViewB using the DataGridViewA.SelectionChanged event, you should be able to check for unsaved changes and then reload the DataGridViewB content.
NOTE: SelectionChanged event is fired only after the selected row is changed, so you should at least save the index of the current selected row when loading the rows in DataGridViewB, so you can check the correct row when a new row is selected.
For example:
void DataGridViewA_SelectionChange(object sender, EventArgs e)
{
if (previousSelectedIndex != -1)
{
//Check for changes end if necessary prompt the message
[...]
}
//Load data in DataGridViewB
[...]
previousSelectedIndex = dataGridViewA.SelectedRows[0].Index;
}
EDIT:
Another solution could be to check if the grid still has the focus. If it has not, then the user clicked outside the grid and you don't have to prompt the message.
private void DataGridViewA_RowLeave(object sender, DataGridViewCellEventArgs e)
{
if (IsSaveNeeded)
{
if (dataGridViewA.Focused == false)
{ //DataGridViewA without focus, then nothing to do
return;
}
if (!WasSavedPerUserRequest())
{
var recordstate = AlternateDGV.CurrentRow.Cells[Glossary.RecordStateName].Value.ToString();
if (recordstate == Glossary.AddedRecordState) // if it was a new row added and the user doesn't want to save
{
AlternateDGV.CurrentRow.Delete();
}
else if (recordstate == Glossary.EditedRecordState) // if it was a row from DB and the user doesn't want to save, refresh with saved data
{
AlternateDGV.CurrentRow.Refresh(_items);
}
}
}
}
i guess you make visible 'Save' button once User click on Edit button at row level , So when user click on edit button and suddenly leave the row you can check visibility of save button at row level on your AlternateDGV_RowLeave event if it is still visible that means that user has not clicked the save button yet and you can prompt user.
I have a master-detail layout with a section of popup menus (the Details) and a section with a DataGridView which holds the rows.
The popup-menu state is updated when the selected row in the DataGridView changes and the state in the DGV's selected row should update when the popup-menu changes.
All of this works except the row in the DataGridView doesn't immediately update when I change the value of the popup-menu. I have to select a different row in order to see my edits.
I'm assuming this is because the edit hasn't been committed until the selection changes.
My question is: How do I make the change to the popup become immediately reflected in the DataGridView?
I have experimented with calling EndEdit() in the SelectionChangeCommitted handler for the popup-menu, but this has no effect. I'm interested in a technique that would allow me to create a DataGridView that would behave as if there were no Undo mechanism to begin with. Ideally the solution would be generic and transplantable to other projects.
It looks like existing answers work well with BindingSource. In my case, where DataTable was directly used as a DataSource, they didn't work for some reason.
// Other answers didn't work in my setup...
private DataGridView dgv;
private Form1()
{
var table = new DataTable();
// ... fill the table ...
dgv.DataSource = table;
}
After some hair-pulling, I got it work without adding BindingSource indirection:
// Add this event handler to the DataGridView
private void dgv_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
dgv.BindingContext[dgv.DataSource].EndCurrentEdit();
}
private Form1()
{
dgv.CellEndEdit += dgv_CellEndEdit;
// ...
}
Here's what was going on. The answer was in the properties of the ComboBox instances. I needed to change their DataSourceUpdateMode from OnValidation to OnPropertyChanged. This makes sense. The DataGridView was very likely showing the current state of the data. It was just that the data hadn't been edited yet because focus had not left the ComboBox, validating the input.
Thanks to everyone for your responses.
this works well for me:
private void CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var dgw = (DataGridView) sender;
dgw.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
Use this extension method. It works for all columns types, not just ComboBoxes:
public static void ChangeEditModeToOnPropertyChanged(this DataGridView gv)
{
gv.CurrentCellDirtyStateChanged += (sender, args) =>
{
gv.CommitEdit(DataGridViewDataErrorContexts.Commit);
if (gv.CurrentCell == null)
return;
if (gv.CurrentCell.EditType != typeof(DataGridViewTextBoxEditingControl))
return;
gv.BeginEdit(false);
var textBox = (TextBox)gv.EditingControl;
textBox.SelectionStart = textBox.Text.Length;
};
}
This method commits every change just after the change is made.
When we have a text column, after typing a character, its value will commit to the DataSource and the editmode of the cell will end.
Therefore current cell should return to edit mode, and position of the cursor set to end of text in order to enable user to continue typing reminder of the text.
Call the DataGridView.EndEdit method.
following will work
_dataGrid.EndEdit()
s fine once you set the value.
I have a dataset, and 2 datatables.
Datatable1 = Combobox source (This will display a list of options)
Datatable2 = DataGrid (This will display data relevant to the options in combo box)
Submit Button (populate datagrid based on combo box selected value)
When i select an item in combo box and click submit, it load up the relevant records in datagrid. If i then change a value in the datagrid and click the submit button, the value i have just changed, dissapears?
How can i make it so that any altered datagrid values amend the datable, so that even if i view different options, i can always return any, an retain any of the changed values?
Here is my code:
//Load the data grid according to the ComboCAtegory selection
public void Grid_Load()
{
DataSet();
var Result = from c in DataSet_Main.Tables[2].AsEnumerable()
where c.Field<string>("Test_Code").Equals(comboBox_CategorySelect.SelectedValue)
select c;
dataGridView_Main.DataSource = Result.AsDataView();
dataGridView_Main.Columns["Test_Code"].Visible = false;
dataGridView_Main.Columns["ID"].Visible = false;
dataGridView_Main.Columns["Description"].Visible = false;
dataGridView_Main.Columns["Expected_Result"].Visible = false;
}
private void buttonSubmit_Click(object sender, EventArgs e)
{
Grid_Load();
}
public void Fail()
{
DataTable dt = DataSet_Main.Tables[2];
//dataGridView_Main.SelectedRows[0].Cells["Check"].Value = "Fail";
dt.Rows[dataGridView_Main.SelectedRows[0].Index]["Check"] = "Fail";
}
private void buttonFail_Click(object sender, EventArgs e)
{
Fail();
}
Hope this makes sense?
I think your DataGrid is already bound to the Data Table. What you need to do is send the changes back to the data source so that they would be reflected in the second data table which is bound to the same data source. To do this, write an event handler for CellChanging event on the DataGrid and in that you can the call Update() method on your Data Adapter (if you are using one, that is) to send changes to the data source. Then, in the same event handler, update the items in the combo box by refreshing the data bind so that the combo box gets latest values from the second data table.
This way, whenever the cell changes its value in the DataGrid, you can check if it is the relevant cell which you want and update the combo box based on the changes in the data grid.
Apologies my bad..i am a boof head.
Tha datagrid IS bound automatically. Ive just realised i was calling my initial dataset() method - which is calling my database, in my datagrid_load method. thus everytime i was populating the datagrid, it was actually refreshing from the database not the datatable.
Thankyou your repy tho..