WPF UI keeps freezing during while loop - c#

During this code my UI freezes and doesn't update like i would like although the console write line works perfectly so im sure it is going through the loop exactly how i want
while (true)
{
MyMethod();
}
void MyMethod() {
Console.WriteLine(DateTime.Now);
TimeSpan duration = SetDate - DateTime.Now;
int days = duration.Days + 1;
string strDays = days.ToString();
string LeftorAgo = "";
if (strDays[0] == '-')
{
LeftorAgo = "ago";
}
else
{
LeftorAgo = "left";
}
this.Dispatcher.Invoke(() =>
{
ShowDate.Text = $"{strDays.TrimStart('-')}\n days {LeftorAgo}";
ShowSubject.Text = Subject;
});
System.Threading.Thread.Sleep(5000);
}
Edit with Darkonekt's help i solved this issue using his timer technique and it works perfectly thank you!
code below
private System.Windows.Threading.DispatcherTimer remainTimer = new System.Windows.Threading.DispatcherTimer();
InitializeComponent();
remainTimer.Tick += new EventHandler(MyMethod);
remainTimer.Interval = TimeSpan.FromSeconds(5);
remainTimer.Start();
void MyMethod(object sender, EventArgs e) {
...
thank you for your help

Use a DispatcherTimer instead of while loop:
private DispatcherTimer remainTimer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
remainTimer.Tick += MyMethod;
remainTimer.Interval = TimeSpan.FromSeconds(5);
remainTimer.Start();
}
private void MyMethod(object sender, EventArgs e)
{
Console.WriteLine(DateTime.Now);
TimeSpan duration = SetDate - DateTime.Now;
int days = duration.Days + 1;
string strDays = days.ToString();
string LeftorAgo = "";
if (strDays[0] == '-')
{
LeftorAgo = "ago";
}
else
{
LeftorAgo = "left";
}
ShowDate.Text = $"{strDays.TrimStart('-')}\n days {LeftorAgo}";
ShowSubject.Text = Subject;
}

Related

Concurrent queue is not dequeued/cleared

