ObjectListView cannot cancel item selection BeforeSelect - c#

I converting a standard TreeView to BrightIdeaSoftware.TreeListView
I cannot found how to convert this event
private void LstAgents_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
// If error save
if (!SaveCurrentValues())
// Keep active selection
e.Cancel = true;
}
How to simply cancel the user action if something was wrong with TreeListView
Thanks ...

If your goal is to prevent the user changing the selected item when there is a validation problem with it then you can use the SelectedIndexChanged event. From a usability point of view it's a bit of a disaster though. You might want to instead highlight the row in red or throw up an error dialog and revert the row.
private object oldSelection = null;
void LstAgents_SelectedIndexChanged(object sender, EventArgs e)
{
if(oldSelection != null && true/* some condition*/)
LstAgents.SelectedObject = oldSelection;
oldSelection = LstAgents.SelectedObject;
}

Related

Show ToolTip on DataGridView when cell value has changed

I want to show a ToolTip when the value of a cell in a DataGridView control is changed and the introduced value is invalid.
This can easily be done with the code below, but the problem is that after the ToolTip is shown, it is also associated with the control, so every time the DataGridView is hovered the ToolTip is displayed, and I want it to be shown only once after the data has been changed.
I have noticed that this happens only with DataGridView and GroupBox. With a TextBox or a Button the ToolTip is not associated with the control when shown over it.
Why does this happen?
public partial class Form1 : Form
{
this.dataGridView1.ShowCellToolTips = false; // so we can show regular tooltip
}
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
DataGridView control = (DataGridView)sender;
// check if control.Rows[e.RowIndex].Cells[e.ColumnIndex].Value is valid
if (invalid)
{
toolTip.Show("Invalid data", dataGridView1, 5000);
}
}
There are many ways to deal with this. The simplest and most direct seems to be to Hide the ToolTip when you are leaving the DataGridView:
private void dataGridView1_Leave(object sender, EventArgs e)
{
toolTip1.Hide(this);
}
Of course it is up to you to decide upon the full design of what should happen while the error is still there..!
As for Textboxes: They are very special and there is usually no use in asking why they behave diffently..
Another way would be to code the Popup event and use the Tag as a flag to make sure it is shown only once:
Before you show it in the CellValueChanged you set a flag:
toolTip1.Tag = "true";
and in the Popup event you check for it:
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
if (toolTip1.Tag == null) return;
bool show = toolTip1.Tag.ToString() == "true";
if (toolTip1.Tag != "true") e.Cancel = true;
toolTip1.Tag = "false";
}

Datagridview Datagridcheckbox cell values not matching GUI

