Reputation: 19
I just started programming after taking a long break and am running into a little issue.
I am using VS2013 Desktop and am creating this as a GUI program.
My problem is that I created a working random number generator when the method used to call the logic method runs once. The number(s) gets generated, text updated and all is good. When it goes into a loop, it doesn't update the text property of the object I'm modifying until it finishes the entire loop or gets broken. The program basically hangs when I run it when the loop gets executed and I have to force it to close.
At the moment I would like to set the generator to run infinitely in the background until I press another button to stop it.
I am new to programming and this probably has all sorts of issues with it so I would be grateful for any feedback on structure and other practices if anything is out of order as well.
Here is the code:
Form1.cs
// Global
bool boolLooper;
// Setting up the random number generator
private string RandomNumber()
{
RandomNumber rndNumber = new RandomNumber();
string strRandNumber = Convert.ToString(rndNumber.RandomInt(1000, 9999999));
return strRandNumber;
}
// TEST - used in buttonclick event
private void TextUpdates()
{
while (BoolLooper == true)
{
txtID1.Text = RandomNumber();
//txtName1.Text = RandomNumber();
//txtSize1.Text = RandomNumber();
//txtKey1.Text = RandomNumber();
//txtType1.Text = RandomNumber();
}
}
//-----------------------------
// Form - Button Clicks
//-----------------------------
// Button - Activate
private void btnActivate_Click(object sender, EventArgs e)
{
BoolLooper = true;
TextUpdates();
//// Update text once
//txtID1.Text = RandomNumber();
//txtName1.Text = RandomNumber();
//txtSize1.Text = RandomNumber();
//txtKey1.Text = RandomNumber();
//txtType1.Text = RandomNumber();
}
// Button - Stop/Deactivate
private void btnDeactivate_Click(object sender, EventArgs e)
{
BoolLooper = false;
}
//-----------------------------
// Properties
//-----------------------------
public bool BoolLooper
{
get { return boolLooper; }
set { boolLooper = value; }
}
RandomNumber.cs
private static readonly Random intRandom = new Random();
private static readonly object syncLock = new object();
public int RandomInt(int minNum, int maxNum)
{
lock (syncLock)
{
// synchronize
return intRandom.Next(minNum, maxNum);
}
}
For the RandomNumber class, I found a great post on this site found here which I will give credit to it's author: https://stackoverflow.com/a/768001
Upvotes: 1
Views: 1027
Reputation: 2594
All your code is being executed on the UI thread. So you're stuck in your while
loop, and the form isn't responding to the button click (which sets your while loop flag back to false). This is what we call a blocking call
. It's blocking the UI from continuing.
Typically in situations like this, you would want to look into threading. However, based on your code. I'd look into a timer, and have it tick every second or so. They're very easy to implement and you can remove the complexity of your while
loop and just execute the random number generation and the assigning it to your UI controls. (This also makes it so that you don't have to marshal from a background thread back onto your UI thread.)
For more information on a timer: System.Windows.Forms.Timer
Upvotes: 1
Reputation: 966
You are creating new instance of a RandomNumber
class each time. Just make it a member of your class. Like :
// Global
bool boolLooper;
//Random number generator
RandomNumber rndNumber = new RandomNumber();
and don't need to create new instance in method RandomNumber , just change it to this:
private string RandomNumber()
{
string strRandNumber = Convert.ToString(rndNumber.RandomInt(1000, 9999999));
return strRandNumber;
}
UPDATE: I've read a bit about Application.DoEvents() after comment, so use Invokes, await calls of Tasks, others, but not this.
Upvotes: 0
Reputation: 2337
if you are using .NET 4.5, update the TextUpdates method to use the async/await call like in the example below
private async void TextUpdates()
{
await Task.Run(() =>
{
while (BoolLooper)
{
txtID1.Invoke((MethodInvoker)(() => txtID1.Text = RandomNumber()));
//txtName1.Text = RandomNumber();
//txtSize1.Text = RandomNumber();
//txtKey1.Text = RandomNumber();
//txtType1.Text = RandomNumber();
}
});
}
Upvotes: 0
Reputation: 219127
You're running this code on the same thread as the UI. Since it's single-threaded, the UI can't respond because it's busy running your loop. You'll want to off-load this to a separate thread or in some way as a separate asynchronous operation. That thread/operation would then just need to tell the UI of updates when it has them.
A simple example of this would be to use a BackgroundWorker
object.
Note in the example on that page where the BackgroundWorker
exposes an event which can be used to update UI elements:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
resultLabel.Text = (e.ProgressPercentage.ToString() + "%");
}
There are other possible approaches as well. You could create a Thread
manually and try to synchronize it manually, but that comes with other potential issues as well. And there really isn't a need to get that complex here.
Do you need the TextBox
to be constantly updating? Or just updating every once in a while? If there's some discernible time period between updates (one second?) then you can use a Timer
to schedule the code to take place periodically. The structure is similar to the BackgroundWorker
in that there's an event exposed which would be used to update the UI.
Upvotes: 2
Reputation: 69985
You basically need to run each call to generate a new number asynchronously. Using the .NET Framework, there are several ways to achieve that, but I prefer to use the Task
class. You could do something like this:
public Task RunAsynchronously(Action method)
{
return Task.Factory.StartNew(method);
}
...
RunAsynchronously(() => MethodThatGeneratesRandomNumber());
Each time this is called, the method execution will run asynchronously.
Upvotes: 0