My requirement is to insert item in a queue and process it but the items should be added first and after a while they should be processed (as some other things needs to be set before processing the items. Here is the coding I have done so far.
#region Variables Declarations
private Thread threadTask = null;
ConcurrentQueue<string> concurrentQueue = new ConcurrentQueue<string>();
string currentSeqNo;
string previousSeqNo = "-1";
#endregion
private void test1_Load(object sender, EventArgs e)
{
AddItems();
if (threadTask == null)
{
threadTask = new Thread(Kick);
Thread.Sleep(5000);
threadTask.Start();
}
}
private void AddItems()
{
for (Int64 i = 100000; i < 300000; i++)
{
concurrentQueue.Enqueue(i.ToString());
this.Invoke(new MethodInvoker(delegate()
{
label1.Text = i.ToString();
label1.Update();
}));
}
}
private void Kick()
{
while (true)
{
int recordCountNew = concurrentQueue.Count();
if (recordCountNew != 0)
{
RemoveItems();
}
}
}
private void RemoveItems()
{
string item;
while (concurrentQueue.TryDequeue(out item))
{
this.Invoke(new MethodInvoker(delegate()
{
label2.Text = item;
label2.Update();
}));
currentSeqNo = item; // second time does not start wil 100000
if (previousSeqNo != "-1")
{
if (long.Parse(currentSeqNo) != long.Parse(previousSeqNo) + 1)
{
Reconnect();
}
else
{
//Process item
previousSeqNo = currentSeqNo;
}
}
else
{
//Process item
previousSeqNo = currentSeqNo;
}
}
}
private void Reconnect()
{
currentSeqNo = "";
previousSeqNo = "-1";
string someItem;
while (concurrentQueue.Count > 0)
{
concurrentQueue.TryDequeue(out someItem);
}
this.Invoke(new MethodInvoker(delegate()
{
label1.Text = "";
label2.Text = "";
label1.Update();
label2.Update();
}));
AddItems();
if (threadTask == null)
{
threadTask = new Thread(Kick);
threadTask.Start();
}
}
private void button1_Click_1(object sender, EventArgs e)
{
Reconnect();
}
To reproduce the issue: Run the app and in the middle click on the button. Now the queue should again be started from 100000 but it shows the number somewhere greater than 100000.
Please advise how do I release all the resources to make a fresh start after clicking a button. Though I am setting them to default and also clearing the queue but it still shows the old values in currentSeqNo when 'RemoveItems' method is called.
What you see is a race condition between the Kick thread and the button click handler. When you press the button you execute Reconnect() in it you clean the queue and then call the AddItems() function. But all this time the Kick function tries to Dequeue and so you end up each time with an arbitrary amount of items in it. What you should do is to synchronize between these functions or prevent the Kick from executing while you are adding items.
Couple of comments:
1) You Kick() method have an infinite loop, that too without sleep. Every thread started will keep on running as you didn't have a scope for thread to come out.
You can have a member variable like bKeepRunning with default value as true. Set that variable to false in beginning of Reconnect() function. Something like:
private void Kick()
{
while (bKeepRunning)
{
int recordCountNew = concurrentQueue.Count();
if (recordCountNew != 0)
{
RemoveItems();
}
}
}
Why do you have Thread.Sleep(5000); in test1_Load()? I dont think that is needed.
I made small change in your code, something like:
private void AddItems()
{
for (Int64 i = 100000; i < 300000; i++)
{
concurrentQueue.Enqueue(i.ToString());
this.Invoke(new MethodInvoker(delegate()
{
label1.Text = i.ToString();
label1.Update();
}));
if (i < 100004)
Thread.Sleep(1000);
}
}
private void Kick()
{
while (bKeepRunning)
{
int recordCountNew = concurrentQueue.Count();
if (recordCountNew != 0)
{
RemoveItems();
}
}
}
private void Reconnect()
{
currentSeqNo = "";
previousSeqNo = "-1";
bKeepRunning = false;
threadTask = null;
string someItem;
while (concurrentQueue.Count > 0)
{
concurrentQueue.TryDequeue(out someItem);
}
this.Invoke(new MethodInvoker(delegate()
{
label1.Text = "";
label2.Text = "";
label1.Update();
label2.Update();
}));
Thread.Sleep(2000);
AddItems();
bKeepRunning = true;
if (threadTask == null)
{
threadTask = new Thread(Kick);
threadTask.Start();
}
}
It helped me to see that value is starting from 100000. You can try the same at your end.
Note: I have stopped thread and restarted after clicking on button. Hence i dont see any flaw in your code as such. It just runs fast so that you are not able to realize start values.
You should make UI thread and threadTask thread sync, just use ManualResetEventSlim Signal Construct to, like this:
static ManualResetEventSlim guard = new ManualResetEventSlim(true);
private void button1_Click_1(object sender, EventArgs e)
{
guard.Reset();
Reconnect();
guard.Set();
}
private void RemoveItems()
{
string item;
while (concurrentQueue.TryDequeue(out item))
{
guard.Wait();
//......
}
}
see:
ManualResetEventSlim Class

Multiple countdown timer in listview C# duplicating