I have a unbound DataGridView with 6 columns, the first being a DataGridCheckBoxColumn. When a user clicks on a checkbox cell, I want to determine what cells have been checked and what ones are not. Here is my code:
private void UpdateSelectedPlaces()
{
//Clear out the places list each time the user selects a new item (or items)
_selectedPlaces.Clear();
foreach (DataGridViewRow row in placesGridView.Rows)
{
if (row.Cells[0].Value != null && row.Cells[0].Value.Equals(true))
{
_selectedPlaces.Add((TripPlace)row.DataBoundItem);
}
}
}
//Click event handler
private void placesGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
UpdateSelectedPlaces();
}
I am finding that the DataGridCheckBoxCells are not holding the correct value at the time of the click. This occurs for all rows. There seems to be no pattern really. I was hoping that the event was just not called at the right time (I.e. the checking of the checkbox was yet to be completed) but I cannot prove that.
In short, even though the GUI displays a checked checkbox, the back end thinks the checkbox is not checked when using .Value Is there a simpler way to just determine if each cell[0] is checked or not checked in a datagridview?
I am finding that the DataGridCheckBoxCells are not holding the
correct value at the time of the click.
This is normal and by design because you are using DataGridView.CellContentClick event. From MSDN:
Use this event to detect button clicks for a DataGridViewButtonCell or
link clicks for a DataGridViewLinkCell. For clicks in a
DataGridViewCheckBoxCell, this event occurs before the check box
changes value, so if you do not want to calculate the expected value
based on the current value, you will typically handle the
DataGridView.CellValueChanged event instead. Because that event occurs
only when the user-specified value is committed, which typically
occurs when focus leaves the cell, you must also handle the
DataGridView.CurrentCellDirtyStateChanged event.
The corrected code is similar to the other answer but unfortunately, the reason of the problem was not explained in that answer.
private void UpdateSelectedPlaces()
{
//Clear out the places list each time the user selects a new item (or items)
_selectedPlaces.Clear();
foreach (DataGridViewRow row in placesGridView.Rows)
{
if (row.Cells[0].Value != null && row.Cells[0].Value.Equals(true))
{
_selectedPlaces.Add((TripPlace)row.DataBoundItem);
}
}
}
private void placesGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
UpdateSelectedPlaces();
}
private void placesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (placesGridView.IsCurrentCellDirty)
{
placesGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void placesGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
Boolean bl;
if (placesGridView.Columns[e.ColumnIndex].Name == "name of check column")
{
DataGridViewCheckBoxCell checkCell = (DataGridViewCheckBoxCell)placesGridView.Rows[e.RowIndex].Cells[2]; //2 number of check column
//bl is the check box current condition.
//Change only this in your list eg list[e.RowIndex] = bl; No need to check all rows.
bl = (Boolean)checkCell.Value;
}
}
private void placesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (placesGridView.IsCurrentCellDirty)
{
placesGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}

DataGridView - cell validation - prevent CurrentRow/Cell change

