C# Filling a progressbar while a function is beeing executed - c#

I'm currently trying to read a textfile and extract all email addresses in it. Got this working with the following function:
My C# function:
public void extractMails(string filePath)
{
List<string> mailAddressList = new List<string>();
string data = File.ReadAllText(filePath);
Regex emailRegex = new Regex(#"\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnoreCase);
MatchCollection emailMatches = emailRegex.Matches(data);
StringBuilder sb = new StringBuilder();
foreach (Match emailMatch in emailMatches)
{
sb.AppendLine(emailMatch.Value);
}
string exePath = System.Reflection.Assembly.GetEntryAssembly().Location;
string dirPath = Path.GetDirectoryName(exePath);
File.WriteAllText(dirPath + "extractedEmails.txt", sb.ToString());
}
Now I have added a progressbar, since the loaded text-file can be huge. How could I fill the progressbar while the function is beeing executed that the progressbar would be filled to 100% in the end?
I would appreciate any kind of help.

#user3185569 comment is correct. I am offering a different kind of solution without using async or await, just in case you are using an older version of Visual Studio.
Basically you need to spin your task up in a new thread, then use Invoke() to update the progress bar. Here is a simple example:
private int _progress;
private delegate void Delegate();
private void btnStartTask_Click(object sender, EventArgs e)
{
// Initialize progress bar to 0 and task a new task
_progress = 0;
progressBar1.Value = 0;
Task.Factory.StartNew(DoTask);
}
private void DoTask()
{
// Simulate a long 5 second task
// Obviously you'll replace this with your own task
for (int i = 0; i < 5; i++)
{
System.Threading.Thread.Sleep(1000);
_progress = (i + 1)*20;
if (progressBar1.InvokeRequired)
{
var myDelegate = new Delegate(UpdateProgressBar);
progressBar1.Invoke(myDelegate);
}
else
{
UpdateProgressBar();
}
}
}
private void UpdateProgressBar()
{
progressBar1.Value = _progress;
}

You just iterate through all the objects in the file that you want. You need the amount of objects in there,then you multiply the current iterator by 100 divided by the total amount of objects. Theres your persentage. Now update the process of the bar with the value you got.

Related

For loop to make a typing effect not working (c#) [duplicate]

I am working on a WinForm project where I have a label in a for loop. I want to show the label each time after executing the label.text statement. But it doesn't show for every time, rather it shows after for loop is finished.
I tried to achieve this by using Thread.Sleep(). But I can't. Please help me.
NOTE :- lblProgress is a Label
Here's my coding.
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(10000);
}
Whenever you create a WinForm application, it is spun up into a new process and a new thread is created. Any updates to the User Interface are all done on the same thread as your process. This means when your application is doing "busy work", your UI will be blocked because they are on the same thread. What this means is that, in order to achieve what it is you're trying to achieve, you have to do a little extra work.
First step we need to do is create a function for your work routine (we could use an anonymous function, but since you are new to C#, I think it'll be easier to understand if we break it out), like this:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
Next, we need to create a new thread that executes our DoWork() function. Its unclear what the "trigger" is for doing your work, but I'm going to assume its a button click:
private void button1_click(object sender, EventArgs e)
{
var work = new Thread(DoWork);
work.Start();
}
So now, whenever someone click the button, we will start a new thread that executes our DoWork function in that thread. The new thread spawns, then execution is immediate returned and our GUI will now update in real time as our thread is executing in the background.
But wait! We still have one more problem to take care of. The problem is that Window's form controls are not thread safe and if we try to update a control from another thread, other then the GUI's thread, we will get a cross-thread operation error. The key to fixing this is to use InvokeRequired and Invoke.
First, we need to make another function that does just the label update:
private void SetProgressLabel(int progress)
{
lblProgress.Text = "Hello World" + progress;
}
In your form class, we also need to create a new delegate:
public partial class Form1 : Form
{
private delegate void ProgressCallback(int progress);
// ..
// The rest of your code
// ..
}
Finally, change your DoWork() method to something like this:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
This uses the label's (derived from Control) InvokeRequired property to determine if an Invoke is required. It returns true or false. If its false, we can just call our SetProgressLabel() function like we'd normally do. If its true, we must use Invoke to call our function instead.
Congratulations! You just made your first thread safe application.
Now, just as an aside note, you are not properly releasing and disposing of your objects. I recommend you change your DoWork() code to something like this:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
using (dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString))
{
dest.Open();
using (destcmd = new SqlCommand(checkout, dest))
{
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
}
}
Because I wrapped your IDisposable's into using blocks, the resources will automatically be disposed of once it goes out of scope.
Although threading would be the more ideal solution another solution is:
Application.DoEvents()
this will give the UI thread time to update.
Example
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Application.DoEvents();
}
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
var task = Task.Factory.StartNew(() =>
{
//Thread.Sleep(1000);
lblProgress.Text = "Hello World" + i;
}, CancellationToken.None, TaskCreationOptions.None, ui);
task.Wait();
}
});
If you are executing the mentioned code on the UI thread, UI will be refreshed only after entire for loop is executed. Based on your needs, progress bar/background worker kind of set up looks suitable.

