Disabling a Textbox Using TextChanged Event - c#

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.

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

VisualC# Cannot Change DisplayName of Control

So I was make the display name (content) of a Lable from another page of the GUI. After realising a public static void function can't change the Displayname because it's not static, I messed around with Events and got a Handler set up to run when there other page publishes the event.
It looks like this
public void UpdateEventHandler(object sender, System.EventArgs e)
{
System.Windows.Forms.MessageBox.Show("Event Received");
tab1.DisplayName = Globals.tab1Name;
}
When the event goes off the message box pops up but the DisplayName does not change.
There was no errors, nothing.
The problems is not Globals.tab1Name because I ran it through the MessageBox and it was fine.
So I made another button, on the same page as the Lable:
private void Button_Click(object sender, RoutedEventArgs e)
{
tab1.DisplayName = Globals.tab1Name;
System.Windows.Forms.MessageBox.Show("clicked");
}
This time all the code worked, lable changed and msgbox popped up.
I made another function with the same two lines of code in it, called it with the event handler, again only the msgbox works. But when I call the same function with the button it all works.
Any help would be great, thanks in advance.
MessageBoxes pause the execution of the current thread. Therefore, when you show the MessageBox first the second line isn't hit thus the string isn't updated
Try adding the display name as a parameter for the form load. By default the form has sender and e as parameters, but you can always add your own too.

How do I prevent any button click events from queuing until the event handle is finished with the first call

I want to prevent a button click from queuing. In testing I have a Form, a Button and in the Code-Behind I have the event handler:
private void button1_Click(object sender, EventArgs e)
{
if (_codeRunning)
return;
_codeRunning = true;
//Application.DoEvents();
//button1.Enabled = false;
_click ++;
Debug.WriteLine("Click Number: " + _click);
Task.Delay(5000).Wait();
//button1.Enabled = true;
_codeRunning = false;
}
When I run debug and click the button twice or three or four times rapidly, Debug Output shows each click about five seconds after the last one. What I would like it to show is a single Click and drop the rest until first Event is complete.
I have also tried to disable the button, as well as temporarily remove the Handler from the Button_click event. It is all the same results.
There are various amounts of trouble you'll get into when you hang-up the UI thread like this. This is certainly one of them, nothing pleasant happens when the user wildly bangs on the button to try to get something noticeable to happen. And sure, those clicks won't get lost, they stay stored in the message queue. To activate your Click event handler again when your event handler stops running.
Pretty important to learn how to use the BackgroundWorker or Task classes to avoid this kind of trouble. Just setting the button's Enabled property is then enough to solve this problem.
Purging the mouse clicks from the message queue is technically possible. But ugly to do, it requires pinvoke. I'll hesitantly post the alternative, don't assume that this is in general a good strategy. You'll need to read this post to have some insight into why DoEvents() is a dangerous method.
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
button1.Update();
'' long running code
''...
Application.DoEvents();
if (!button1.IsDisposed) button1.Enabled = true;
}
The Update() call ensures that the user gets the feedback he needs to know that banging the button repeatedly isn't going to do anything useful. The DoEvents() call will dispatch all the queued-up mouse clicks, nothing happens with them since the button is still disabled. The IsDisposed test is essential to solve the problem with DoEvents(), it ensures your program won't crash when the user clicked the window's Close button while the code was running.
Use the HourGlass class in this post to provide more feedback.
I had a button that on click event was going to run a method. Same issue happent and when the user clicked multiple times the method was triggered multiple times. So I made a boolean and changed it value when the method started.
private bool IsTaskRunning = false;
private void MyMethod()
{
if ( IsTaskRunning==false )
{
IsTaskRunning=true;
// My heavy duty code that takes a long time
IsTaskRunning=false; // When method is finished
}
}
So now the method runs only if it's done the last time.

Displaying a MessageBox over a DialogForm causing a weird UI glitch

I've got a button on my form, and when it's clicked it does the following:
private void btnCheckSVN_Click(object sender, EventArgs e)
{
wait = new DevExpress.Utils.WaitDialogForm("Fetching File SVN Status",
"Please Wait");
wait.AutoSize = true;
wait.Visible = false;
bgwSVN.RunWorkerAsync();
wait.ShowDialog();
}
private void bgwSVN_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = svn.SvnStatusEventArgsToDataTable(svn.CheckSVN(_localPath));
}
private void bgwSVn_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DataTable dt = (DataTable)e.Result;
dt.DefaultView.Sort = "File ASC";
gdcSVN.DataSource = dt;
gdcSVNDefaultView.BestFitColumns();
xtcTabs.SelectedTabPageIndex = 1;
lblTotalFileCount.Text = dt.Rows.Count.ToString();
if (dt.Rows.Count == 0)
XtraMessageBox.Show("No files found.",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
wait.Close();
}
With this order of operations, you can (obviously) still see the Wait Dialog behind the MessageBox and it just looks really sloppy in my opinion.
If I switch the IF statement and the Close() call, my WaitDialogForm half-disappears for lack of a better term. There's an empty rectangle with a red frame and a giant X going through it, then the MessageBox on top of that as its own form.
This is my first time messing around with any sort of multithreading or using the BackgroundWorker objects at all, so I'm sure I'm just missing something really stupid but I don't know what?
I tried completely removing the MessageBox from this event, and instead doing this:
wait.Close();
lblTotalFileCount.Text = dt.Rows.Count.ToString();
Then handling the label's TextChanged event and checking to see if it should display the MessageBox then... but I still get the same results with the red box.
Edit; Also, a weird thing I noticed. On form load, lblTotalFileCount was set to 0. When this method runs and say it found 0 files, it would re-set lblTotalFileCount.Text to zero... Which did not trigger the TextChanged event? I understand that it was being set to the same thing it already is, but it was still technically changed. Am I missing something, or does the event actually check for that condition and "skip" it if that's the case? I had to just set the label to be an empty string instead... But say they run the application, it gets set to 0, alerts them, they make some changes, run it again... still 0, it won't alert them.
Edit 2; I thought maybe if I tried calling wait.Close() in the TextChanged event for the label as mentioned in my first edit before the MessageBox shows up, maybe it'd work? Nope. Same Problem. And then if I click the button a second time, the form crashes because wait has been disposed? It's a form level property why would it get disposed?
Edit 3; Well, if I call wait.Dispose in the TextChanged event, I get the desired behavior. Except it minimizes the main application and just shows me the MessageBox unless there's nothing behind it. Son of a...
Make Thread-Safe Calls to Windows Forms Controls. To modify a winform or a control from another thread (BackgroundWorker does that) you should make this call from the main thread. In this question you have your answer: How to update the GUI from another thread in C#?

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

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.

Categories

Resources