How can I exit while loop with "ESC" at any time/ C# - c#

I have the following code in C#:
while (wechsel)
{
ProfDpDrv.MDPReadSlaveData(SlaveAddress, resetdiag, out dpData);
newStatus = dpData.m_InputData[i];
if (newStatus == 1)
{
int debug = e.RowIndex;
Dgv_Data_List.Rows[e.RowIndex].Cells["Adresse"].Style.BackColor = Color.Green; //
Dgv_Data_List.Refresh();
wechsel = false;
}
}
Here I want to EXIT the loop with the ESC if wechsel still at true. I have no console by the way.

Due to the fact, that we don't know where this code is running (WinForms, WPF, ASP, etc) except it is not a console it is hard to give you a concrete help.
A general advice to solve this issue, would be to create a CancellationTokenSource and give the source.Token to the long running method. This method can within the loop regulary check, if a cancellation was requested by checking token.IsCancellationRequested and then simply do whatever needed to leave a consistent state and exit the method.
And back to the beginning of my answer, it depends on the used interface you have to hook onto something to register a keypress and then calling source.Cancel().

I assumed that you are using WPF or WinForms.
Just subscribe to KetDown method on host window to capture all keys being pressed and then check if it's ESC and handle that appropriately, see code below:
public partial class MainWindow : Window
{
private bool wechsel = true;
public MainWindow()
{
InitializeComponent();
this.KeyDown += MainWindow_KeyDown;
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Escape) return;
wechsel = false;
txtInfo.Text = "interrupted by ESC";
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
int i = 0;
while (wechsel)
{
txtInfo.Text = i++.ToString();
await Task.Delay(500);
}
}
}

Related

How to make a program wait for user to type in a textbox?

When I run a WinForm program to a line, I would like to check if a textbox already has user input, if not, I will ask user to type in the textbox and wait till user types in some input, before running the next line of the code. I was wondering how to do the wait?
The program has to wait for the information required as input for the next line of code.
Thanks.
Waiting for something to happen in the GUI (using a timer, loops from other threads, etc...) is a massive waste of resources. Almost all functional programming languages have Events including C#
From Wikipedia:
event-driven programming is a programming paradigm in which the flow
of the program is determined by events such as user actions (mouse
clicks, key presses), sensor outputs, or message passing from other
programs or threads. Event-driven programming is the dominant paradigm
used in graphical user interfaces and other applications (e.g.,
JavaScript web applications) that are centered on performing certain
actions in response to user input. This is also true of programming
for device drivers (e.g., P in USB device driver stacks).
You can do it like this with the help of Control.TextChanged event inherited by the Textbox control:
private void Form1_Load(object sender, EventArgs e)
{
ValidateGUI();
}
private const int MIN_CHARS_TO_DO_SOMETHING = 8;
private const string NOT_VALID = "Oh No There is No User Input )-:";
private const string VALID = "Great We Can Do Something (-:";
private void textBox1_TextChanged(object sender, EventArgs e)
{
ValidateGUI();
}
private void ValidateGUI()
{
if (textBox1.Text.Length < MIN_CHARS_TO_DO_SOMETHING)
{
lblMessege.Text = NOT_VALID;
}
else
{
lblMessege.Text = VALID;
// Execute some code..
//...
//...
}
}
I'm going to assume you have a valid reason for waiting instead of monitoring input.
You just need to use background workers and then you need to set the DoWork event to wait for a specified amount of time, and the RunWorkerCompleted event to run your code checking if input is being made.
Here's an example assuming a label and a textbox are on the form already. Alternatively you can just add the background worker as a form element instead of creating it in code:
public Form1()
{
InitializeComponent();
waitForInput();
}
private void waitForInput()
{
BackgroundWorker waiter = new BackgroundWorker();
waiter.WorkerSupportsCancellation = true;
waiter.WorkerReportsProgress = true;
waiter.DoWork += wait10Seconds;
waiter.RunWorkerCompleted += doneWaiting;
waiter.RunWorkerAsync();
}
private void wait10Seconds(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000);
}
private void doneWaiting(object sender, RunWorkerCompletedEventArgs e)
{
if(textBox1.Text == "")
{
label1.Text = "Why haven't you typed anything?";
}
}
If I understand what you're getting at, a synchronization object like SemaphoreSlim might be a good fit for something like this. This declaration sets the initial count to 0 so the semaphore will block. The initSync method pauses halfway through and will await the textbox Enter key to release the semaphoreSlim before executing the next line. However, the UI thread is not blocked during the waiting period.
SemaphoreSlim _waitForText = new SemaphoreSlim(0, maxCount: 1);
private async Task initAsync()
{
richTextBox1.AppendText(
$"The async method that populates this RichTextBox waits indefinitely for input.");
richTextBox1.AppendText($"{Environment.NewLine}>");;
richTextBox1.SelectionColor = Color.Red;
// "check if a textbox already has user input"
if (string.IsNullOrWhiteSpace(textBox1.Text))
{
// "ask user to type"
textBox1.Text = "Enter ID";
await _waitForText.WaitAsync();
}
else onUserInputOK();
// The MOCK login has completed. Enable the app.
richTextBox1.Enabled = true;
richTextBox1.AppendText(
$"{Environment.NewLine}Now this method will complete, and you'll see a message box in 5 seconds");
}
Example
public MainForm() => InitializeComponent();
protected override async void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Disable UI response until "logged in"
richTextBox1.Enabled = false;
// Subscribe to TextChanged event
textBox1.KeyDown += detectUserInput;
await initAsync();
await Task.Delay(TimeSpan.FromSeconds(5));
MessageBox.Show("All done");
}
private void detectUserInput(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Return)
{
e.Handled = e.SuppressKeyPress = true;
if (!string.IsNullOrWhiteSpace(textBox1.Text))
{
onUserInputOK();
}
}
}
private void onUserInputOK()
{
richTextBox1.AppendText($"{textBox1.Text}");
Text = textBox1.Text; // Start echo to title bar
richTextBox1.SelectionColor = Color.DarkGreen;
// Remove this listener. Install normal runtime hook.
textBox1.KeyDown -= detectUserInput;
textBox1.TextChanged += normalTextboxListener;
_waitForText.Release();
}
private void normalTextboxListener(object sender, EventArgs e) => Text = textBox1.Text;