I'm new to C#.
I'm trying to make a simple task reminder program.
The problem is, when I try to add a countdown for deadline time, it won't work correctly.
My first task countdown will be overwritten by my second task countdown, the same case when I add the third task and so on.
Here is the code of the correlating part.
private void buttonSave_Click(object sender, EventArgs e)
{
if (this.textBox_Task.Text != "")
{
listView1.View = View.Details;
ListViewItem lvwItem = listView1.Items.Add(dateTimePicker1.Text);
var day = dateTimePicker1.Value.Day;
var month = dateTimePicker1.Value.Month;
var year = dateTimePicker1.Value.Year;
endTime = new DateTime(year,month,day);
//Console.WriteLine(day);
//Console.WriteLine(month);
//Console.WriteLine(year);
//Console.WriteLine(dTime
Timer t = new Timer();
t.Interval = 500;
t.Tick += new EventHandler(t_Tick);
t.Start();
lvwItem.SubItems.Add(textBox_Task.Text);
lvwItem.SubItems.Add(textBox_Note.Text);
lvwItem.SubItems.Add("");
this.dateTimePicker1.Focus();
this.textBox_Note.Focus();
this.textBox_Task.Focus();
this.textBox_Task.Clear();
this.textBox_Note.Clear();
}
else
{
MessageBox.Show("Please enter a task to add.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.textBox_Task.Clear();
this.textBox_Note.Clear();
}
}
void t_Tick(object sender, EventArgs e)
{
TimeSpan ts = endTime.Subtract(DateTime.Now);
var hari = dateTimePicker1.Value.Day;
Console.WriteLine(ts.Days);
for (int i = 0; i < listView1.Items.Count; i++)
{
if (ts.Days == 0)
{
listView1.Items[i].SubItems[3].Text = "DEADLINE";
}
else
{
listView1.Items[i].SubItems[3].Text = ts.ToString("d' Days 'h' Hours 'm' Minutes 's' Seconds to go'");
}
}
}
It would be much appreciated for anyone who willing to help.
Thanks in advance.
Here is the link to the picture of my problem
What you are doing now is on each button click override the current endTime object by a new one like:
endTime = new DateTime(year,month,day);
If you assign a new DateTime object to endTime. You override the old one. So the first button click will work but the second will create a new object of DateTime and assign it to endTime. Next you are calculating the time difference on that one object DateTime. So it is logic that it will be the same time for each listview items
If you want to have more than one DateTime use a List to store it in like
List<DateTime> _times = new List<DateTime>();
In the button click method add the DateTime to the list
// here add the datetime to the list
DateTime dateTime = new DateTime(year, month, day);
_times.Add(dateTime);
Next you can loop thru the dates and calculate for each one the time difference in the tick method:
foreach (var dateTime in _times)
{
TimeSpan ts = dateTime.Subtract(DateTime.Now);
// etc..
}
Also you are creating a timer for each time to calculate after 500 ms. You now can use one timer this is more efficient than crating one for each time. Just assign this in the constructor
public Form1()
{
InitializeComponent();
Timer t = new Timer();
t.Interval = 500;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
Whole code
public partial class Form1 : Form
{
// This is the list where the DateTimes are stored so you can have more values
List<DateTime> _times = new List<DateTime>();
public Form1()
{
InitializeComponent();
// Assign the timer here
Timer t = new Timer();
t.Interval = 500;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
private void buttonSave_Click(object sender, EventArgs e)
{
if (this.textBox_Task.Text != "")
{
listView1.View = View.Details;
ListViewItem lvwItem = listView1.Items.Add(dateTimePicker1.Text);
var day = dateTimePicker1.Value.Day;
var month = dateTimePicker1.Value.Month;
var year = dateTimePicker1.Value.Year;
// Add Datetime to list
DateTime dateTime = new DateTime(year, month, day);
_times.Add(dateTime);
lvwItem.SubItems.Add(textBox_Task.Text);
lvwItem.SubItems.Add(textBox_Note.Text);
lvwItem.SubItems.Add("");
this.dateTimePicker1.Focus();
this.textBox_Note.Focus();
this.textBox_Task.Focus();
this.textBox_Task.Clear();
this.textBox_Note.Clear();
}
else
{
MessageBox.Show("Please enter a task to add.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.textBox_Task.Clear();
this.textBox_Note.Clear();
}
}
void t_Tick(object sender, EventArgs e)
{
// loop thru all datetimes and calculate the diffrence
foreach (var dateTime in _times)
{
// Call the specific date and subtract on it
TimeSpan ts = dateTime.Subtract(DateTime.Now);
var hari = dateTimePicker1.Value.Day;
Console.WriteLine(ts.Days);
for (int i = 0; i < listView1.Items.Count; i++)
{
if (ts.Days == 0)
{
listView1.Items[i].SubItems[3].Text = "DEADLINE";
}
else
{
listView1.Items[i].SubItems[3].Text = ts.ToString("d' Days 'h' Hours 'm' Minutes 's' Seconds to go'");
}
}
}
}
}

Ping Completed Event Handler not working [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
When I execute this code in command line, it's working fine:
class Program
{
private static List<Ping> pingers = new List<Ping>();
private static List<string> value = new List<string>();
private static int instances = 0;
private static object #lock = new object();
private static int result = 0;
private static int timeOut = 2500;
private static int ttl = 7;
public static void Main()
{
string baseIP = "192.168.1.";
Console.WriteLine("Pinging destinations of D-class in {0}*", baseIP);
CreatePingers(254);
PingOptions po = new PingOptions(ttl, true);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] data = enc.GetBytes("");
SpinWait wait = new SpinWait();
int cnt =1;
Stopwatch watch = Stopwatch.StartNew();
foreach (Ping p in pingers)
{
lock (#lock)
{
instances += 1;
}
p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeOut, data, po);
cnt += 1;
}
//while (instances > 0)
//{
// wait.SpinOnce();
//}
watch.Stop();
for (int i = 0; i < value.Count; i++)
{
Console.WriteLine(value[i]);
}
DestroyPingers();
Console.WriteLine("Finished in {0}. Found {1} active IP-addresses.", watch.Elapsed.ToString(), result);
Console.ReadKey();
}
public static void Ping_completed(object s, PingCompletedEventArgs e)
{
lock (#lock)
{
instances -= 1;
}
if (e.Reply.Status == IPStatus.Success)
{
string sa = string.Concat("Active IP: ", e.Reply.Address.ToString());
value.Add(sa);
//Console.WriteLine(sa);
String diachiip = e.Reply.Address.ToString();
result += 1;
}
else
{
//Console.WriteLine(String.Concat("Non-active IP: ", e.Reply.Address.ToString()))
}
}
private static void CreatePingers(int cnt)
{
for (int i = 1; i <= cnt; i++)
{
Ping p = new Ping();
p.PingCompleted += Ping_completed;
pingers.Add(p);
}
}
private static void DestroyPingers()
{
foreach (Ping p in pingers)
{
p.PingCompleted -= Ping_completed;
p.Dispose();
}
pingers.Clear();
}
}
But when I convert from it to window form, it doesn't work. I don't kwow why, I have tried many different ways...
Code is here:
public partial class Form1 : Form
{
public static List<Ping> pingers = new List<Ping>();
public static List<string> value = new List<string>();
public static int instances = 0;
public static object #lock = new object();
public static int result = 0;
public int timeout = 2500;
public static int ttl = 7;
public Form1()
{
InitializeComponent();
}
public void btnscan_Click(object sender, EventArgs e)
{
string baseIP = "192.168.1.";
//int kt = Int32.Parse(txtkt.Text);
//int start = Int32.Parse(txtstart.Text);
CreatePingers(254);
PingOptions po = new PingOptions(ttl, true);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] data = enc.GetBytes("");
int cnt = 1;
Stopwatch watch = Stopwatch.StartNew();
foreach (Ping p in pingers)
{
lock (#lock)
{
instances += 1;
}
p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeout, data, po);
cnt += 1;
}
watch.Stop();
//Result alway return 0
lst1.Items.Add(result.ToString());
lst1.Items.Add(value.Count.ToString());
for (int i = 0; i < value.Count; i++)
{
lst1.Items.Add(value[i]);
lst1.Items.Add("\n");
}
DestroyPingers();
string a = "Finished in " + watch.Elapsed.ToString() + ". Found " + result + " active IP-addresses.";
lst1.Items.Add(a);
}
public static void CreatePingers(int kt)
{
for (int start = 1; start <= kt; start++)
{
// class System.Net.NetworkInformation.Ping
Ping p = new Ping();
p.PingCompleted += Ping_completed();
pingers.Add(p);
}
}
public static PingCompletedEventHandler Ping_completed()
{
PingCompletedEventHandler a = new PingCompletedEventHandler(abc);
return a;
}
static void abc(object s, PingCompletedEventArgs e)
{
value.Add("abc");
lock (#lock)
{
instances -= 1;
}
if (e.Reply.Status == IPStatus.Success)
{
string abcd = string.Concat("Active IP: ", e.Reply.Address.ToString());
value.Add(abcd);
result += 1;
}
}
public static void DestroyPingers()
{
foreach (Ping p in pingers)
{
p.PingCompleted -= Ping_completed();
p.Dispose();
}
pingers.Clear();
}
}
What is wrong in this code?
Method SendAsync returns 0 because you are not waiting for it to complete. You are missing await and async (see msdn):
async void btnscan_Click(object sender, EventArgs e)
{
...
await p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeout, data,
...
}
SpinWait was making code to work in console application. In winforms you should not use SpinWait (nor Sleep) in UI thread. You can create another thread (e.g. by using Task) and then you can copy/paste code from console application 1-to-1. But then you will need to use Invoke each time when you want to access UI controls.
async/await is really better.. if it will work (I concluded that from method name, I've no idea what method does, nor how to use it).
Perhaps I miss one thing, if SendAsync returns value, then you can get it by (the requirement to mark method where you use await with async still):
var result = await p.SendAsync(...);

How to execute all function inside a loop before looping again?

please help me i wanted to set my pictureBox into visible for only 1 second then hide it again before going to the next loop. here's my code.
private void sampleTxt_Validated(object sender, EventArgs e)
{
words = "AB"
char[] ch = words.ToCharArray();
for (int i = 0; i < ch.Length; i++)
{
if (ch[i] == 'A')
{
a = true;
Application.Idle += imageA;
}
else if (ch[i] == 'B')
{
b = true;
Application.Idle += imageB;
}
}
}
private void imageA(object sender, EventArgs arg)
{
TimeSpan ts;
if(a == true)
{
letterA.Visible = true;
stopWatchA.Start();
ts = stopWatchA.Elapsed;
if (ts.Seconds >= 1)
{
stopwatch();
letterA.Visible = false;
a = false;
}
}
}
private void imageB(object sender, EventArgs arg)
{
TimeSpan ts;
if (b == true)
{
letterB.Visible = true;
stopWatchB.Start();
ts = stopWatchB.Elapsed;
if (ts.Seconds >= 0.5)
{
stopwatch();
letterB.Visible = false;
b = false;
}
}
}
the problem with my code is that it displays both images at the same time.
I want to display the letter "A" image for 1sec first before looping again to display the second image. Is that possible?
You need a loop on letterB, also maybe change the timer from 0.5 to 1.0

DispatcherTimer class tick event issue

I don't know how to explain this clearly but I will try my best.
I'm using a DispatcherTimer class in my windows phone 8 project. I've got some check boxes and when a check box is checked and the button is pressed the countdown from a bus schedule starts. Everything works perfectly here but when I uncheck the check box and when I check another one, the same bus time starts and it goes down(countdown) by 2 seconds now. If I uncheck the check box and check another again the same time starts to countdown and the countdown goes by 3 seconds... I don't understand how, because on the click_event there is always a new instance of the class.
The only thing that solves the problem is to reload my wp8 page and check something again and press the button for the countdown to start.
Note: I'm reading my times from a .txt file which are store on a folder within my project (using streamreader).
Any suggestions?
Here is some code:
CheckBox[] listchekbox;
DispatcherTimer timer;
private void Button_Click(object sender, RoutedEventArgs e)//BUTTON CLICK
{
timer = new DispatcherTimer();
listchekbox = new CheckBox[4] { Check1, Check2, Check3, Check4 };
if (Onlyoneclick(listchekbox, 0)) { Gettingbustime(path_names[0]); }
else if (Onlyoneclick(listchekbox, 1)) { Gettingbustime(path_names[1]); }
timer.Interval = new TimeSpan(0,0,1);
timer.Tick += onclick;
timer.Start();
}
private void onclick(object sender, EventArgs e) // TIMER EVENT COUNT
{
Countdown(bus1); // the parameter is the textbox where the result is displayed
Countdown(bus2);
}
private bool Onlyoneclick(CheckBox[] itemselected,int id) // this is not so important
{
int itemcounter = 0;
int falsecounter = 0;
for (int i = 0; i < listchekbox.Length; i++)
{
if (itemselected[i].IsChecked == true && id == i)
{
itemcounter++;
}
else if (itemselected[i].IsChecked == true) { falsecounter++; }
}
if (itemcounter == 1 && falsecounter==0) { return true; }
else { return false; }
}
public void Countdown(TextBlock tx)
{
string minute = tx.Text.Substring(0, 2);
string sekunde = tx.Text.Substring(3, 2);
int minute1 = Convert.ToInt32(minute);
int sekunde1 = Convert.ToInt32(sekunde);
sekunde1 = sekunde1 - 1;
if (sekunde1 < 0)
{
minute1 = minute1 - 1;
sekunde1 = 59;
}
if (minute1 < 0)
{
timer.Stop();
MessageBox.Show("Autobus left!");
}
if (sekunde1 < 10) { tx.Text = minute1.ToString() + ":0" + sekunde1.ToString(); }
else { tx.Text = minute1.ToString() + ":" + sekunde1.ToString(); }
if (minute1 < 10) { tx.Text = "0" + minute1.ToString() + ":" + sekunde1.ToString(); }
if (minute1 < 10 && sekunde1 < 10) { tx.Text = "0" + minute1.ToString() + ":0" + sekunde1.ToString(); }
}
Try removing this code from Button_Click:
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0,0,1);
timer.Tick += onclick;
And add it instead to your constructor.

Categories

Resources