Backgroundworker not Rendering Window fluently - c#

I just got into WPF and am currently trying my luck with the Background worker, so I figured I'd just open any file using the FileOpenDialog, loop through all the bytes inside the file and report the total progress via worker.ReportProgress in percentage ... alas, this only works for like ~20 times and then it gets really stuck and suddenly stops at 100%.
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.IO;
using System.Threading;
using System.ComponentModel;
namespace BitStream
{
public partial class MainWindow : Window
{
private int bytes = 0;
private long length = 0;
public MainWindow()
{
InitializeComponent();
}
private void selectFile_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
OpenFileDialog ofd = new OpenFileDialog();
if ((bool)ofd.ShowDialog())
{
FileInfo fi = new FileInfo(ofd.FileName);
this.length = fi.Length;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.ProgressChanged += bw_ProgressChanged;
bw.WorkerReportsProgress = true;
Stream str = ofd.OpenFile();
bw.RunWorkerAsync(str);
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Stream str = (Stream)e.Argument;
int singleByte = 0;
this.Dispatcher.Invoke(
new Action(() =>
{
int currentProgress = 0;
while ((singleByte = str.ReadByte()) != -1)
{
label1.Content = singleByte;
bytes++;
currentProgress = Convert.ToInt32(((double)bytes) / length * 100);
if (currentProgress > progress)
{
progress = currentProgress;
((BackgroundWorker)sender).ReportProgress(progress);
Thread.Sleep(100);
}
}
}
), System.Windows.Threading.DispatcherPriority.Render);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label2.Content = e.ProgressPercentage + "% completed";
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
}
}
Labels 1 and 2 are there for showing the current byte and the current progress in %.
Feel free to also criticize every other aspect of my code, I just got started with WPF today.
Edited DoWork-Method:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Stream str = (Stream)e.Argument;
int singleByte = 0;
int currentProgress = 0;
while ((singleByte = str.ReadByte()) != -1)
{
this.Dispatcher.Invoke(
new Action(() =>
{
label1.Content = singleByte;
}), System.Windows.Threading.DispatcherPriority.Render);
bytes++;
currentProgress = Convert.ToInt32(((double)bytes) / length * 100);
if (currentProgress > progress)
{
progress = currentProgress;
this.Dispatcher.Invoke(
new Action(() =>
{
((BackgroundWorker)sender).ReportProgress(progress);
}), System.Windows.Threading.DispatcherPriority.Render);
Thread.Sleep(500);
}
}
}
Thanks,
Dennis

So assuming you really want to make a cross thread call for each byte (which I wouldn't recommend), the code would look something like:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Stream str = (Stream)e.Argument;
int singleByte = 0;
int currentProgress = 0;
while ((singleByte = str.ReadByte()) != -1)
{
bytes++;
this.Dispatcher.Invoke(
new Action(() =>
{
label1.Content = singleByte;
}
), System.Windows.Threading.DispatcherPriority.Render);
currentProgress = Convert.ToInt32(((double)bytes) / length * 100);
if (currentProgress > progress)
{
progress = currentProgress;
((BackgroundWorker)sender).ReportProgress(progress);
Thread.Sleep(100);
}
}
}
The idea being that you can only manipulate a DispatcherObject on the thread on which it was created.

First thought is you aren't disposing the return from the openfiledialog so the more times you run it the more resources you throw away...
I would have thrown the filename at the worker and then let it manage the resource, but
using(Stream s = ofd.OpenFileDalog())
{
get length and such
}
// run up woker pass filename.
in your calling code will solve the problem, as I'm assuming you are using length to sort out your progress bar.

Related

Application is not responding

