checking if form data has been changed - c#

I have been trying the following C# code to check if form data has been changed before closing and saving but it seems that it always reports the form as being changed even when no changes were made.
//Declare a private variable
private bool requiresSaving =false;
//Declare an event
private void SomethingChanged(object sender, EventArgs e)
{
requiresSaving = true;
}
//Hook up this event to the various changed events, eg
this.txtNameDepart.TextChanged += new System.EventHandler(this.SomethingChanged);
//Check this variable when you are closing the form
private void DepartamentEdit_FormClosing(object sender, FormClosingEventArgs e)
{
if (requiresSaving)
{
....
You also need to set requiresSaving false in the saveDepart method.

I think you hook up these events even before you load your initial data. Then SomethingChanged fires and enable the save button even the user doesn't change anything.
You could either unhook these events when loading the default/existing data or hook up these events after loading default/existing data.
//Un-Hook when loading your default/existing data.
private void SetDefaultData()
{
this.txtNameDepart.TextChanged -= new System.EventHandler(this.SomethingChanged);
this.txtNameDepart = "My default text";
this.txtNameDepart.TextChanged += new System.EventHandler(this.SomethingChanged);
}

you should show all the places where you set the flag to true, just in case.
also this code:
//Hook up this event to the various changed events, eg
this.txtNameDepart.TextChanged += new System.EventHandler(this.SomethingChanged)
even understanding what you are trying to do, I think this is bad because if you have many controls and soon or later you will handle the logic of each change event with some more specific code for the individual controls, you should not attach the same event handler to all of them at once.
if all your controls are bound to a BindingSource you could use another approach.
if your controls are populated manually by you with some assignment from a business object on form load, you could also imagine to compare such object's properties with the original one (if you also saved a copy of the original of course) in the form_closing.

Related

Skip Layout event handler

I use the Layout Event Handler in order to set the focus to a specific field. So everytime the Layout event occurs, the focus will be set to that field.
My problem is that when I make something visible/invisible, the focus will be set again to this specific field which I don't want.
How to avoid that?
Is there a way to say "skip the Layout Event Handler next time" ?
or should I use another Event Handler instead of the Layout one?
I just want it to be set the first time, but the Load event does not seems to work as expected.
Code for Load event handler:
in the Designer.cs
private void InitializeComponent()
{
// other stuff automatically generated by windows
this.Load += new System.EventHandler(this.myControl_Load);
// other stuff automatically generated by windows
}
and in myControl.cs:
private void myControl_Layout(object sender, LayoutEventArgs e)
{
this.myTimeEdit.Focus();
}
From what I gather, you need to focus a particular control when a form is shown for the first time. First, you can try to set the TabIndex property to 0. In this case, this control will be focused on the first form showing.
Try also to focus the control on the Form.Shown event instead of the Load event.
You can use LayoutEventArgs.AffectedProperty property. Just check if its value is set to "Visible":
private void myControl_Layout(object sender, LayoutEventArgs e)
{
if (e.AffectedProperty == "Visible")
return;
this.myTimeEdit.Focus();
}

How can I cancel a ColumnChanging event and have the bound control show the original value instead of the rejected change?

I have a rather complicated form with several interchangeable sub-panel classes, each of which has several user controls of its own. One of the sub-panels has a checkbox that, depending on what options are selected in other sub-panels may not be allowed to uncheck.
Ideally I would handle this by making the checkbox readonly when it's not allowed to uncheck, but that would entail detecting any change that could potentially enable/disable the checkbox, calculating whether it should be enabled, and then telling the sub-panel to enable/disable the checkbox. It seems like it would be easier to detect an attempt to uncheck the checkbox, calculate whether the current state allows unchecking it, and then accept or reject the attempt.
The convention we're using is to attach an event handler to a page's data table's ColumnChanged or ColumnChanging event and then have that handler call appropriate helper methods based on which column changed. I put in the code below:
private void MyTable_ColumnChanging(object sender, DataColumnChangeEventArgs e) {
...
if (e.Column == MyTrueFalseColumn) {
CheckboxChanging(e);
}
...
}
private void CheckboxChanging(DataColumnChangeEventArgs e) {
if (!(bool)e.ProposedValue && MustRemainChecked())
e.ProposedValue = true;
}
I also tried e.Row.SetColumnError(e.Column, "error message"), e.Row.CancelEdit(), and e.Row.RejectChanges() in place of e.ProposedValue = true and confirmed those lines executed, but the checkbox still unchecks without any indication anything happened behind the scenes. I also tried all 4 of those cancellation methods followed by MyCheckBox.DataBindings[0].ReadValue(), again with no success.
I've used breakpoints to step through and confirm that the proposed value is being set to true, but the checkbox still unchecks when the event is complete. What am I doing wrong?
Is the data column properly storing true but the checkbox doesn't update to reflect that? Is there something else I need to do to have the data column store the changed ProposedValue?
I eventually solved this by messing with the checkbox directly in the ColumnChanged event rather than the ColumnChanging event:
private void MyTable_ColumnChanged(object sender, DataColumnChangeEventArgs e) {
if (e.Column == MyTrueFalseColumn)
CheckboxChanged(e);
}
private void CheckboxChanged(DataColumnChangeEventArgs e) {
if (!(bool)e.ProposedValue && MustRemainChecked()) {
MyCheckbox.Checked = true;
MyCheckbox.DataBindings[0].WriteValue();
}
}
If anyone has a solution that only messes with the associated DataTable I'd be happy to accept that.
e.Row.CancelEdit();
This will cancel the proposed value from updating the the current row of data.

How can I know Text changes for textboxes without Textchanged event

In my C# Windows form MyForm I have some TextBoxes.
In these TextBoxes, we have to detect if the TextChanged event occurs,
if there're changes in these TextBoxes and click close button, it will ask if we want to cancel the changes when we close the form.
However, when I run the MyForm, I can't know text change for each textbox caused by user typing for without textchanged event property.
But I am thinking how do I make the TextBox's TextChanged know the
event cuased by user typing without textchanged event?
Thanks for help.
Sorry for my English.
There is no (decent) way of knowing what's typed without a TextChanged or a Leave event.
You need to use one of these events to get the typed content. Doing this enable you to set a "dirty" flag that you can check at close and clear at save.
Comparing old and new value has no point without this cause you won't know what the value should be set to without knowing something was changed.
With one exception: If your original data came from a database you could use the compare old/new approach as you would compare the textbox of that which came from the database.
Update:
Addressing this comment:
"Because Myform have many textboxes and if no text change ,this will
not display the confirm message.If I catch textchanged event for all
textboxes, this is so many code."
You can use a common handler to collect the changes for all textboxes in one single method. Use the sender object (cast it to Textbox) to identify which textbox is changed, if needed, or simply set a dirty flag for whatever textbox has a change.
bool isDirty = false;
void SomeInitMethod() //ie. Form_Load
{
textbox1.TextChanged += new EventHandler(DirtyTextChange);
textbox2.TextChanged += new EventHandler(DirtyTextChange);
textbox3.TextChanged += new EventHandler(DirtyTextChange);
//...etc
}
void DirtyTextChange(object sender, EventArgs e)
{
isDirty = true;
}
void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (isDirty) {
//ask user
}
}
// to clear
void Save()
{
SaveMyDataMethod();
isDirty = false;
}
If you have a lot of textboxes in the form loop through the forms control collection and use typeof to address the textboxes. If you have textboxes requiring different approaches use the Tag property of the textbox to distinguish.
A possible approach is using the timer. Have a timer that ticks every 1000 ms (say) and checks the textBox.Text.
A second possible approach is overriding WndProc for the textbox (by inheriting a new class) and handling the change text message. This would be the same as overriding TextBox.OnTextChanged.
Why dont you use a timer which will check after a few intervals if the textboxes do contain any text

Stopping Multiple Events from Looping

I have a function that is intended to allow my user to move a set of objects (an airport) to a new location.
On the dialog that for this I have four controls - two textboxes that represent coordinates and two numeric up downs one for distance and one for bearing. The user can choose to enter either a distance and bearing or a set of coordinates. This is done via a checkbox and the non used set is disabled. So that the user has feedback I would like to update the disabled pair values as the user enters the enabled pair. If they are entering coordinates then the distance and bearing updates and vice versa.
My method for doing this is to respond to the TextChanged event of each object to update the others. Of course this creates one event firing another and back again as the other control updates. It does not create an infinite loop as far as I can tell but it does mess up the caret position in the active boxes.
I have tried some approaches including including trying to disable events based on which boxes are active or using some bool flags. I also tried using a timer rather than events. It is all very messy and no approach seems to work. I would like to know if there is a better way.
Thanks
Assuming you are using Windows Forms:
// this.ckPosition.CheckedChanged += new System.EventHandler(this.ckPosition_CheckedChanged);
private void ckPosition_CheckedChanged(object sender, EventArgs e)
{
txtX.Enabled = txtY.Enabled = ckPosition.Checked;
numBearing.Enabled = numDistance.Enabled = !ckPosition.Checked;
}
// this.txtX.TextChanged += new System.EventHandler(this.Position_TextChanged);
// this.txtY.TextChanged += new System.EventHandler(this.Position_TextChanged);
private void Position_TextChanged(object sender, EventArgs e)
{
if (ckPosition.Checked)
{
// Replace with your calculations
numBearing.Value = Convert.ToInt32(txtX.Text);
numDistance.Value = Convert.ToInt32(txtY.Text);
}
}
// this.numBearing.ValueChanged += new System.EventHandler(this.BearingDistance_ValueChanged);
// this.numDistance.ValueChanged += new System.EventHandler(this.BearingDistance_ValueChanged);
private void BearingDistance_ValueChanged(object sender, EventArgs e)
{
if (ckPosition.Checked == false)
{
// Replace with your calculations
txtX.Text = numBearing.Value.ToString();
txtY.Text = numDistance.Value.ToString();
}
}
As long as you don't change the position in the position event handler or the bearing/distance in it's handler, there should not be any 'looping' of events.
I know this is probably a big performance no-no but have you tried finding the origin of your event using reflection to tell if it's a forwarded event or not? you could look further up the stack to find out if the event is a forwarded event, or you could abort if the call stack depth gets ridiculous.
Or, if you're calling TextChanged events manually, you could derive from the eventArgs class to pass your own custom eventArgs and then abort if the eventArgs is your custom type or use the custom eventArgs to pass state information to help you.
Or maybe you could swap the TextChanged event that sits on the control with a similar event handler of your own.

C# TreeView SetFocus firing leave event twice

I have this simple code, where when the user leaves the TextBox control, TreeView gets focused:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.treeView1.Nodes.Add("A");
this.treeView1.Nodes[0].Nodes.Add("A.A");
this.treeView1.Nodes.Add("B");
this.treeView1.Nodes[0].Nodes.Add("B.A");
}
private void textBox1_Leave(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Leave..");
this.treeView1.Focus();
}
}
If we execute this code the Leave event is fired twice:
Leave..
Leave..
But if we set focus to other control, only one Leave event is fired.
Is that a problem of the TreeView? Do you know any workaround? Should we report this to Microsoft?
Thanks,
RG
this.treeView1.Focus();
Do not use the Focus() method in an event handler that's called because of a focusing event, like Leave. If you need to prevent a focus change then use the Validating event instead. Setting e.Cancel = true stops it.
But do note that this isn't very logical to do so for a TreeView, there isn't anything the user can do to alter the state of the control. You'll trap the user. Maybe that was the intention, do make sure the user can still close the window. If not then you might need the FormClosing event to force e.Cancel back to false.
Given that there is no code there to wire up the event I'm guessing you did it from the designer which means a line of code such as
textBox1.Leave += new EventHandler(textBox1_Leave);
will have been added to the Form1.designer.cs, check this file to ensure the line doesn't exist more than once as for each time this line is run you will get an event trigger, so if you run the line 3 times the Leave event will fire 3 times when you leave the textbox!
HTH
OneShot

Categories