C# array available outside function

private void frmExecute_Load(object sender, EventArgs e)
{
string[] data = File.ReadAllLines("c:\\toyotoconsole\\tssetup.txt");
// Loop through each line, split, add if server
for (int i = 0; i < data.Length; i++)
{
var serverValues = data[i].Split('|');
}
}
Okay first C# windows project, which is cool since I have never really touched this beyond the odd debugging foray.
My problem is the array serverValues is built in a function, how do I make this array available for all functions (not sure if using the right term here) in the same form script (probably the wrong word there as well). Is there a public declaration?
Technically, you should turn local variable serverValues into a field (or property):
private string[] serverValues = new string[0];
private void frmExecute_Load(object sender, EventArgs e) {
...
serverValues = data[i].Split('|');
...
}
However, as one can see, you rewrite serverValues within the loop; that's why serverValues will contain the last line splitted only. Another issue is mixing Business logic (ServerValues) and UI (form loading).
It seems you want something like this:
using System.Linq;
...
private string[] m_ServerValues = null;
// Pure buiness logic: ServerValues without any UI (form)
public string[] ServerValues {
get {
// If we have the array we return it
if (m_ServerValues != null)
return m_ServerValues;
// otherwise we compute it
m_ServerValues = File
.ReadLines("c:\\toyotoconsole\\tssetup.txt")
.SelectMany(line => line.Split('|'))
.ToArray();
// And return it
return m_ServerValues;
}
}
// UI: form loading
private void frmExecute_Load(object sender, EventArgs e) {
// If you want to prefetch (it's not necessary)
var values = ServerValues;
}
// Declare a private property in you class
private serverValues = Array.Empty<string>();
And then use within any event
private void frmExecute_Load(object sender, EventArgs e)
{
string[] data = File.ReadAllLines("c:\\toyotoconsole\\tssetup.txt");
// Loop through each line, split, add if server
for (int i = 0; i < data.Length; i++)
{
serverValues = data[i].Split('|');
}
}

Change TextBlock.Inlines from Backgroundworker

