So my problem is the following:
I have a button, which has a set background. If the button is pressed, I call the function, which is called check();
The problem is, that if the button is pressed, I will set the background to a certain picture, and if I call the fucntion check, and in that check, sometimes I delete the background.
So sometimes without even seeing the background it gets deleted. How can I improve it, so it gets deleted only like after 3-4 seconds?
private void button2_Click(object sender, EventArgs e)
{
Image img = Image.FromFile(#"C:\Users\nhorv\Downloads\javascript.jpg");
button2.BackgroundImage = img;
buttonList.Add(button2);
counter++;
check();
}
This is the button_click function, and this is the check() func:
void check()
{
if (counter == 2)
{
if (buttonList.Contains(button1) && buttonList.Contains(button6))
{
progressBar1.Increment(10);
}
.
.
.
else
{
buttonList[0].BackgroundImage = null;
buttonList[1].BackgroundImage = null;
}
buttonList.Clear();
counter = 0;
}
}
The buttons are stored in the buttonList list.
For the timing, I heard about: System.Threading.Thread.Sleep(1000); - for a 1 second delay.
Any help is really appreciated!
Edit:
The problem is with the buttons. If I call the button1_pressed function, it runs trough and only after that is the background changed. Unfortunately, there is the check function, which immediately deletes the background. So I have to somehow make the check function after the button press, so that the button can change the background, and only after that will I call check();
Use
System.Threading.Thread.Sleep(3000);
to pause the execution for 3 seconds and then delete the background based on the condition.
private void button2_Click(object sender, EventArgs e)
{
Image img = Image.FromFile(#"C:\Users\nhorv\Downloads\javascript.jpg");
button2.BackgroundImage = img;
buttonList.Add(button2);
System.Threading.Thread.Sleep(3000);
check();
}
So this is what I have understood from your code: you have button 2 with the event handler button2_Click associated on it.
Once you have clicked button 2, you set the background image to the button, add the BUTTON 2 to a list of button, increment a variable called counter by 1 and then call check function.
Check function, first of all, controls if counter is 2. If not, exit. Else, controls if button1 and button6, that I don't know where they're and if thery're added to to buttonList or not, are into the list or not. If they both are present, then increment the progressbar by 10. If one of them is not in the list, or both, you set the background immage of the first 2 buttons in the buttonList to null...
In the end, you clear the buttonList and set the counter to 0.
First of all, we should see the entire code in order to understand what's going on. Relying only on what I can see, start the programm in debug mode and check if button1 and button6 are in the list. In my opinion, you never add those 2 buttons to the list (is enough that only one of them is not present, since there's an and into the if), so the program will always delete the background image.
If I understand you correctly, you want to delay the check() function that deletes the background. You will need to create a thread that will do that:
async Task check()
{
if (counter == 2)
{
await Task.Delay(3000);
...<your other code>
}
}
Your button click then becomes:
private async void button2_Click(object sender, EventArgs e)
An you call your check method:
_ = check();
To create a small delay (so the player can check if they match, you should go for the async method)
But you need to prevent that, when the Task.Delay is running, a button is disabled. The await Delay ensures that the paint messages are handled, but also new buttons can be clicked.
Here an example:
private bool _buttonsEnabled = true;
private async void button2_Click(object sender, EventArgs e)
{
if(!_buttonsEnabled)
return;
_buttonsEnabled = false;
Image img = Image.FromFile(#"C:\Users\nhorv\Downloads\javascript.jpg");
button2.BackgroundImage = img;
buttonList.Add(button2);
counter++;
await check();
_buttonsEnabled = true;
}
private async Task check()
{
if (counter == 2)
{
if (buttonList.Contains(button1) && buttonList.Contains(button6))
{
progressBar1.Increment(10);
}
.
.
.
else
{
// wait for a second before clearing the background.
await Task.Delay(1000);
buttonList[0].BackgroundImage = null;
buttonList[1].BackgroundImage = null;
}
buttonList.Clear();
counter = 0;
}
}
You could set up a timer (see https://learn.microsoft.com/dotnet/api/system.windows.forms.timer) after the second button was pressed and reset the button images in the event handler of elapsed timer. (event is named Tick)
Related
I want to disable a textbox while processes are running in a button click event.
I noticed that the textbox gets disabled after the event while the radiobuttons in the groupbox gets disabled immediately.
The button should be disabled at the start and depending on the return values of somemethods it should get back enabled or stays disabled.
Thats my current code:
private async void BtnConfirmClick(object sender, EventArgs e)
{
try
{
textbox.Enabled = false;
groupbox.Enabled = false;
await somemethod()
...
}
}
I've the same problem when changing the text while in the button event.
private async void BtnConfirm_Click(object sender, EventArgs e)
{
try
{
// you dont want user to click twice
BtnConfirm.Enabled = false;
// ConfigureAwait(false) configures the task so it doesn't need to block caller thread
await Somemethod().ConfigureAwait(false);
}
finally
{
// BeginInvoke prevents thread access exceptions
BeginInvoke((Action)delegate
{
BtnConfirm.Enabled = true;
});
}
}
private async Task Somemethod()
{
await Task.Delay(TimeSpan.FromSeconds(5));
}
There it is. Be sure to do any UI manipulations in BeginInvoke after ConfigureAwait(false) because task is probably going to end up on another thread.
Hope it helps.
private void AddMyScrollEventHandlers()
{
VScrollBar vScrollBar1 = new VScrollBar();
}
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
break;
}
System.Threading.Thread.Sleep(200);
}
}
private void button2_Click(object sender, EventArgs e)
{
// vScrollBar1.Scroll
}
I am new in C#. I was working on scroll. What I wanted here is, if anyone click button1 then scroll automatically move to the end and I wanted to show gradual value in label1. Also when someone click button2 scrolling stop.
Now the problem is label1 do not show gradual change in value. It shows value once when the scrolling stop.
Also when scrolling continue i,e when while loop is working I can not click on button2. Actually I can not click on the form even.
Someone please give me some idea how to do this.
This happens because the thread that is performing the task is busy, and it's the same thread that updates the UI. You can use a multithreading solution. Take a look at
BackgroundWorker
All the UI events run in the main thread of the application, so the application can only process one event at a time. When the application is processing an event, no other event will be processed.
Since you are doing a UI related work periodically, the best option is to use the Timer class:
Drop Timer from the toolbox into the form.
In the properties window, set the interval to 200.
Double click the timer object to create the Tick event handler.
Put this code in the newly created timer1_Tick method:
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
timer1.Stop();
}
Change your methods as below:
private void AddMyScrollEventHandlers()
{
timer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
}
Now you're done.
I would recommend using BackgroundWorker control, as suggested by Agustin Meriles. However, one more important thing to note is that You should use Control.Invoke(...) method to update controls from another thread.
I've modified Your code, tested it in a sample application and it seems to work correctly.
First, add a new BackgroundWorker control to Your form and assign backgroundWorker1_DoWork to its DoWork event.
Then, You can use the code below:
private void button1_Click(object sender, EventArgs e)
{
//code from here is moved to BackgroundWorker control
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//while (true)
//the condition directly in the while looks more clear to me
while (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
//update controls using Invoke method and anonymous functions
vScrollBar1.Invoke((MethodInvoker)delegate() { vScrollBar1.Value += 1; });
label1.Invoke((MethodInvoker)delegate() { label1.Text = vScrollBar1.Value.ToString(); });
//when called inside BackgroundWorker, this sleeps the background thread,
//so UI should be responsive now
System.Threading.Thread.Sleep(200);
}
}
If You have any problems when using this code, please let me know.
Update
As mentioned in the comments, You could also use ProgressChanged event of the BackgroundWorker. It requires some more changes in the code, but is more suitable in this case. You can find some information about it here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx.
If You are not going to add any other code with more processing in the while loop, You can also use Timer control, as suggested by MD.Unicorn in his answer.
I've got a button in an app. I want it to do different things depending on whether I tap it (pause an animation) or double-tap (restart the animation)
however, when I double-tap, it seems to fire the tap event first and then the double-tap in quick succession. Is there a way around this? Is this a known issue or am I making a rookie mistake?
Edit: For those asking, I'm using the Tapped and DoubleTapped events.
Give the some pause to single tab . If there is double tab occur then single tab event eliminated
bool singleTap;
private async void control_Tapped_1(object sender, TappedRoutedEventArgs e)
{
this.singleTap = true;
await Task.Delay(200);
if (this.singleTap)
{
// Single tab Method .
}
}
private void control_DoubleTapped_1(object sender, DoubleTappedRoutedEventArgs e)
{
this.singleTap = false;
// Double tab Method
}
You might want to consider letting it do both. Think about it like double clicking a folder in windows. Users will be used to something happening on the first click (highlighting the folder) and they will be expecting the double click to both highlight, then navigate.
All in all it seems like a design issue, not a technical one.
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
bool _singleTapped = false;
//Tapped event handler
private async void control_Tapped(object sender, TappedRoutedEventArgs e)
{
_singleTapped = true;
//var x = GetDoubleClickTime();
//GetDoubleClickTime() gets the maximum number of milliseconds that can elapse between a first click and a second click
await Task.Delay(TimeSpan.FromMilliseconds(GetDoubleClickTime()));
if (!_singleTapped)
{ return; }
//SingleTapped code
}
//DoubleTapped event handler
private async void control_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
_singleTapped = false;
//DoubleTapped code
}
I am making a application where i would like to log in with a face detection. But this is not real, its just to make ik look like its scanning.
So when i press the LOG IN button, the kinect takes my picture and show me the picture, on top of it is showing me in a text that its scanning.
Now i am stuck with the following issue, when i press the login button, the scanning label appears, but i would like to fire an other event that takes me to the next page, the homepage.
So i want the SCANNING label appearing for 3 seconds, and then the page should change.
This is what i tried, i worked with a timer, but that doesnt do annything.
private void ActionButton_Click(object sender, System.EventArgs eventArgs)
{
_main.TakePicture();
identifyBox.Source = _main.source.Clone();
scanningLabel.Visibility = Visibility.Visible;
_storyboard = (Storyboard)FindResource("scanningSB");
//_storyboard.Begin();
Start();
}
private void Start()
{
_tm = new Timer(3000);
_tm.Elapsed += new ElapsedEventHandler(_tm_Elapsed);
_tm.Enabled = true;
}
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tm == new Timer(3000))
{
((Timer)sender).Enabled = false;
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
Okay i removed the if statement, but now it fires every 3 seconds a method.
How can i make it work 1 time.
Ok even this works, now i my ContentPage wont change? It gives me this error: The calling thread cannot access this object because a different thread owns it.
What can be wrong?
I think you can remove condition
if (_tm == new Timer(3000))
and keep it simple
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer)sender).Enabled = false;
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
when you set _tm = new Timer(3000); it will set the time to fire event after 3 seconds..
Change the _tm_Elapse to this:
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tm == (sender as Timer))
{
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
Edit for answering:
"I just want it 1 time to fire after 3 sec"
void _tm_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tm == (sender as Timer))
{
_tm.Stop();
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
}
This is even simpler, and it will only run once per call, the three second timer is built in, and furthermore, it won't disable other program functionalities while it is running:
async Task Start()
{
await Task.Delay(3000);
_main.ContentPage.Children.Clear();
_main.ContentPage.Children.Add(_homeScreen);
}
I would like to use the following code in C# but I just can't seem to get out of it. I would like to terminate the application if the user presses a key or moves the rodent (aka mouse). Here is my code (no laughing!).
private void frmDots_KeyDown(object sender, KeyEventArgs e)
{
bgNotClicked = false;
Close();
}
private void frmDots_Click(object sender, EventArgs e)
{
bgNotClicked = false;
Close();
}
while (bgNotClicked)
{
// Clear the first element in our XY position. This is the reverse of the way I normally create the dots application
System.Drawing.Rectangle clearDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the black color and brush to clear dots
Color clearDotsColor = Color.Black;
SolidBrush clearDotsBrush = new SolidBrush(clearDotsColor);
// Finally clear the dot
e.Graphics.FillEllipse(clearDotsBrush, clearDots);
GetRandomPosition(iCounter);
// Fill the elements to display colors on the displays canvas
System.Drawing.Rectangle colorDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the color and brush to show dots
Color colorRandom = GetRandomColor();
SolidBrush colorBrush = new SolidBrush(colorRandom);
// Finally show the dot
e.Graphics.FillEllipse(colorBrush, colorDots);
Thread.Sleep(5);
iCounter++;
if (iCounter == 399)
{
iCounter = 0;
}
}
}
Your "busy waiting" strategy is poor design. Instead, you should use event handlers that are fired:
On keypress.
When the mouse is moved.
In either case, you can respond by terminating the application.
Edit:
After seeing your edit, this is definitely your problem. The issue is that your while loop blocks the main UI thread, so it never handles the Windows Messages which trigger your key press/mouse/etc handlers.
You have a couple of options - you can either move some of this onto a separate thread, do what I suggested below, or add a call to Application.DoEvents in your while loop. This would allow your event handlers to run, which would in turn set bgNotClicked = false;. Right now, that's never occurring because your UI is blocked entirely.
Original Post:
If you're doing this loop in your UI thread, you're going to need to rethink the design a bit.
Setting bgNotClicked = false; somewhere in an event handler will work, but only if your event handler is able to run. If you're doing the above code in the UI thread, it will block your UI thread indefinitely, preventing the event handler from firing.
I would recommend reworking this to be based off a timer (so it runs repeatedly on regular intervals), instead of locked into a while loop. This would allow your other UI events to fire between runs, and instead of setting bgNotClicked = false;, your event handler could just set the timer to be not enabled.
Your bgNotClicked variable needs to be set to false by your event handler for key-press.
If the rodent is moved by your mouse, you would need a similar mouse event handler.
The break keyword will terminate a loop. In this case, when you hit the case where you want to stop the loop, you would just use break;.
If you're looping like that you need to give the application a moment to process the events that you're hoping will cause the interruption. This is the job of the DoEvents method.
private bool WeAreDone = false;
private void DoingIt()
{
while (true)
{
Application.DoEvents();
if (WeAreDone)
{
break;
}
}
}
private void InterruptButton_Click(object sender, EventArgs e)
{
WeAreDone = true;
}
I think using a Timer fits the Windows event-driven model better than the busy wait while loop. You might try something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int iCounter = 0;
private void Draw()
{
// ....
}
private void timer1_Tick(object sender, EventArgs e)
{
Draw();
iCounter++;
if(iCounter == 399)
{
iCounter = 0;
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 5;
timer1.Enabled = true;
}
private void Form1_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
Close();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
timer1.Enabled = false;
Close();
}
}
This does not seems to be the correct way. .Net Framework has provided you with the events to handle the KeyPress and MouseMove/Click actions. Why are you not using them?
Try moving the loop into a BackgroundWorker's DoWork event handler. Then your GUI will still be responsive and instead of that nasty variable, you can just call the CancelAsync method to stop the loop.
You can exit the loop using the break statement.
EDIT: OK, I take back the flag thing!
Use Environment.Exit(2); (or Environment.Exit(1) it really doesn't make a difference) to exit out of the application.
Exit While
...................