I have WinForms DataGridView with source set to SortableBindingList. In this form, there's column Comment and I need to prevent user from inserting some characters, thus validation.
What I want to do is, whenever user enters invalid value, system will notify him (OnNotification( 'You entered wrong comment');) and force him/her to stay in edit mode.
So far I build solution like this:
void MyDataGridView_CellEndEdit( object sender, DataGridViewCellEventArgs e )
{
if (e.ColumnIndex == ColumnComment.Index) {
object data = Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
if( (data != null) && (!CommentIsValid( data.ToString()))){
CurrentCell = Rows[e.RowIndex].Cells[e.ColumnIndex];
BeginEdit( true );
// My notification method
OnNotification( String.Format( "Comment `{0}` contains invalid characters) );
return;
}
}
}
I have following issues with this:
OnCellValidating is triggered only when whole form is closing or when current row is changed, not after I finish editing of single cell, so I've put check into CellEndEdit.
When I used Enter/Esc to end editing, it works as expected and desired.
When I use mouse and click to another row, cell stays in edit mode, but another row gets selected.
When I try to use Enter (displays notification on invalid comment) and then Esc (to cancel edit) it uses value pushed by Enter (because edit mode has finished).
So my questions are:
How can I fire CellValidating after each cell edit, not when form is closing
How can I prevent CurrentRow and CurrentCell change even after mouse click?
How can I force cell to stay in edit mode?
When I use mouse and click to another row, cell stays in edit mode, but another row gets selected.
Here I would use a global Boolean, bool isInvalidState say and a global DataGridViewCell = invalidCell object. In the default state you can set isInvalidState = false and invalidCell = null. Then using
private bool OnNotification(string cellValue)
{
// Check for error.
if (error)
return false;
}
Then in the above method
void MyDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == ColumnComment.Index) {
object data = Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
if((data != null) && (!CommentIsValid(data.ToString()))){
CurrentCell = Rows[e.RowIndex].Cells[e.ColumnIndex];
BeginEdit(true);
// My notification method
isInvalidState = OnNotification(
String.Format("Comment `{0}` contains invalid characters));
if (isInvalidState)
invalidCell = MyDataGridView[e.RowIndex, e.ColumnIndex];
return;
}
}
}
Now, wire-up an event CellContentClick on your DataGridView and check if isInvalidState == true
private void MyDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (isInvlaidState)
{
isInvalidState = false;
MyDataGridView.CurrentCell = invalidCell;
invalidCell = null;
return;
}
// Do other stuff here.
}
When I try to use Enter (displays notification on invalid comment) and then Esc (to cancel edit) it uses value pushed by Enter (because edit mode has finished).
I am not sure about this problem; it is likely you will have to handle the KeyDown event and capture the escape key - handling it differently.
I hope this helps.
Try something like this. It shall work.
private void datagridview1_dataGridview_CellValidating
(object sender, DataGridViewCellValidatingEventArgs e)
{
if (datagridview1_dataGridview.Rows[e.RowIndex].Cells[2].Value.Equals(""))
{
MessageBox.Show("Product name should not be empty", "Error");
datagridview1_dataGridview.CurrentCell = datagridview1_dataGridview.Rows[e.RowIndex].Cells[2];
datagridview1_dataGridview.CurrentCell.Selected = true;
}
}
Unfortunately, MoonKnight's solution didn't work fully for me as the code in CellContentClick event handler never set the control back to the cell that was being validated for its value, when it had an invalid value. Nevertheless, considering his valuable hint of using global variables isInvalidState and invalidCell helped me constructing the following solution that works exactly as asked in the OP.
Using the combination of CellValidating and CellValidated in the right way solves the problem as follows:
Do your data validation inside the CellValidating event handler. Set the isInvalidState flag and the cellWithInvalidUserInput variable (NOTE: I renamed the invalidCell to cellWithInvalidUserInput):
private void MyDataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
var cellUnderConsideration = MyDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex];
if (!ValidateCurrentCellValue(cellUnderConsideration))
{
OnNotification( String.Format( "Comment `{0}` contains invalid characters) );
//Or MessageBox.Show("your custom message");
isInvalidState = true;
cellWithInvalidUserInput = cellUnderConsideration;
e.Cancel = true;
}
}
The data validation function:
bool isInvalidState;
DataGridViewCell cellWithInvalidUserInput;
private bool ValidateCurrentCellValue(DataGridViewCell cellToBeValidated)
{
//return 'true' if valid, 'false' otherwise
}
Perform desired actions on UI controls inside the CellValidated event handler:
private void MyDataGridView_CellValidated(object sender, DataGridViewCellEventArgs e)
{
if (isInvalidState)
{
isInvalidState = false;
if (cellWithInvalidUserInput != null && cellWithInvalidUserInput.RowIndex > -1)
{
MyDataGridView.CurrentCell = cellWithInvalidUserInput;
MyDataGridView.CurrentCell.Selected = true;
MyDataGridView.BeginEdit(true);
}
cellWithInvalidUserInput = null;
}
}

Windows C# CheckedListBox Checked Item Event Handling

I'm currently developing a Window app that uses CheckedListBoxes for certain aspects of the program. A problem I've encountered is that I have been trying to find which event is triggered when an item is checked so that I can enable a form button when any list item is checked.
Problem is that I tried using the following;
private void clbAvailMods_ItemCheck(object sender, ItemCheckEventArgs e)
{
if(e.NewValue == CheckState.Checked)
{
btnInstall.Enabled = true;
}
}
but when I set a breakpoint on the if statement, it never fires upon checking an item in the listbox.
Am I doing something wrong here?
A standard Windows Forms trick is to delay running code until all event side-effects have been completed. You delay running code with the Control.BeginInvoke() method. This will fix your problem:
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e) {
this.BeginInvoke(new MethodInvoker(evalList), null);
}
private void evalList() {
bool any = false;
for (int ix = 0; ix < checkedListBox1.Items.Count; ++ix) {
if (checkedListBox1.GetItemChecked(ix)) {
any = true;
break;
}
}
btnInstall.Enabled = any;
}
You can use the NewValue property to manually update CheckedItems.Count. This is the code I use to only enable a button when there's at least one item checked:
private void checkedListBoxProds_ItemCheck(object sender, ItemCheckEventArgs e)
{
this.buttonGenerar.Enabled = ((this.checkedListBoxProds.CheckedItems.Count + (e.NewValue == CheckState.Checked ? 1 : -1)) > 0);
}
A couple of potential gotchas. Presumably you've added the event through the VS.Net GUI to ensure that it gets plumbed into the control. Try clicking on an item twice - once to give the item focus and again to toggle the check state - if you want an item to have its check state toggled on first click then set the "CheckOnClick" property to true.
I think it is the SelectedIndexChanged event but I will confirm right now.
EDIT: SelectedIndexChanged event does work. But that is firing regardless of whether the checkbox was checked. So I would then check the checked state if you want to do that.
But as an aside when I did use the ItemCheck event it did fire when I actually checked the checkbox and not just the text.
I know this has been answered long ago, but I found it easier to just handle the MouseUp and KeyUp events. The CheckedItems.Count property is accurate when those events are fired. Since they both do the same thing, I created a method to to the work and called that method from both event handlers.
private void clbFolders_KeyUp(object sender, KeyEventArgs e) { Update(); }
private void clbFolders_MouseUp(object sender, MouseEventArgs e) { Update(); }
private void Update()
{
btnDelete.Enabled = clbFolders.CheckedItems.Count > 0;
}