What the application should do
This application should take the input of time (seconds, minutes and hours) and shutdown the computer after that time. It should also update the text box with how long left until the computer has shut down.
What the application actually does
I had an issue that I 'fixed' where the called ac across threads weren't safe, so I fixed it and I don't get that error now. However, the updateThread doesn't update and print the time left; and the text box doesn't get "test" appended to it. The UI also becomes Not Responding. Any help would be much appreciated.
Also, if you see anything else that could be done better, please comment and explain. Thanks!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ShutdownPC
{
public partial class Form1 : Form
{
int inputHours;
int inputMinutes;
int inputSeconds;
Thread sleepingThread;
Thread updatingThread;
NotifyIcon shutdownPCIcon;
Icon defaultIcon;
public Form1()
{
InitializeComponent();
defaultIcon = new Icon("defaultIcon.ico");
shutdownPCIcon = new NotifyIcon();
shutdownPCIcon.Icon = defaultIcon;
shutdownPCIcon.Visible = true;
MenuItem progNameMenuItem = new MenuItem("ShutdownPC by Conor");
MenuItem breakMenuItem = new MenuItem("-");
MenuItem quitMenuItem = new MenuItem("Quit");
ContextMenu contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(progNameMenuItem);
contextMenu.MenuItems.Add(breakMenuItem);
contextMenu.MenuItems.Add(quitMenuItem);
shutdownPCIcon.ContextMenu = contextMenu;
shutdownPCIcon.Text = "ShutdownPC";
quitMenuItem.Click += QuitMenuItem_Click;
}
private void QuitMenuItem_Click(object sender, EventArgs e)
{
shutdownPCIcon.Dispose();
sleepingThread.Abort();
updatingThread.Abort();
this.Close();
}
public void sleepThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(sleepThread));
}
else {
textBox1.Enabled = false;
textBox2.Enabled = false;
textBox3.Enabled = false;
button1.Enabled = false;
int totalMilliseconds = ((inputHours * 3600) + (inputMinutes * 60) + inputSeconds) * 1000;
Thread.Sleep(totalMilliseconds);
//Process.Start("shutdown", "/s /t 0");
richTextBox1.AppendText(String.Format("test"));
}
}
public void updateThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(updateThread));
}
else {
int totalSeconds = (inputHours * 3600) + (inputMinutes * 60) + inputSeconds;
while (totalSeconds > 0)
{
TimeSpan time = TimeSpan.FromSeconds(totalSeconds);
string timeOutput = time.ToString(#"hh\:mm\:ss");
richTextBox1.AppendText(String.Format(timeOutput));
Thread.Sleep(1000);
richTextBox1.Clear();
totalSeconds--;
}
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
inputHours = Convert.ToInt32(textBox1.Text);
inputHours = int.Parse(textBox1.Text);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
inputMinutes = Convert.ToInt32(textBox2.Text);
inputMinutes = int.Parse(textBox2.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
inputSeconds = Convert.ToInt32(textBox3.Text);
inputSeconds = int.Parse(textBox3.Text);
}
private void button1_Click(object sender, EventArgs e)
{
updatingThread = new Thread(new ThreadStart(updateThread));
updatingThread.Start();
sleepingThread = new Thread(new ThreadStart(sleepThread));
sleepingThread.Start();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
Using Invoke in the beginning of method that runs in separate thread is bad idea, because all code runs in GUI thread and lock it.
You should Invoke only GUI updating code!!!

c# wpf window opacity change error

I'm working on a c# project with WPF but I've a problem and this makes me crazy :)
Here is the problem. I'm trying to change new window's opacity with timer. But when I run the project, "this.Opacity += .1;" code throws an exception like "Invalid operation etc..."
I'm opening a window from MainWindow.cs file with this code:
private void MenuItemArchiveClick(object sender, RoutedEventArgs e)
{
var archiveWindow = new ArchiveWindow();
var screenSize = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
archiveWindow.Width = (screenSize.Width * 95) / 100;
archiveWindow.Height = (screenSize.Height * 90) / 100;
archiveWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
archiveWindow.Margin = new Thickness(0, 10, 0, 0);
archiveWindow.AllowsTransparency = true;
archiveWindow.Opacity = 0.1;
archiveWindow.Topmost = true;
archiveWindow.Show();
}
My ArchiveWindow code is,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace POCentury
{
/// <summary>
/// Interaction logic for ArchiveWindow.xaml
/// </summary>
public partial class ArchiveWindow : Window
{
Timer timer1 = new Timer();
public ArchiveWindow()
{
InitializeComponent();
timer1.Interval = 1 * 1000;
timer1.Elapsed += new ElapsedEventHandler(opacityChange);
timer1.Enabled = true;
timer1.AutoReset = false;
timer1.Start();
}
private void opacityChange(object sender, EventArgs a)
{
if (this.Opacity == 1)
{
timer1.Stop();
}
else
{
this.Opacity += .1;
}
}
private void ArchiveWindowClose()
{
timer1.Stop();
this.Close();
}
private void btnArchiveWindowClose(object sender, RoutedEventArgs e)
{
ArchiveWindowClose();
}
private void imgPatternClick(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("sd");
}
}
}
Can you help me with doing that?
Thank you so much!
Basically, you can't access the timer inside the opacityChange event because it's happening in a different thread. You need the Dispatcher to do that.
Dispatcher.BeginInvoke(new Action(() =>
{
if (this.Opacity == 1)
{
timer1.Stop();
}
else
{
this.Opacity += .1;
}
}));

Ping over a period of Time

I have a windows form application that basically pings an ip and then returns an image with a tooltip that displays the rtt to that ip.
What i want to do is have the the form ping that ip every 20 seconds, so that the form and images change. If i could get that to work then I would like to some how store maybe 4 rtt's and then show an average of the 4 in the tooltip.
So far the form is only pinging once, I've played around with a timer but I don't really know what I am doing. Here is my code so far.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.ServiceProcess;
using System.Threading;
using System.ComponentModel;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Form1_Load(object sender, EventArgs e)
{
Ping pingClass = new Ping();
PingReply pingReply = pingClass.Send("10.209.123.123");
label4.Text = (pingReply.RoundtripTime.ToString());
//+ "ms");
label5.Text = (pingReply.Status.ToString());
if (Convert.ToInt32(label4.Text) > 0 && Convert.ToInt32(label4.Text) < 100)
this.pictureBox1.Load("greenLAT.png");
if (Convert.ToInt32(label4.Text) > 100 && Convert.ToInt32(label4.Text) < 200)
this.pictureBox1.Load("yellowLAT.png");
if (Convert.ToInt32(label4.Text) > 200 && Convert.ToInt32(label4.Text) < 1000)
this.pictureBox1.Load("redLAT.png");
ToolTip tt = new ToolTip();
tt.SetToolTip(this.pictureBox1, "Your current network delay is " + label4.Text + "ms");
timer1.Interval = 1000;
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
//MessageBox.Show("Timeout!");
Refresh();
}
}
}
Try this:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Net;
using System.Windows.Forms;
using System.Net.NetworkInformation;
namespace DXWindowsApplication4
{
public partial class Form2 : Form
{
private readonly Timer _timer;
private readonly Ping _pingClass;
private readonly IPAddress _ipAddress;
private readonly int _timeout;
private Image _greenImage;
private Image _yellowImage;
private Image _redImage;
private int _pingCount;
private long _avgRtt;
public Form2()
{
InitializeComponent();
IPAddress.TryParse("98.138.253.109", out _ipAddress); // yahoo.com Ip address
_timer = new Timer();
_timeout = 3000;
_pingClass = new Ping();
_pingClass.PingCompleted += PingClassPingCompleted;
}
void PingClassPingCompleted(object sender, PingCompletedEventArgs e)
{
RefreshPing(e.Reply);
}
public void FormLoad(object sender, EventArgs e)
{
_timer.Tick += TimerTick;
_timer.Interval = 4000;
_timer.Start();
}
private void TimerTick(object sender, EventArgs e)
{
_pingClass.SendAsync(_ipAddress, _timeout);
}
private void RefreshPing(PingReply pingReply)
{
label4.Text = (pingReply.RoundtripTime.ToString(CultureInfo.InstalledUICulture));
label5.Text = (pingReply.Status.ToString());
_avgRtt = (_avgRtt * _pingCount++ + pingReply.RoundtripTime)/_pingCount;
if (Convert.ToInt32(label4.Text) > 0 && Convert.ToInt32(label4.Text) < 100)
{
SetImage(pictureBox1, Images.Green);
}
if (Convert.ToInt32(label4.Text) > 100 && Convert.ToInt32(label4.Text) < 200)
{
SetImage(pictureBox1, Images.Yellow);
}
if (Convert.ToInt32(label4.Text) > 200 && Convert.ToInt32(label4.Text) < 1000)
{
SetImage(pictureBox1, Images.Red);
}
ToolTip tt = new ToolTip();
tt.SetToolTip(pictureBox1, "Your average network delay is " + _avgRtt + "ms");
Refresh();
}
private void SetImage(PictureBox pBox, Images images)
{
switch (images)
{
case Images.Green:
if (_greenImage == null)
{
_greenImage = new Bitmap("greenImage.png");
}
pictureBox1.Image = _greenImage;
break;
case Images.Yellow:
if (_greenImage == null)
{
_yellowImage = new Bitmap("yellowImage.png");
}
pictureBox1.Image = _yellowImage;
break;
case Images.Red:
if (_redImage == null)
{
_redImage = new Bitmap("redImage.png");
}
pictureBox1.Image = _greenImage;
break;
default:
throw new InvalidEnumArgumentException("invalid enum name");
}
}
}
internal enum Images
{
Green,
Yellow,
Red
}
}

