How to handle the user changing his mind without boolean flags? - c#

I have a NumericUpDown in my application but it is dangerous. When the value is changed the entire document is erased. Because of this, I'd like to give the user a warning (even if he accidentally hits OK he can undo it.)
The problem is that it seems that the only event I could handle would be the ValueChanged event and I'd end up with code like this.
private bool ignoreValueChanged = false;
private void numFoobar_ValueChanged(object sender, EventArgs e)
{
if (ignoreValueChanged)
{
ignoreValueChanged = false;
return;
}
if (MessageBox.Show("This will erase the entire document. Are you sure?", "Confirmation", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
{
ignoreValueChanged = true;
numFoobar.Value = oldValue; // The ValueChanged event gets called again =/
return;
}
// More code
}
There has got to be a better way. I was hoping Validating would help but it is only called when closing the form it seems.

Oh well you could remove the event subscribed to the numericUpdown control before resetting its value, after resetting it then again subscribe it back. This way, the event is not called when you reset the value.
But i am also thinking about how to check if the event is already subscribed or not. But above said method shall give you half the solution.
Here i tried this a bit and it seems to work but i cant seem to figure out how to check if already that same event is subscribed or not.
void NumericUpDown1ValueChanged(object sender, EventArgs e)
{
if(numericUpDown1.Value > 10)
{numericUpDown1.ValueChanged -= new System.EventHandler(this.NumericUpDown1ValueChanged);
numericUpDown1.Text = "5";
}
else numericUpDown1.ValueChanged += NumericUpDown1ValueChanged;//Here i need to first check if already it is subscribed or not before such that i dont want to subscribe double time
}

Did some Googling, and here's something that might work:
private void numFoobar_ValueChanged(object sender, EventArgs e)
{
this.ValidateChildren();
}
private void numFoobar_Validating(object sender, CancelEventArgs e)
{
if (MessageBox.Show("This will erase the entire document. Are you sure?", "Confirmation", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
{
e.Cancel = true;
}
}
Note that you'll need to reset the value as canceling the validation doesn't change the value. But this is the only way I was able to get the Validating event to fire.
ContainerControl.ValidateChildren Method
There are couple of issues to work out with this, however:
When exiting the program, it will fire the Validating event again; probably need to handle it in one of the closing events for the form or application.
I played with resetting the value in the ValueChanged event, but that trigged the Validating event again.
I'll keep playing with it for a bit and see if I can come up with a more solid solution for you.

This is really a usability issue. I guess what you are trying to do is to ignore the valueChanged event when the value has changed to the current persistent value. One option is to compare with the current value the document is based on.

Been googling a bit. First, I came up with this:
typeof(NumericUpDown).GetField("currentValue", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(numericUpDown1, 5m);
Which works but it is reflection and it seems a little over the top so I decided against it. Then I found this:
C# winforms numericupdown control
And based my solution on the second answer, which isn't so bad to be honest.

Related

Calling a KeyDown Event on Form Load

I would want to launch a KeyDown Event on Form_Load however its taking me somewhere else in the Form_Load event.
Form_Load:
int static_int = 0;
private void Form1_Load(object sender, EventArgs e)
{
if(condition == true)
{
txtInput.Text = "something";
txtInput.Focus();
SendKeys.Send("{Enter}");
int somegeneratednubmer = 20;
static_int = static_int + somegeneratednumber;
//somemore code here
}
}
KeyDown:
private void txtInput_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Enter)
{
static_int = 10;
//somemore codes here too
}
I would like to get the SUM of static_int and somegeneratednumber which is 30. However, after Debugging, I'm getting its initialized value of 0. From what I understood, after SendKeys.Send("{Enter}") the KeyDown event should proceed.
Why is it not??
How would I get the correct result? I really should do the KeyDown event on Form_Load, a conditional event...
or What am I doing wrong here?
Note: originally static_int is initialized on a Class
No, the KeyDown even will proceed at the earliest possible moment, which is when the appropriate message is executed from the form's message queue. That cannot happen before the Load event finishes, because that also on the message queue. Even if that weren't the case, SendKeys doesn't wait for the action to be processed. It just sends the message and returns immediately.
Another problem is that SendKeys sends the virtual keys to the currently active window. That can never be your window, since your window isn't even shown yet! When something behaves weird, a good first step is to read the documentation.
So, why is the value of static_int zero, instead of 20 or 30? Well, the likeliest case is an unhandled exception, and I'm pretty sure that's exactly what happens when you do tbxInput.Focus. The control doesn't quite exist yet, and it can't be made the input focus. If you have trouble understanding all this, you might want to find some book on the basics of how Windows windows work - there's nothing .NET can do about it, and it's places like this where the (very pretty) .NET abstraction leaks a lot. If you're planning to do any Windows UI development, you really need to know at least the basics.
However, that's completely unnecessary anyway. You don't have to execute a KeyDown event. Just make a method that's called from both the Load event handler and the KeyDown event handler.
try adding this event instead
Form1 isn't loaded yet so no events yet.
private void Form1_Shown(Object sender, EventArgs e)
{
SendKeys.Send("{Enter}");
}
But truly this design is wrong

Stop events in WPF

Is it possible to stop/suspend raised events in WPF? I want to stop events like SizeChanged, LocationChanged, MouseDown, when my window touches a screen side.
For now on I cannot modify window dimensions, because somehow other events overwrites the values.
EDIT I've discovered, that my problem is caused by raised events which overwrites my window object (i guess so), because when I've printed window dimensions in all the events, it occurred that after change other event causes setting original dimensions.
To unsubscribed from an event just type this in the place where you want to stop.
eg: SizeChanged;
this.SizeChanged -= MethodName;
AFAIK you cannot suspend firing of events. You can either unsubscribe using EventHandlerssimilar to subscribing them:
object.someEventHandler -= someMethodOrDelegate
or you can suppress the bubbling up/tunneling down of event based on special condition:
private void SomeEventHappened(object sender, RoutedEventArgs e)
{
if(someCondition)
e.Handled = true;
else
DoSomeWork();
}
As per your edit on question and your comments, as far as I understood you have events that change window dimensions, and these prevent you from resizing window manually. Moreover, your eventhandlers are not WPF originated (they use EventArgs instead of RoutedEventArgs). If I understood correctly, a solution may be having a class-scoped (or internal, based on your needs) boolean variable that holds whether to allow/overwrite dimensions or not:
bool allowManualResize = false;
And then when you are doing size modifications manually, you set this variable to true:
private void SomeManualSizingEventHandler(object sender, EventArgs e)
{
allowManualResize = true;
....
After these, you can check for this value in your events that override your new values:
private void SomeUpperLevelResizeHandler(object sender, EventArgs e)
{
// Check if resizing is allowed,
if(!allowManualResize)
{
// do your default things here
....
Of course you need to find a suitable location to set allowManualResize to false so that future firings are not suppressed.

Displaying a MessageBox prevents IsChecked from firing

I have a few TextBoxes that are bound to a single CheckBox.
This is the logic associated with it:
If the Checkbox is checked, it will overwrite any existing text in the associated TextBoxes and make it '0'++
If all of the TextBoxes have a score of '0,' the CheckBox should
become disabled and checked.
If any of the TextBoxes then change from '0,' it will become
enabled and unchecked.
++ *Note:* The caveat to this is if the TextBox has a value of 'C.'
Okay, so the issue I have is implementing the caveat above when one of the associated TextBoxes has a value of 'C.' What I would like to have happen is loop through the TextBoxes and check to see if any are scored 'C.' If one is found, display a warning message to the user confirming if they want to proceed. If Yes is selected, all associated scores will be overwritten to '0.' If No is selected then the Checked event should be cancelled.
To accomplish this, I added Event Listeners for the CheckBox.PreviewMouseDown and CheckBox.Checked events. Here is the code for my CheckBox.PreviewMouseDown event listener:
private void NormalCheckBoxControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
CheckBox normalCheckBox = (CheckBox)sender;
bool showCorrespondingScoreWarningMsg = false;
//Get a Row to loop through the AssociatedAdqScore controls for each
ScoreControl tempScoreControl = new ScoreControl();
foreach (ScoreControl score in this.ScoreControlList)
{
if (score.ScoreTextBox.Text == "C")
{
showCorrespondingScoreWarningMsg = true;
}
}
if (showCorrespondingScoreWarningMsg)
{
MessageBoxResult msgResult = InformationBox.Show("WARNING: Proceeding will remove corresponding 'C' scores. Continue?", "Continue?", ButtonStyle.YesNo, IconStyle.Question);
if (msgResult == MessageBoxResult.No)
{
e.Handled = true;
}
}
}
This works if the user selects 'No,' however the issue I'm having is that when choosing 'Yes,' the CheckBox.Checked event still does not get fired. I have tried to manually set CheckBox.IsChecked = true; if if (msgResult == MessageBoxResult.Yes), but this breaks the bindings so that is not a viable solution.
Is there any way I can resolve this issue and proceed with the NormalCheckBoxControl_Checked(object sender, RoutedEventArgs e) event if the user selects 'Yes?'
I don't know of a "Checked" event, though there is a CheckedChanged event and also a CheckStateChanged event. I am used to .NET 2.0, so this may be a 3.0+ thing. Either way, I think I have manually called the event handler in instances like this without any problem.
You can manually call the event handler with null params (or known object & new event args):
NormalCheckBoxControl_Checked(null, null);
or
NormalCheckBoxControl_Checked(new object, new EventArgs());
This should manually kick off your routine, and unless you need them, then there's really no problem with providing dummy params. No need to raise an event and wait for it to bubble, just call the routine.
Of course, if there are other routines which rely on the event bubbling or if you have multiple handlers for the same event, then it might cause you a problem. Be aware of that, just in case.

Disabling a Textbox Using TextChanged Event

The form I am using requires a copy pasted URL. I am trying to have a textChanged event that will check the url as soon as it is pasted, telling the user whether it is valid or invalid. I also want to be able to lock out the textbox when this happens, with a message saying something like "Processing...".
The problem is with the code below, the textbox is never disabled, the program will do the checkUrl() method and the textbox is never disabled even though it is first to execute (I assume it is but the fact there is a function call right underneath it is messing around with something or getting higher priority).
How do I go about making the control visually disabled while the method runs?
private void urlTxtBx_TextChanged(object sender, EventArgs e)
{
urlTxtBx.Enabled = false;
checkUrl();
urlTxtBx.Enabled = true;
}
I think this is happening because the Application needs to complete all the active threads before disabling the TextBox. Please try the following code:
private void urlTxtBx_TextChanged(object sender, EventArgs e)
{
urlTxtBx.Enabled = false;
Application.DoEvents();
checkUrl();
urlTxtBx.Enabled = true;
}
This will let the UI to be updated. For more details check here.

Triggering an event after a Winform layout is complete

I am working on a C# WinForm application.
I want to trigger some processing once the form has been "shown" and the layout of the form is complete.
I am using the "_Shown" event, but this seems to trigger before the layout of the form has completed. Is there event I can use that fires once the layout is complete?
Put Application.DoEvents() at the start of the form's Shown event handler. This will force all the controls to be rendered.
I don't see an event after Shown you can use for this purpose. Could you not use a timer to delay your processing in the Shown event?
An old trick in VB6 used to be to use the Paint event:
bool firstShown = false;
void form_Paint(Object sender, EventArgs e) {
if ( !firstShown ) {
YourMethodThatNeedsToRunOnShown();
firstShown = true;
}
//the rest of your paint method (if any)
}
It is a little hacky, but it does work
This works for me and is much less "hacky" than other suggestions:
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if(someControl == null)
return; // be careful of OnLayout being called multiple times
// otherwise, do some stuff here, set control sizes, etc.
}
AS far as I can remember the event order is something like
Form.Load
Form.Layout
Form.VisibleChanged
Form.GotFocus
Form.Activated
Form.Shown
So if something is still happening after Form.Show it's because of the way you coded it.
Are you maybe creating the form dynamically?
The best solution is the Shown() event: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.shown.aspx
"The Shown event is only raised the first time a form is displayed; subsequently minimizing, maximizing, restoring, hiding, showing, or invalidating and repainting will not raise this event."
Try using Form.GotFocus (inherited from control)..
something like this.
private void Form1_Load(object sender, EventArgs e)
{
this.GotFocus += new EventHandler(Form1_gotFocus);
this.Focus();
}
private void Form1_gotFocus(object sender, EventArgs e)
{
// You will need to Switch focus from form at the end of this function,
//to make sure it doesnt keep Firing.
}
According To msdn , the following happens:
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or SelectNextControl methods, or by setting the ContainerControl..::.ActiveControl property to the current form, focus events occur in the following order:
Enter
GotFocus
Leave
Validating
Validated
LostFocus

Categories

Resources