Check multiple checkbox.checked state inside a do while cycle

I asked in a previous question how to "Threading 2 forms to use simultaneously C#".
I realize now that I was not explicit enough and was asking the wrong question.
Here is my scenario:
I have some data, that I receive from a local server, that I need to write to a file.
This data is being sent at a constant time rate that I cant control.
What I would like to do is to have one winform for the initial setup of the tcp stream and then click on a button to start reading the tcp stream and write it to a file, and at the same time launch another winform with multiple check-boxes that I need to check the checked state and add that info simultaneously to the same file.
This processing is to be stopped when a different button is pressed, closing the stream, the file and the second winform. (this button location is not specifically mandatory to any of the winforms).
Because of this cancel button (and before I tried to implement the 2nd form) I used a background worker to be able to asynchronously cancel the do while loop used to read the stream and write the file.
private void bRecord_Click(object sender, EventArgs e)
{
System.IO.StreamWriter file = new System.IO.StreamWriter(AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".xml", true);
data_feed = client.GetStream();
data_write = new StreamWriter(data_feed);
data_write.Write("<SEND_DATA/>\r\n");
data_write.Flush();
exit_state = false;
string behavior = null;
//code to launch form2 with the checkboxes
//...
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
int var = data_feed.ReadByte();
if (var != -1)
{
data_in += (char)var;
if (data_in.IndexOf("\r\n") != -1)
{
//code to check the checkboxes state in form2
//if (form2.checkBox1.Checked) behavior = form2.checkBox1.Text;
//if (form2.checkBoxn.Checked) behavior = form2.checkBoxn.Text;
file.WriteLine(data_in + behavior);
data_in = "";
}
}
}
while (exit_state == false);
});
worker.RunWorkerAsync();
}
private void bStop_Click(object sender, EventArgs e)
{
exit_state = true;
worker.CancelAsync();
}
I hope I've been clearer now.
I not experienced in event programming and just started in C# so please try to provide some simple examples in the answers if possible.
At first would it be enough to use one Winform? Disable all checkboxes, click a button which enables the checkboxes and start reading the tcpstream? If you need two Forms for other reasons let me know, but i think this isn't needed from what i can see in your question.
Then i would suggest you to use the Task Library from .Net. This is the "modern" way to handle multithreading. BackgroundWorker is kind of old school. If you just able to run on .Net 2.0 you have to use BackgroundWorker, but don't seem to be the case (example follows).
Further if you want to cancel a BackgroundWorker operation this isn't only call CancelAsync();. You also need to handle the e.Cancelled flag.
backgroundWorker.WorkerSupportsCancellation = true;
private void CancelBW()
{
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork += ((sender, args)
{
//Handle the cancellation (in your case do this in your loop for sure)
if (e.Cancelled) //Flag is true if someone call backgroundWorker.CancelAsync();
return;
//Do your stuff.
});
There is no common way to directly cancel the backgroundWorker
operation. You always need to handle this.
Now let's change your code to the modern TAP-Pattern and make some stuff you want to have.
private void MyForm : Form
{
private CancellationTokenSource ct;
public MyForm()
{
InitializeComponent();
checkbox1.Enable = false;
//Disable all checkboxes here.
ct = new CancellationTokenSource();
}
//Event if someone click your start button
private void buttonStart_Click(object sender, EventArgs e)
{
//Enable all checkboxes here
//This will be called if we get some progress from tcp
var progress = new Progress<string>(value =>
{
//check the behaviour of the checkboxes and write to file
file.WriteLine(value + behavior);
});
Task.Factory.StartNew(() => ListenToTcp(ct, progress as IProgress<string)); //starts the tcp listening async
}
//Event if someone click your stop button
private void buttonStop_Click(object sender, EventArgs e)
{
ct.Cancel();
//Disable all checkboxes (better make a method for this :D)
}
private void ListenToTcp(CancellationToken ct, IProgess<string> progress)
{
do
{
if (ct.IsCancellationRequested)
return;
int temp = data_feed.ReadByte(); //replaced var => temp because var is keyword
if (temp != -1)
{
data_in += (char)temp;
if (data_in.IndexOf("\r\n") != -1)
{
if (progress != null)
progress.Report(data_in); //Report the tcp-data to form thread
data_in = string.empty;
}
}
while (exit_state == false);
}
}
This snippet should do the trick. I don't test it so some syntax error maybe occur :P, but the principle will work.
The most important part is that you are not allowed to access gui
components in another thread then gui thread. You tried to access the
checkboxes within your BackgroundWorker DoWork which is no possible
and throw an exception.
So I use a Progress-Object to reuse the data we get in the Tcp-Stream, back to the Main-Thread. There we can access the checkboxes, build our string and write it to the file. More about BackgroundWorker vs. Task and the Progress behaviour you can find here.
Let me know if you have any further questions.

Check if event (doubleClick) is running

I am writing a tool which switchs between a lot of states. For some events I need to be sure they wont get executed a second time while the called function (inside the event) is running. This is how I managed it before:
// Global variables //
public bool func1IsRunning = false;
public bool func2IsRunning = false;
...
public void listView_DoubleClick(object sender, EventArgs e)
{
if(!func1IsRunning)
{
func1();
func1IsRunning = false;
}
}
public void func1()
{
func1IsRunning = true;
// some code in here //
}
But with every extension of my tool the list of the global variables grows up. Also the events and functions getting less clear to read.
Isnt there a way like this(?):
public void listView_DoubleClick(object sender, EventArgs e)
{
if(DoubleClick.IsHandled)
{
func1();
}
}
public void func1()
{
// some code in here //
// ................. //
DoubleClick.IsHandled = true; // at the end of the function //
}
So what I am looking for is a way to determine if an event is still running or not. My code is working, im just unhappy with how it looks like.
Any ideas?
UPDATE 1
I decided to use Steve's answer as it solves my problem by the clearest way.
Anyway it is NOT running correctly for now.
Here is how my code looks like:
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= new EventHandler(listView_DoubleClick);
itemEdit();
}
finally
{
listView.DoubleClick += new EventHandler(listView_DoubleClick);
}
}
The code above is NOT disabling the handler.
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
itemEdit();
}
finally
{
listView.DoubleClick += listView_DoubleClick;
}
}
This code is also not disabling the handler.
This is the line where the handler gets enabled (MainForm.Designer.cs):
this.listView.DoubleClick += new System.EventHandler(this.listView_DoubleClick);
There are no errors raised. The event just gets fired again and again. Where is the problem?
UPDATE 2:
As Sinatr asked in the comments below if my function is really waiting or just enabling user input he discovered where the mistake was made.
Steve's answer is correct according to my wrong written question. Thanks a lot to all of you guys.
Just disable the event handler
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
listView.DoubleClick += listView_DoubleClick;
}
}
EDIT
Looking at your comment I suspect that this DoubleClick event is assigned to more than one control. If this is the case, using the global listView global instance of a listview doesn't disable the double click on other controls that are linked to the same code.
If this is the case then you need a more generic approach
public void listView_DoubleClick(object sender, EventArgs e)
{
Control c = sender as Control;
try
{
if(c != null)
{
c.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
if(c != null) c.DoubleClick += listView_DoubleClick;
}
}
Of course, this is just to enable/disable DoubleClicks events, it cannot works if you assign this event handler to other standard events like Click that have the same signature (object sender, EventArgs e)
How about something like the following using locks:
private object globalLock = new object();
private Dictionary<int, object> lockObjects = new Dictionary<int, object>();
public void listView_DoubleClick(object sender, EventArgs e)
{
object lockObject;
lock (globalLock) // to avoid two threads creating the object
{
if (!lockObjects.ContainsKey(1))
lockObjects.Add(1, new object());
lockObject = lockObjects[1];
}
if (Monitor.TryEnter(lockObject) // enter only if no thread has already entered
{
try { func1(); }
finally { Monitor.Exit(lockObject); }
}
}
This is different to Steve's logic in the matter that it is thread-safe.
A simple state-machine should solve your problem without requiring too many variables. Create an Enum named AppState like this:
enum AppState
{
Ready = 1,
InsideListView1Click = 2,
InsideListView1DoubleClick = 3
InsideListView2Click = 4,
InsideListView2DoubleClick = 5
}
This enum could grow as you add new controls and/or event-handlers to your application. Now use a single global variable that keeps track of the application state and modify it inside event-handlers appropriately:
private AppState m_State = AppState.Ready;
And in the event-handlers you would do:
private void ListView1_DoubleClick(object sender, EventArgs e)
{
lock
{
if(m_State != AppState.Ready)
return;
else
m_State = AppState.InsideListView1DoubleClick;
}
//Do your stuff
m_State = AppState.Ready;
}
This way newer calls will be ignored instead of being queued. If you expect to be in multiple states at the same time, you could apply [Flags] attribute on this enum as well. Also note that enums are thread-safe and evaluating them is atomic, so multi-threading shouldn't be a problem either.

How to avoid copy/paste many event handlers

My application cannot access a specific menu item unless some condition is true (DataRepository.IsAllDataLoaded). I came up with this code, which works great. It checks for the condition first. If it is not ready, it calls a timer, which waits some milliseconds and call the same method again. The Timer needs an ElapsedEventHandler.
public void FirstMenuItem_Click(object sender, RoutedEventArgs e)
{
if (!DataRepository.IsAllDataLoaded)
{
WaitForDataLoading(FirstTimedEvent);
}
else
{
Dispatcher.BeginInvoke(new Action(() =>
{
IndividualEntryWindow Window = new IndividualEntryWindow();
Window.Show();
}));
}
}
private void FirstTimedEvent(object source, ElapsedEventArgs e)
{
FirstMenuItem_Click(null, null);
}
private static void WaitForDataLoading(ElapsedEventHandler timerEvent)
{
Timer t = new Timer();
t.Interval = 0.2;
t.AutoReset = false;
t.Elapsed += new ElapsedEventHandler(timerEvent);
t.Start();
}
Initially, the FirstMenuItem_Click was the only method. I had to add FirstTimedEvent handler for my timer. Is there a way to avoid creating that ElapsedEventHandler? Can I create it inline in my FirstMenuItem_Click method?
I now have to use that same pattern for many other Item_Click methods. I wish I don't have to create a ElapsedEventHandler for each Item_Click method.
Use an anonymous lambda expression:
WaitForDataLoading((s,e) => FirstMenuItem_Click(null, null));
You appear to be using WPF, based on your use of the Dispatcher class. That being the case, there are nicer means for you to control the access to your UI.
Two of these are:
bind your menu's Enabled property to ViewModel class, which would have a property to indicate whether the menu should be available. When your long-running job is complete, set the property to true and the menu will be enabled.
use an ICommand to drive the behaviour of your menu. The command's CanExecute return false while your long-running job is active, which will cause the menu to automatically be disabled until the job is complete.
It's worth noting that this will subtly change the behaviour of your menu - but not, I think, in a bad way. Your current code will wait for the job to complete before showing the dialog - but there's nothing to stop the user clicking the menu again in the meantime. These multiple clicks will each wait for the job to complete, and each display their own dialog when the job completes. In a trivial case this might mean that I see multiple dialogs appear; in a severe case the multiple timers that you're creating might badly affect the performance of the application.
Either of the methods suggested above would prevent the menu from being clicked while the job is running, which is not quite your current behaviour but, I think, would make more sense from a usability perspective.
In the following code you can call the method CheckDataShowWindow() anytime you wish to show the windows when the data is ready. If you want to add it to another cick handler, you can just make another like so:
public void Another_Click(object sender, RoutedEventArgs e)
{
CheckDataShowWindow();
}
Main code:
public void FirstMenuItem_Click(object sender, RoutedEventArgs e)
{
CheckDataShowWindow();
}
private void CheckDataShowWindow()
{
if (!DataRepository.IsAllDataLoaded)
{
Timer t = new Timer();
t.Interval = 0.2;
t.AutoReset = false;
t.Elapsed += (s,e) => CheckDataShowWindow();
t.Start();
}
else
{
Dispatcher.BeginInvoke(new Action(() =>
{
IndividualEntryWindow Window = new IndividualEntryWindow();
Window.Show();
}));
}
}
Update
If you can edit the code of the datarepository you should add an event for when the data is done loading.
public delegate void DoneLoadingHandler(object sender, EventArgs e);
public class DataRepository
{
public event DoneLoadingHandler DoneLoading;
//Your loading function
private void LoadAllData()
{
//Load like you do now
//Now fire the event that loading is done.
if(DoneLoading != null)
DoneLoading(this, new EventArgs());
}
}
Now in your other class:
public void FirstMenuItem_Click(object sender, RoutedEventArgs e)
{
CheckDataShowWindow();
}
private bool AllReadyWaiting = false;
private void CheckDataShowWindow()
{
if (!DataRepository.IsAllDataLoaded)
{
if(!AllReadyWaiting)
{
DataRepository.DoneLoading += (s,e) => ShowWindow();
AllReadyWaiting = true;
}
}
else
{
ShowWindow();
}
}
private void ShowWindow()
{
Dispatcher.BeginInvoke(new Action(() =>
{
IndividualEntryWindow Window = new IndividualEntryWindow();
Window.Show();
}));
}

breaking out of my infinite loop

I have two buttons one for start and one for stop in my UI form,and i have one infinite loop that executes some function in my class name programs in a method.The start button is clicked by the user it invokes this method to execute the infinite loop and i need to break this infinite loop when the user clicks the stop button,after that my compiler will break out of this infinite loop and enters to the code inside the button stop click.
I am trying to use the Application.DoEvents() method,this is working well if my infinite loop code is inside of the start button click,but if my infinite loop code is in the new class which is created by me i.e programs,how can use the Application.DoEvents() method to break out of this infinite loop.
Example:
namespace inFiniteLoopTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
bool stopBtnClk = false;
bool startBtnClk = false;
private void StartBtn_Click(object sender, EventArgs e)
{
stopBtnClk=false;
startBtnClk = true;
while(true)
{
//some code to be executed
Application.DoEvents();
if (stopBtnClk == true)
{
break;
}
}
}
private void StopBtn_Click(object sender, EventArgs e)
{
stopBtnClk = true;
if (startBtnClk == true)
{
//Application.Exit();
MessageBox.Show("success");
}
}
this is working well.
But
public class programs
{
public static void infiniteLoop(bool stopBtnClick)
{
while(true)
{
//some code to be executed
Application.DoEvents();
if (stopBtnClk == true)
{
break;
}
}
}
}
//and my UI code to call this class is
namespace inFiniteLoopTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
bool stopBtnClk = false;
bool startBtnClk = false;
private void StartBtn_Click(object sender, EventArgs e)
{
stopBtnClk=false;
startBtnClk = true;
programs.infiniteLoop(stopBtnClk);
}
private void StopBtn_Click(object sender, EventArgs e)
{
stopBtnClk = true;
if (startBtnClk == true)
{
//Application.Exit();
MessageBox.Show("success");
}
}
}
but this is not working .
Even if the compiler displays the message "success" when the stop button is clicked, but the debugger still said running in my form.
I hope my question is clear.
And i am kindly requesting you to answer my question as soon as possible and get rid of this problem!
I openly accept your answer if you come especially with a thread.
sorry i am a beginner for C#, but i need to continue on that.
Thank you!!
Don't block the GUI thread. The fact that you have to use Application.DoEvents() to update the GUI is an indicator for bad design. Do the work in a separate worker thread.
BackgroundWorker is predestinated for such a task.
Change signature of your infiniteLoop method like this:
public static void infiniteLoop(ref bool stopBtnClick)
...
The code you have provided is really difficult to read but as far as I can see when you create your infinite loop do:while(looping) // do stuff
Then when you press the Stop button set the bool variable looping to false and it will break out of the loop and show the message.
In the second code snippet, the infinite loop is started in a subroutine that accepts a boolean value as a parameter. How does that subroutine ever get a second chance to take a look at that boolean? It only "sees" the value once, and it's false at that time. It's a scoping question.
Why instead of an infinite loop you use a start stop condition determined by the buttons?
I thinking you can have a variable, just call it
bool stop_loop = false
and your loop
while(!stop_loop){ //CODE HERE }
Now when you click the first button (Start) you call the method (wherever it is) to start the loop. The loop is going to seem endless until you click the button stop and the value of stop_loop become in True.
HTH

Categories