Caller ID Check if Caller has ended Call

I have a program that gets the incoming number, date and time. I want to check however if the person who is ringing me has put the phone down, how can I do this?
Below is the code which I currently have:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
namespace CallerID
{
public partial class CallerID : Form
{
int timesTicked = 0;
Point defaultLocation = new Point();
Point newLocation = new Point();
public CallerID()
{
InitializeComponent();
port.Open();
SetModem(); // SetModem(); originally went after WatchModem();
WatchModem();
//SetModem();
telephoneTimer.Interval = 16;
telephoneTimer.Tick += new EventHandler(telephoneTimer_Tick);
defaultLocation = pictureBox1.Location;
newLocation = pictureBox1.Location;
}
void telephoneTimer_Tick(object sender, EventArgs e)
{
if (timesTicked <= 2)
newLocation.X++;
if (timesTicked >= 4)
newLocation.X--;
if (timesTicked == 6)
{
timesTicked = 0;
pictureBox1.Location = defaultLocation;
newLocation = defaultLocation;
}
pictureBox1.Location = newLocation;
timesTicked++;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
WatchModem();
}
private SerialPort port = new SerialPort("COM3");
string CallName;
string CallNumber;
string ReadData;
private void SetModem()
{
port.WriteLine("AT+VCID=1\n");
//port.WriteLine("AT+VCID=1");
port.RtsEnable = true;
}
private void WatchModem()
{
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
public delegate void SetCallerIdText();
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
ReadData = port.ReadExisting();
//Add code to split up/decode the incoming data
//if (lblCallerIDTitle.InvokeRequired)
if (ReadData.Contains("NMBR"))
{
lblData.Invoke(new SetCallerIdText(() => lblData.Text = ReadData));
}
//else
// lblCallerIDTitle.Text = ReadData;
}
private void lblData_TextChanged(object sender, EventArgs e)
{
telephoneTimer.Start();
button1.Visible = true;
}
}
}
Please ignore the Timer Code as that is just for animation.
Have you tried the PinChanged event? Normally Carrier Detect will go low when the remote end disconnects.