How to prevent/cancel a combobox's value change in c#?

I have a combobox at the top of a form that loads editable data into fields below. If the user has made changes, but not saved, and tries to select a different option from the combobox, I want to warn them and give them a chance to cancel or save.
I am in need of a "BeforeValueChange" event with a cancelable event argument.
Any advice on how to accomplish?
Save the ComboBox's SelectedIndex when to box if first entered, and then restore it's value when you need to cancel the change.
cbx_Example.Enter += cbx_Example_Enter;
cbx_Example.SelectionChangeCommitted += cbx_Example_SelectionChangeCommitted;
...
private int prevExampleIndex = 0;
private void cbx_Example_Enter(object sender, EventArgs e)
{
prevExampleIndex = cbx_Example.SelectedIndex;
}
private void cbx_Example_SelectionChangeCommitted(object sender, EventArgs e)
{
// some custom flag to determine Edit mode
if (mode == FormModes.EDIT)
{
cbx_Example.SelectedIndex = prevExampleIndex;
}
}
Here is the simplest fix:-
bool isSelectionHandled = true;
void CmbBx_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (isSelectionHandled)
{
MessageBoxResult result = MessageBox.Show("Do you wish to continue selection change?", this.Title, MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.No)
{
ComboBox combo = (ComboBox)sender;
isSelectionHandled = false;
if (e.RemovedItems.Count > 0)
combo.SelectedItem = e.RemovedItems[0];
return;
}
}
isSelectionHandled = true;
}
Save the current value on the Enter event.
Implement the BeforeValueChange logic in the ValueChanged event, before the actual ValueChanged logic. If the user cancels, set the stored value and don't continue in the method (return).
If you're going to use this system a lot, I'd suggest inheriting ComboBox and implementing your BeforeValuechange event there.
The Validating event can be used for this scenario
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validating.aspx
You don't get an appropriate event by default. You could cache the previous value and set it back to that if the user wants to cancel.
How about using the Validating / Validated events?
It works well, if the event happening on LostFocus instead of Change is ok with you.
Otherwise, how about
public void Combobox_ValueChanged(object sender, EventArgs e) {
if (!AskUserIfHeIsSureHeWantsToChangeTheValue())
{
// Set previous value
return;
}
// perform rest of onChange code
}
You could use a message filter to intercept clicks and key presses, which would allow you to prevent the combo box's normal behaviour. But I think you'd be better off disabling the combo box when the user makes a change, and require them to either save or revert their changes.
You can't really prevent it, but you can change it back to the old value if certain requirements aren't met:
private SomeObject = selectedSomeObject=null;
private void cbxTemplates_SelectionChangeCommitted(object sender, EventArgs e)
{
if (!(sender is ComboBox cb)) return;
if (!(cb.SelectedItem is SomeObject tem)) return;
if (MessageBox.Show("You sure?", "??.",
MessageBoxButtons.OKCancel) != DialogResult.OK)
cb.SelectedItem = selectedSomeObject;
else
{
selectedSomeObject = tem;
}
}

Categories

Resources