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;
}
}
Related
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;
}
I understand that there are Listbox Select index change questions floating around. However, this question focuses on a different matter. I have a list box, with some strings on the form. What I am trying to accomplish is to be able to scroll through the items in the list box (i.e using the arrow keys to navigate to a particular item). Once I navigate to the item I want, I want to either be able to press enter on the item and continue my application. So, the question is How to determine the Event type of that was raised on the List box in order to compare the event with either a Mouse Click event or a Keydown event, thus allowing me to decide which conditional statement to execute based of the result of the boolean expression......The reason I need to determine the type is because if the user presses ENter on the selectedIndexed Item a Dialogbox Appears, currently the dialogbox appears everytime a user HIGHLIGHTS a new item (you can see how that is a problem).
Psuedo Code
if (Listbox_Selected_Event_EventType isEqualTo Mouse_Click)
{
// execute code
} else if (Listbox_Selected_Event_EventType isEqualTo KeydownEvent)
{
// execute code
}
Finished code thanks to Evan,
private void listBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (listBox1.SelectedIndex != -1)
{
if (e.KeyChar == (char)Keys.Return)
{
var file = Directory.GetFiles(urlHistoryFolder, listBox1.Text).FirstOrDefault();
String line;
try
{
using (StreamReader sr = new StreamReader(file))
{
line = sr.ReadToEnd();
}
DialogResult result1 = MessageBox.Show("Are You sure you want to Load this WebService", "Important Question", MessageBoxButtons.YesNo);
if (result1 == DialogResult.Yes)
{
//MessageBox.Show("Loading WebService");
textEndPointUri.Text = line;
listBox1.Visible = false;
GetBtn_Click(sender, e);
}
}
catch (Exception exp)
{
Console.WriteLine("File could not be read:");
Console.WriteLine(exp.Message);
}
}
}
}
The problem is you are looking at the wrong event. You should be handling the MouseClick event and the KeyUp or KeyDown event on the list box.
private void listBox1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
//Get the selected item and do whatever you need to it
//Open your dialog box
}
}
private void listBox1_Click(object sender, MouseEventArgs e)
{
//Get the selected item and do whatever you need to it
//Open your dialog box
}
Then there is no need for a conditional as you have handled both the events individually. Make sure you remove your Dialog box code from the SelectedIndexChanged event.
EDIT:
SelectedIndexChanged fires every time you select and item in the ListBox Object. The box still stores an index even if you don't handle that event. So you can reference or manipulate the PROPERTY of SelectedIndex anywhere. If you handle the two above events, any time a user clicks an item or presses enter you check if there is a selected item:
if (listBox1.SelectedIndex != -1)
{
//Now we know you have an item selected
//Do some stuff
}
Add a Button to the Form and set the AcceptButton() Property of the FORM to that Button. Now when Enter is pressed the Button will fire. Display your dialog in the Button Click() handler. This has the added benefit that people can also click the Button instead of pressing Enter:
private void button1_Click(object sender, EventArgs e)
{
if (ListBox.SelectedIndex != -1)
{
// ... display the dialog ...
Console.WriteLine(ListBox.SelectedItem.ToString());
}
}
To identify if ENTER has been pressed:
private void listBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
// do something
}
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;
}
}
Let's say we have a Win32 form with a Save toolbar button and some sontrols like a CheckBox are on the form, now we write a one line code for onchange event of checkbox to enable/disable the Save button. Let's say checkbox is selected at first, Save button disabled, now de-select the checkbox, Save button becomes enabled...now select the checkbox again Save button is still enabled...Same for a TextBox for example. Let's say its text is "Hi"...change it to "Hi Bye" , Save is enabled...change it BACK to "Hi" as it was, Save remains enabled...
Is there a model we can use to prevent these wrong enabling/disabling of save button?
You need to write some IF - ELSE code in the CheckedChanged event of the Checkbox. Check what is the current state by inspecting the Checked proeprty of the control (checkbox) ,If yes set the Enabled proeprty of the Button to true, else false.
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
if (checkBox2.Checked)
button1.Enabled = true;
else
button1.Enabled = false;
}
Assuming checkBox2 is the name of the Checkbox and button1 is the name of the Save button.
You can use the same IF ELSE logic for other controls also. To Set the Value of the Textbox, Use the Text property
TextBox1.Text="I am gonna try something now"l
EDIT : As comecme suggested, If you only want to enable/disable button based on the checbox, It can be done in one line instead of the IF else block like this
button1.Enabled=checkBox2.Checked
You could store the last saved state, and compare the current state to it whenever it changes, to see if they're identical. If so, disable the button.
If these comparisons are expensive, you could make this more efficient, by calculating a hash value over all of the fields that need to be saved, and only doing the proper comparison if the hash of the last saved state matches the hash of the current state.
I prefer to put all my control state checking and setting into a single method:
private void UpdateControls()
{
saveButton.Enabled = checkBox1.Checked;
otherButton.Visible = checkBox2.Checked && textBox.Text.Length > 0;
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
UpdateControls();
}
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
UpdateControls();
}
private void textBox_TextChanged(object sender, EventArgs e)
{
UpdateControls();
}
This means you just have one place in which to check and set the state, and makes it much easier to understand what is going on, especially when you have complex forms. I also prefer boolean expressions rather than if statements when assigning boolean variables, because it caters for both true and false without having to write a separate else statement (which may be forgotten).
I don't get where you're going with your checkbox, but I would use a Boolean variable:
private Boolean _canSave = false;
private Boolean CanSave
{
get { return _canSave; }
set
{
_canSave = value;
MenuSave.Enabled = value;
}
}
public void MenuSave_Click()
{
Save();
}
private void Save()
{
// do your thing
CanSave = false;
}
public void TextBox_TextChanged()
{
CanSave = true;
}
This won't account for disabling the saving menu when you revert the text back to its original. If you want that, you'll have to store the text in the Save() method in a private variable, and compare that to the current text on every TextBox_TextChanged() to determine whether a change compared to the original (i.e. since last save) has occurred.
I use checkbox in WPF window. I use some logic in unchecked event of checkbox. And I want to cancel uncheck if need it in the body of this event. How can I do this?
private void chApprove_Checked(object sender, RoutedEventArgs e)
{
btnAddDepartment.IsEnabled = true;
brnRemoveDepartment.IsEnabled = true;
}
private void chApprove_Unchecked(object sender, RoutedEventArgs e)
{
if (lbSource.Count == 0)
{
btnAddDepartment.IsEnabled = false;
brnRemoveDepartment.IsEnabled = false;
}
else
{
MessageBox.Show("Staff already in use! Release it first from dependecies!");
CheckBox myCheckBox = e.Source as CheckBox;
myCheckBox.IsChecked = true;
}
}
Impossible to cancel uncheck !!!
void CheckBox1_Unchecked(object sender, RoutedEventArgs e)
{
if(ResultOfSomeLogic)
{
CheckBox myCheckBox = e.Source as CheckBox;
myCheckBox.IsChecked = True; // Check it again
}
else
{
}
}
Also take a look at EventToCommand Binding Behaviour in MVVM Light to take advantage of CanExecute method.
You could do this easily with an attached behavior (rather than using code behind), you can take a look at this answer if you need a sample of how to structure one (it's only a few lines of code).
My spider-sense is telling me this isn't a very good idea though - I can't imagine a way to "justify" rechecking a checkbox that a user has clicked, it just strikes me as very jarring. Can you not either bind the enabled state of the checkbox to a property on your ViewModel or, if you have an ICommand bound to it, use the CanExecute delegate to enable/disable it based on the same logic?
Bind the IsChecked property of check box. Like
IsChecked="{Binding IsChecked, Mode = TwoWay}"
and in your class define some thing like dis;
private bool isChecked;
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
this.isChecked = value;
OnPropertyChanged["IsChecked"];
}
}
and in your event
void CheckBox1_Unchecked(object sender, RoutedEventArgs e)
{
if(ResultOfSomeLogic)
{
this.IsChecked = true;
}
else
{
}
}
hope this will work for u..
Good Luck..
In my case, I could not use a solution that allowed unchecking in the first place. If the checked state initiates a critical asynchronous operation, it is not always ideal to uncheck just to check it again: Why allow cancelling this operation if it shouldn't have been allowed to cancel in the first place?
For MenuItems, you can subscribe to the PreviewMouseDown event and set IsCheckable to false; then subscribe to the Click event and set IsCheckable back to true. The reason this works is because IsCheckable just determines whether or not to initiate the state change, unlike IsHitTestEnabled="false" and IsEnabled="False", which will stop all events from firing.
If you try to disable it, no subsequent events will fire making it impossible to restore checkability; by making it uncheckable beforehand, we avoid this mistake. Click also happens to occur after the state would've been changed so it works out quite nicely.
Unfortunately, CheckBox does not have an equivalent IsCheckable property; however, the same concepts described above (i.e., PreviewMouseDown, Click pattern) can produce a similar, if not identical, result.
Well assuming a check box is intended to interact with users instead of programmatic ways, there's a simple way to cancel Unchecked events based on some logic when user hits left mouse button or space bar:
private void CheckBox_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// If it's already checked so next click would uncheck it hence trigger the Unchecked event.
if ((sender as System.Windows.Controls.CheckBox).IsChecked == true)
{
var isConfirmed = false; // Use your confirmation logic here instead.
// If e.Handled is set to false that will cancel further events such as the Unchecked event.
e.Handled = isConfirmed;
}
}
private void CheckBox_PreviewKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
// If it's already checked so when user presses space bar it would uncheck it hence trigger the Unchecked event.
if ((sender as System.Windows.Controls.CheckBox).IsChecked == true)
{
if (e.Key == System.Windows.Input.Key.Space)
{
var isConfirmed = false; // Use your confirmation logic here instead.
// If e.Handled is set to false that will cancel further events such as the Unchecked event.
e.Handled = isConfirmed;
}
}
}