I have a RichTextBox full of text from a text file I want now to color in red all the places the word "Error" is in the text how to do it?

I tried to do it in the backgroundworker_ProgressChanged event but its coloring in red some areas in the text and I didn't see in those areas the word "Error".
What am I doing wrong ?
This the part of the code where I'm trying to color the word "Error" in any place in the text. What I want is to paint each place in the text that there is a word "Error" and only the word "Error" and after I want to paint the whole line where the word "Error" is exist in it.
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
this.richTextBox1.AppendText(e.UserState.ToString());
if (e.UserState.ToString().Contains("Error"))
{
Invoke(new Action(() => richTextBox1.SelectionColor = Color.Red));
}
}
And this is the complete code of the new form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class textBoxLoggerViewer : Form
{
//all the text-lines
string[] allText;
//counter
int lineCounter = 0;
//amount of lines to display
private int maxDisplayAmount = 2000;
string log_file_name = #"\logger.txt";
string logger_file_to_read = Path.GetDirectoryName(Application.LocalUserAppDataPath) + #"\log";
//string logger_file_to_read = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
BackgroundWorker bgw = null;
public textBoxLoggerViewer()
{
InitializeComponent();
label1.Text = "Loading text please wait";
richTextBox1.Font = new Font("Consolas", 8f, FontStyle.Bold);
richTextBox1.BackColor = Color.AliceBlue;
richTextBox1.SelectionColor = Color.Black;
richTextBox1.DoubleClick += new EventHandler(richTextBox1_DoubleClick);
richTextBox1.Enabled = false;
this.Shown += new EventHandler(textBoxLoggerViewer_Shown);
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
this.FormClosed += new FormClosedEventHandler(textBoxLoggerViewer_FormClosed);
}
void textBoxLoggerViewer_FormClosed(object sender, FormClosedEventArgs e)
{
this.bgw.Dispose();
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
this.richTextBox1.AppendText(e.UserState.ToString());
if (e.UserState.ToString().Contains("Error"))
{
Invoke(new Action(() => richTextBox1.SelectionColor = Color.Red));
}
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
this.allText = File.ReadAllLines(logger_file_to_read + log_file_name);
while (lineCounter < allText.Length - 1)
{
string current = ReadText();
//get current amount in percent
bgw.ReportProgress((int)(((double)this.lineCounter / (double)allText.Length) * 100), current);
}
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.richTextBox1.Enabled = true;
progressBar1.Enabled = false;
progressBar1.Visible = false;
label1.Text = "All the text have been loaded successfully";
}
void textBoxLoggerViewer_Shown(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
void richTextBox1_DoubleClick(object sender, EventArgs e)
{
ReadText();
}
//will be called from bgw
private string ReadText()
{
StringBuilder sb = new StringBuilder();
int curLine = lineCounter;
for (int i = curLine; i < Math.Min(curLine + maxDisplayAmount, allText.Length); i++)
{
sb.Append(i.ToString() + "\t\t" + allText[i] + "\r\n");
lineCounter++;
/* if (allText[i].Contains("Error"))
{
Invoke(new Action(() => richTextBox1.SelectionColor = Color.Red));
}*/
}
return sb.ToString();
}
}
}
BTW: I wanted to add to the progressbar "%" with numbers so when the progressbar is in progress it will count percentages and show them now its just showing this green bar I want in the front of the green bar to show the percentages. like %1 %2 %3 or 1% 2% 3% or without the "%" I don't know how to do it tried many ways didn't work.
Thanks.
You need to select the word Error in the string before you set the selection color:
RichTextBox1.SelectionStart = [redStringStart]
RichTextBox1.SelectionLength = [redStringLength]
RichTextBox1.SelectionColor = Color.Red

Categories