Is there any way to change the inlines from a BackgroundWorker?
I tried the following:
private void test()
{
var rows = GetDataGridRows(dgVarConfig);
foreach (DataGridRow r in rows)
{
TextBlock tb = cMatchEx.GetCellContent(r) as TextBlock;
if (!syntaxWorker.IsBusy)
syntaxWorker.RunWorkerAsync(new KeyValuePair<TextBlock, String>(tb, tb.Text));
}
}
private void syntaxWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (e.Argument == null)
Thread.Sleep(100);
else
{
KeyValuePair<TextBlock, String> kvp = (KeyValuePair<TextBlock, String>)e.Argument;
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
}
}
private void syntaxWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
KeyValuePair<TextBlock, List<Run>> kvp = (KeyValuePair<TextBlock, List<Run>>)e.Result;
TextBlock tb = kvp.Key;
tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));
}
}
And the syntax class:
public static class Syntax
{
static Regex subFormula = new Regex(#"\w+\(\)");
static Regex sapFormula = new Regex(#"\w+\(([^)]+)\)");
static Regex strings = new Regex(#"\'[^']+\'");
static Regex numerals = new Regex(#"\b[0-9\.]+\b");
static Regex characteristic = new Regex(#"(?:)?\w+(?:)?");
static Regex andOr = new Regex(#"( and )|( AND )|( or )|( OR )");
static Regex not = new Regex(#"(not )|(NOT )");
private static Brush[] colorArray;
public static List<Run> Highlight(String input)
{
colorArray = new Brush[input.Length];
for (int i = 0; i < input.Length; i++)
colorArray[i] = Brushes.Black;
//Reihenfolge beibehalten!!
assignColor(Brushes.Blue, characteristic.Matches(input));
assignColor(Brushes.Black, andOr.Matches(input));
assignColor(Brushes.Black, numerals.Matches(input));
assignColor(Brushes.Orange, strings.Matches(input));
assignColor(Brushes.DeepPink, subFormula.Matches(input));
assignColor(Brushes.Green, sapFormula.Matches(input));
assignColor(Brushes.Green, not.Matches(input));
int index = 0;
List<Run> runList = new List<Run>();
foreach (Char character in input)
{
runList.Add(new Run(character.ToString()) { Foreground = colorArray[index] });
index++;
}
colorArray = null;
return runList;
}
public static void Check(TextBlock textBlock)
{
}
private static void assignColor(Brush brush, MatchCollection matchCollection)
{
foreach (Match match in matchCollection)
{
int start = match.Index;
int end = start + match.Length;
for (int i = start; i < end; i++)
{
colorArray[i] = brush;
}
}
}
}
I alway get this error: The calling thread cannot access this object because a different thread owns it.
I tried many different things: return the runList with progress changed, changed the static syntax class to a normal class.. but nothing worked, its always the same error.
I also tried to invoke it from the Backgroundworker.. that means call
List<Run> runList = Syntax.Highlight(kvp.Value);
this.Dispatcher.Invoke((Action)(() =>
{
runList.ForEach(x => publicRunList.Add(x));
}));
Anybody knows the problem?
Use
tb.Dispatcher.Invoke(() => {
tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));
});
instead of
tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));
Gui elements can only be accessed from the Gui thread. Using Dispatcher.Invoke ensures that the invoked action runs on it.
You are also creating Run objects in Syntax.Highlight. You also have to create Gui elements on the Gui thread. So you should also wrap this call in a dispatcher invoke:
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
This should work:
//this runs synchronously
kvp.Key.Dispatcher.Invoke(() => {
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
});
//this runs asynchronously
kvp.Key.Dispatcher.BeginInvoke((Action)(() => {
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
}));
This probably defeats the purpose of why you wanted to use a BackgroundWorker in the first place. I'd suggest to change the interface of Syntax.Highlight to return a list of tuples with the string and the highlight color instead, and then create the Run objects on the Gui thread.
Edit:
As Gopichandar noted, using BeginInvoke executes the given Action asynchronously, so that would solve the freezing of the application. It would still take a couple of seconds until all elements are added to the Gui though.
In WPF, only the thread that the UI element belongs to (i.e. the UI thread) can communicate with it. The DoWork part of the BackgroundWorker is executed in a different thread and thus cannot do anything UI-related. The same thing goes for Timers instead of BackgroundWorkers.
But if you create the BackgroundWorker with var worker = new BackgroundWorker {WorkerReportsProgress = true}; then you can set an event handler for ProgressChanged. Inside your _DoWork(), you can then say: (sender as BackgroundWorker).ReportProgress, which will call your ProgressChanged event handler in the original thread, where you can manipulate the UI elements.
Full example:
http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/

Display strings from a list one by one into a label

I have a label. I have a list. when I do "label1.Text = match.Value;", it just displays the last item of the list, as opposed to 1 string that changes each time I click a button. The code is:
private void frontPageToolStripMenuItem_Click(object sender, EventArgs e)
{
const string url = "http://reddit.com/r/pics";
var source = getSource(url);
var regex = new Regex([regex removed]);
var links = new List<string>();
var titles = new List<string>();
foreach (Match match in regex.Matches(source))
{
links.Add(match.Groups[1].Value);
titles.Add(match.Groups[2].Value);
}
foreach (var title in titles)
{
label1.Text = title; /*it just shows the last 'title' in 'titles', I want it to start at the first, and go to the next title every time the event occurs (frontPageToolStripMenuItem_Click)*/
}
}
Thanks in advance!
You need to initialize the list outside of your click event handler. You could create an FetchImageData method that is called when your program starts (perhaps call it from the constructor of your class). Or you could call it the list the first time the click event is fired.
private int clickCounter = 0;
private List<string> links;
private List<string> titles;
private void FetchImageData()
{
links = new List<string>();
titles = new List<string>();
const string url = "http://reddit.com/r/pics";
var source = getSource(url);
var regex = new Regex([regex removed]);
foreach (Match match in regex.Matches(source))
{
links.Add(match.Groups[1].Value);
titles.Add(match.Groups[2].Value);
}
}
You haven't said what should happen when the user clicks more times than there are elements. One option is to wrap around and starts again from the beginning. This can be achieved using the % operator.
private void frontPageToolStripMenuItem_Click(object sender, EventArgs e)
{
if (titles == null) { FetchImageData(); }
label1.Text = titles[clickCounter % titles.Count];
clickCounter++;
}
UI thread doesn't get a chance to update the label because your for loop is on the UI thread. Even if you move the for loop to a background thread good chance you 'll only see some flickering and then the final string. Just append the strings to a textbox or output them using Debug.writeLine. This will show you that you are reading the correct strings.
To change the label just once don't do it in a for loop
1) titles should be global variable
2) Move this in the constructor of the form
const string url = "http://reddit.com/r/pics";
var source = getSource(url);
var regex = new Regex("<a class=\"title \" href=\"(.*?)\" >(.*?)<");
titles = new List<string>();
foreach (Match match in regex.Matches(source))
{
links.Add(match.Groups[1].Value);
titles.Add(match.Groups[2].Value);
}
label1.Text = first value of the titles
3) Your event handler should be like this:
private void frontPageToolStripMenuItem_Click(object sender, EventArgs e)
{
label1.Text = next value of the titles variable;
}
In Second foreach loop put label1.Text += title; instead of label1.Text = title; then it will work fine.

