In two words I want to return the previous selecteditem without selecting it again or without triggering the SelectedIndexChanged method.
This is the situation: I have combo box and when a item is selected, datagridview is filled with rows. You can edit things in the datagridview and save them to a file. If you don't save the changes and try to change the item in the combo box it tells you that all the changes will be lost. I make a messagebox that make you choose between yes(to change) and no(not to change). If you click yes everything is OK, but if you click NO then I have to return the previous selected item. When I do so I trigger SelectedIndexChanged and that makes the datagridview load again and delete the changes. Here is my code in the SelectedIndexChanged method:
if (!ChangeMade)
{
//#1 Some Code
}
else
{
DialogResult dialogResult =
MessageBox.Show("Are you sure you want to change the manifacturer?" +
"\n All the changes you have done will be lost.",
"Warning", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
//same code as #1
}
else
{
//Here must be the code that returns the previous
//item without selecting it.
}
Sorry for my english.
As I see it you want to change data only when user changes combo box. For this scenario SelectionChangeCommitted is perfect because it only triggers when user makes changes to combobox.
private void TypeSelectionChangeCommitted(object sender, EventArgs e)
{
if (!ChangeMade)
{
//#1 Some Code
}
else
{
DialogResult dialogResult =
MessageBox.Show("Are you sure you want to change the manifacturer?" +
"\n All the changes you have done will be lost.",
"Warning", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
//same code as #1
}
else
{
//Here must be the code that returns the previous
//item without selecting it.
}
}
}
More info on MSDN
The way I do this:
private void ComboBoxOnChange(...)
{
if (!shouldTrigger)
return;
shouldTrigger = true;
// Here goes your code
should trigger = false;
}
this.comboBox1.SelectedIndexChanged -= new EventHandler(comboBox1_SelectedIndexChanged);
Related
I have a WinForms application that checks for pending changes whenever the user hits the cancel button. If there are pending changes, I prompt the user to see if they are sure they wish to cancel. If they do, I close the form. If not, I just return. However, the form is closing anyways. After some debugging, I realized it was because this particular button is set to the form's CancelButton, so clicking it caused the form to close. To verify, I removed the CancelButton property, but the behavior persisted.
How can I prevent this automatic closing? Here is my event handler code:
private void closeButton_Click(object sender, EventArgs e)
{
DialogResult dr = DialogResult.Yes;
if (changesMade)
{
dr = MessageBoxEx.Show(this, "Are you sure you wish to disregard the changes made?", "Changes Made", MessageBoxButtons.YesNo);
}
if (dr == DialogResult.Yes)
{
Close();
}
else
{
//TODO:
}
}
In the above code, the form should only close if there are no changes made, or if the user chose to disregard them. I made changes, and clicked 'No' to the DialogBox, but the form still closed. With and without the button set as the form's CancelButton.
Just set the property DialogResult of the form to the enum DialogResult.None
....
if (dr == DialogResult.Yes)
{
Close();
}
else
{
this.DialogResult = DialogResult.None;
}
or simply:
if (dr != DialogResult.Yes)
this.DialogResult = DialogResult.None;
The form closes automatically because the property DialogResult of the button is not set to DialogResult.None in the Forms Designer. In this scenario, the WinForms engine takes that value and assign it to the DialogResult property of the whole form causing it to automatically close. This is usually used in the calling code of the form to distinguish between a Confirm and a Cancel button
In the example below suppose that on the frmCustomers there are two buttons, one with the DialogResult property set to DialogResult.OK and another set to DialogResult.Cancel. Now if the user hits the OK button you know, in the calling code what to do with the inputs for your new customer
using(frmCustomers f = new frmCustomers())
{
if(f.ShowDialog() == DialogResult.OK)
{
// Execute code to save a customer
}
}
Following up on my comment, this is what I do for an internal tool I wrote recently:
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = !PromptUnsavedChanges();
}
private bool PromptUnsavedChanges()
{
if (HasFormChanged()) //checks if form is different from the DB
{
DialogResult dr = MessageBox.Show("You have unsaved changes. Would you like to save them?", "Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (dr == System.Windows.Forms.DialogResult.Yes)
tsmiSave_Click(null, null); // Saves the data
else if (dr == System.Windows.Forms.DialogResult.Cancel)
return false; // Cancel the closure of the form, but don't save either
}
return true; // Close the form
}
The logic could probably cleaned up from a readability point of view, now that I'm looking at it months later. But it certainly works.
With this you simply just call this.Close(); in your button click event. Then that event is what handles the prompting and decides if it should actually close or not.
My question somewhat relates to this question but the solution suggested is not working for me.
So here's my case.
I have a child form within an MDI parent. The form contains Tab control and a GridView in it.
I have added keyboard shortcuts within KeyUp event of the form itself. Now when user has selected one of the rows in Grid and hits Delete, I do MessageBox.Show() with YESNO buttons to confirm user's action.
Also, Form supports Enter (or Ctrl+O) key that if User hits it while record is selected from the Grid, it opens the Record in another child form for editing.
Here, Enter key is causing conflicts, as when I have that delete confirmation MessageBox open, and I hit "Enter", it does the delete operation but the same record is also opened in the child form for editing (this can obviously lead to NullPointers but I guess delete from Database occurs after the record is cached for opening).
As the solutions provided in the similar question I linked earlier, I tried setting a Form level flag which is set to true when MessageBox is opened and set to false when user clicks either of Yes or No keys, but I'm unsure if I'm setting flag at proper place in code or not.
PSA: I have Delete and Open as buttons on the form as well and thus I'm using same methods on Shortcuts.
Here's my KeyUp Event of Form
private void FormAnalystOpenReport_KeyUp(object sender, KeyEventArgs e)
{
if (((e.Control && e.KeyCode == Keys.O) || e.KeyCode == Keys.Enter) &&
!this.DELETE_CONFIRM_OPEN)
{
rtBtnOpen_Click(sender, e);
}
else if (e.KeyCode == Keys.Delete)
{
rtBtnDelete_Click(sender, e);
}
}
And following method to delete record
private void rtBtnDelete_Click(object sender, EventArgs e)
{
DataGridViewRow row = (DataGridViewRow)rtDataGrid.SelectedRows[0];
int delete_id = int.Parse(row.Cells[0].Value.ToString());
this.DELETE_CONFIRM_OPEN = true;
DialogResult feedback = MessageBox.Show(this,"Are you sure you want to delete selected record?", "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if(feedback == DialogResult.Yes)
{
if (this.db.DeleteRecordById(delete_id)) //Would return true for successful delete of record, false otherwise.
{
//Code to reload Grid Data with updated Records list.
}
else
{
MessageBox.Show(this, "Failed to delete record!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
this.DELETE_CONFIRM_OPEN = false;
}
Thanks!
I think your problem is that messagebox works off a KeyDown event so when you return to your form the button is Down and you release it thus triggering your KeyUp.
try adding a keydown event to your form to set the deleteconfirm.
if ((e.Control && e.KeyCode == Keys.O) || e.KeyCode == Keys.Enter)
{
canDelete = true;
}
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 a form that is linked to a database and allows a user to insert, update and delete records. I want the user to be notified of any unsaved changes when they click to go back to the main menu, and have the option whether or not to save them.
The code I have so far is below, but it doesn't recognize the changes. It just goes straight back to the main menu.
private void btnBack_Click(object sender, EventArgs e)
{
frmMenu frmMainMenu = new frmMenu();
if (dsOrders.HasChanges())
{
if (DialogResult.Yes == MessageBox.Show("There are changes that have not been saved and will be lost. Would you like to save them before leaving this form?", "Unsaved Changes", MessageBoxButtons.YesNo))
{
dsOrders.AcceptChanges();
}
else
{
frmOrders.ActiveForm.Hide();
frmMainMenu.Show();
}
}
else
{
frmOrders.ActiveForm.Hide();
frmMainMenu.Show();
}
}
I am guessing you are using DataBinding and dsOrders is a dataset.
What you could try to do is to check if your databinding is working correctly (both-ways) by setting a breakpoint just before the menu is called.
Then, you could edit some data, and when the breakpoint is triggered, check if the changes are in the dataset. If they are not, the HasChanges method will return false, and you won't get the messagebox.
An other way to check if your data has changed to show this message would be to attach an eventhandler on the change event of all the controls on your form. In this event handler you could set a boolean like blnChanged to true and check its value instead of dsOrders.HasChanges
One way to do is use the individual controls' Changed event and set a dirty bit
for eg
public bool Dirty { get; set; }
private void textBox1_TextChanged(object sender, EventArgs e)
{
Dirty = true;
}
and then
if (Dirty)
{
if (DialogResult.Yes == MessageBox.Show("There are changes that have not been saved and will be lost. Would you like to save them before leaving this form?", "Unsaved Changes", MessageBoxButtons.YesNo))
{
dsOrders.AcceptChanges();
}
else
{
frmOrders.ActiveForm.Hide();
frmMainMenu.Show();
}
}
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;
}
}