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();
}
}
Related
I'm using web forms
Right now my code runs when I leave the text box and the text has changed. I am running into issues. If I change the text but hit a button instead of enter, it resets via code. I need to be able to change the text and click a button which wont yet do anything, or change the text and hit enter which will trigger code.
thanks for the help
This is text changed event for the text box with notations of what im needing to do. really what I think I need is an event for clicking enter, not changing text
protected void txtboxPlate_TextChanged(object sender, EventArgs e)
{
if (txtboxPlate.Text == "plate number")
{
//will check database for "plate number" and do stuff on enter.
}
else
{
resetforms();// on enter
}
else
{
the text has changed by user, but has clicked a button and needs nothing to happen because of this text change
}
}
You need to implement a method for the KeyDown event of your textbox.
private void txtboxPlate_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) {
//Code
}
}
Keydown is a client-side event, as far as I know you're more than likely going to have to use JavaScript/Jquery.
Refer to the following link:
Textbox Keydown Event
I would of used a comment but... rep issues :/
edit:
To anyone that hasn't realised yet, the question changed to webforms not winforms
Alternative:
Use a button.
You could place a button next to the textfield, it's simple and not a lot of work goes into it. You can set the TabIndex of the textbox to 1 and the TabIndex of the button to 2 so when you hit TAB it will focus the textbox and if pressed again it will focus the button. You could also look into adding the button to the textbox for design purposes.
edit: You also need to think about post back, when you hit a button the page get's post back meaning values are lost, to persist the data within the field you would have to use view state, session variables or a hidden field. For simplicity I'd set the value of the text box to a hidden field and then simply re-apply the value on page_load. If the value needs to persist across multiple postbacks/accessed from other pages use session variables.
So as a code example:
Remove the Text_changed event entirely.
protected void btnDoStuff(object sender, EventArgs e)
{
if(txtPlateNumber.Text == "Plate Number")
{
//Do stuff
}
else
{
//Do other stuff
}
}
-
If you notice the value disappearing after the post back do something like:
protected void btnDoStuff(object sender, EventArgs e)
{
if(txtPlateNumber.Text == "Plate Number")
{
//Do stuff
myHiddenField.Value == txtPlateNumber.Text;
}
else
{
//Do other stuff
}
}
then reset the value on page_load.
If this is winform and you still want to use textchanged you can try catching your code first for example:
protected void txtboxPlate_TextChanged(object sender, EventArgs e)
{
//Catching code
if (txtboxPlate.Text != "")
{
if (txtboxPlate.Text == "plate number")
{
//will check database for "plate number" and do stuff on enter.
}
else
{
resetforms();// on enter
}
}
else
{
the text has changed by user, but has clicked a button and needs nothing to happen because of this text change
}
cause what textchanged is doing is unless txtboxPlate.Text equels "plate number" it will always do the else statement. Correct me if i'm wrong though but i had the same problem before which almost made me go insane.
Or try above 1 upvote code:
protected void txtboxPlate_TextChanged(object sender, EventArgs e)
{
//Event only happens if you press enter
if (e.KeyCode == Keys.Enter)
{
if (txtboxPlate.Text == "plate number")
{
//will check database for "plate number" and do stuff on enter.
}
else
{
resetforms();// on enter
}
}
else
{
the text has changed by user, but has clicked a button and needs nothing to happen because of this text change
}
}
I am trying to follow the MSDN example code for the Control.Validating Event and to apply it to a bindingNavigator item instead of the textbox as in the example.
The code provided for the textbox validation event is shown below.
private void textBox1_Validating(object sender,
System.ComponentModel.CancelEventArgs e)
{
string errorMsg;
if(!ValidEmailAddress(textBox1.Text, out errorMsg))
{
// Cancel the event and select the text to be corrected by the user.
e.Cancel = true;
textBox1.Select(0, textBox1.Text.Length);
// Set the ErrorProvider error with the text to display.
this.errorProvider1.SetError(textBox1, errorMsg);
}
}
I too intend on validating texboxes however I have several and have already written the validation methods for them which handle the error messages. I would like these methods to be tested when the user selects an item (Arrow buttons / Save button / Add button) on the bindingNavidator.
I therefore have the code below to attempt this,
private void myBindingNavigator_Validating(object sender, CancelEventArgs e)
{
if (!IsPostCodeValid())
{
PostCodeTextBox.Focus();
e.Cancel = true;
}
}
I have the ItemClicked event for the bindingNavigator set to bindingNavigator.Focus() with the hope of initiating the Validating event. (myBindingNavigator.CausesValidation = true; has been declared on formLoad). However I feel this is where an infinite loop of focusing the Navigation bar follows? I have stepped through and no code is executed once it has locked on to the Navigation bar it simply won't let the user interact with the rest of the form to change and correct the error after the navigation bar has been locked onto.
I can provide any extra information required and test any suggestions to discover what is going on.
Thanks
When you work with BindingNavigator and you have put your controls in details mode on the form, to ensure save only valid data, you should write validation rules for your controls and also you should handle your binding navigator items yourself.
This way you even don't need to set AutoValidate Property of your form to annoying EnablePreventFocusChange and you can set it to be the friendly EnableAllowFocusChange and because you can't navigate or save anything when there is validation errors, then you can be sure only valid data will save in database.
To do so, follow these steps:
Step 1
Handle Validating event of child controls and set e.cancel = true when the value is not valid.
private void nameTextBox_Validating(object sender, CancelEventArgs e)
{
//Put validation logic here
if (this.nameTextBox.Text.Length < 10)
{
e.Cancel = true;
this.errorProvider1.SetError(this.nameTextBox, "Some Error");
}
else
{
this.errorProvider1.SetError(this.nameTextBox, "");
}
}
Step 2
Go to BindingNavigator properties, and set MoveFirstItem, MovePreviousItem, MoveNextItem, MoveLastItem, AddNewItem, DeleteItem properties To (none). Also from designer click on the text box that shows record number, it is bindingNavigatorPositionItem, and then set its ReadOnly property to true.
Step 3
For all buttons, including navigation buttons, add button, delete button, save button and other custom buttons, handle Click event and call ValidateChildren method of the container of your text boxes and check if ValidateChildren() doesn't return true, exit the method, else do the task that the method should do, for example:
private void toolStripButton1_Click(object sender, EventArgs e)
{
if (!this.ValidateChildren())
return;
//Put the logic for this button here
//MoveFirstItem: `this.myBindingSource.MoveFirst();`
//MovePreviousItem: `this.myBindingSource.MovePrevious();`
//MoveNextItem: `this.myBindingSource.MoveNext();`
//MoveLastItem: `this.myBindingSource.MoveLast();`
//AddNewItem: `this.myBindingSource.AddNew();`
//DeleteItem: `this.myBindingSource.RemoveCurrent();`
}
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);
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 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;
}
}