Making an method execute on interval in Winforms in C#

I’m making an RSS reader, I want it to be able to update at a given interval of time.
I’m not interested in using the Winforms Timer component.
I was more thinking about using the System.Threading.Timer.
The method I want to execute on an interval looks like this:
public void getNews()
{
for (int i2 = 0; i2 < urlList.Count; i2++)
{
//Creates a XmlTextReader which reads from the url entered in input field
rssReader = new XmlTextReader(urlList[i2]);
//Creates an xml doc to save the content of the entered path
rssDoc = new XmlDocument();
//Loads the xml content from the reader into a XmlDocument
rssDoc.Load(rssReader);
//Make a loop to search for the <rss> tag
for (int i = 0; i < rssDoc.ChildNodes.Count; i++)
{
//If the childenode is the rss tag
if (rssDoc.ChildNodes[i].Name == "rss")
{
//the <rss> tag is found, and we know where it is
nodeRss = rssDoc.ChildNodes[i];
}
}
//Make a loop to search for the <channel> tag
for (int i = 0; i < nodeRss.ChildNodes.Count; i++)
{
//If the childnode is the channel tag
if (nodeRss.ChildNodes[i].Name == "channel")
{
//The channel tag is found and we know where it is
nodeChannel = nodeRss.ChildNodes[i];
}
}
//Make a loop to search for the <item> tag
for (int i = 0; i < nodeChannel.ChildNodes.Count; i++)
{
//If the childnode is the item tag
if (nodeChannel.ChildNodes[i].Name == "item")
{
//the item tag is found, and we know where it is
nodeItem = nodeChannel.ChildNodes[i];
//Creates a new row in the LstView which contains information from inside the nodes
rowNews = new ListViewItem();
rowNews.Text = nodeItem["title"].InnerText;
rowNews.SubItems.Add(nodeItem["link"].InnerText);
if (this.lstView.InvokeRequired)
{
AddItemCallback d = new AddItemCallback(getNews);
this.Invoke(d);
return;
}
lstView.Items.Add(rowNews);
}
}
}
}
This is the button, that executes the method:
private void btnRead_Click(object sender, EventArgs e)
{
lstView.Items.Clear();
Thread myThread = new Thread(getNews);
myThread.Start();
}
How do I execute my getNews() method on a specific interval? Examples with my code are very appreciated.
User Timer Control and write code in Tick event...
http://www.c-sharpcorner.com/UploadFile/mahesh/WorkingwithTimerControlinCSharp11302005054911AM/WorkingwithTimerControlinCSharp.aspx
I would start a new thread and sleep for the specified interval at the end of it.
for example
you would have a member variable for whether the process is running and the interval
private bool _isRunning = false;
private int _interval = 1000;
then in your start method create a new thread
public void Start()
{
ThreadStart oThreadStart = new ThreadStart(DoWork);
Thread t = new Thread(oThreadStart);
_isRunning = true;
t.Start();
}
public void Stop()
{
_isRunning = false;
}
private void DoWork()
{
while(_isRunning)
{
// do work
Thread.Sleep(_interval);
}
Thread.CurrentThread.Join();
}
You then have all processing on one thread and it sleeps while not in use (eg waiting for the next 'tick')
also, using this method prevents the possibility of a second tick event being fired until the first one has finished processing
I like the Reactive Extensions for these things.
var observable = Observable.Interval(TimeSpan.FromSeconds(2)); // Interval in seconds
var subscription = observable.Subscribe(_ => getNews());
// Or if it does not work then try this:
var subscription = observable.ObserveOnWindowsForms().Subscribe(_ => getNews());
using (subscription)
{
Console.WriteLine("Press any key to stop...");
Console.ReadKey();
}
Instead of stopping with a console key press, you can call .Dispose() on subscription and delete the whole using block.
For testing this approach, try replacing _ => getNews() with Console.WriteLine and then you will see how it works :) (it is a example from http://rxwiki.wikidot.com/101samples)

Categories