Increment ProgressBar within another class - c#

it's my first question I'm asking here, so please be gentle with me ;)
So I've actually got two WinForms in my C# application I'm writing at the moment (I'm quite new to C#).
This window has a button, which saves photos from an usb device you selected before in a list box to another folder.
After clicking on this button my main thread is of course busy with copying, so I decided to create another WinForm which contains my ProgressBar.
Foreach completed copy, I want to increment my ProgressBar accordingly.
So I count the number of copies I have to do and give it the progressbar as maximum. But my problem at the moment is, that I really don't know how to increment the ProgressBar without getting a Thread Unsafe Exception.
Here's my ProgressWindow code:
public partial class ProgressWindow : Form
{
BackgroundWorker updateProgressBarThread = new BackgroundWorker();
private Boolean _isThreadRunning = false;
public Boolean IsThreadRunning
{
get { return _isThreadRunning; }
set { _isThreadRunning = value; }
}
private int _progressbarLength;
public int ProgressbarLength
{
get { return _progressbarLength; }
set { _progressbarLength = value; }
}
private int progress = 1;
public ProgressWindow()
{
Show();
InitializeComponent();
}
private void StartUpdateThread(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Reports progress to the ProgressChangedEvent function. (Thread Safe)
}
private void FinishProgressThread(object sender, RunWorkerCompletedEventArgs e)
{
if (!_isThreadRunning)
{
MessageBox.Show("Erfolgreich kopiert");
Close();
}
}
private void ProgressChangedEvent(object sender, ProgressChangedEventArgs e)
{
this.copyProgressbar.Value = e.ProgressPercentage;
this.progressStatus.Text = e.ProgressPercentage.ToString();
}
public void CallUpdateThread()
{
updateProgressBarThread.WorkerReportsProgress = true;
updateProgressBarThread.DoWork += new DoWorkEventHandler(StartUpdateThread);
updateProgressBarThread.ProgressChanged += new ProgressChangedEventHandler(ProgressChangedEvent);
updateProgressBarThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FinishProgressThread);
updateProgressBarThread.RunWorkerAsync();
}
}
I want to increment my ProgressBar with 1 after each succesful copy.
How do I do this from my main thread?
This is the function which actually handles the copy process
private void SaveFile(System.IO.DirectoryInfo root)
{
try
{
IEnumerable<DirectoryInfo> directoriesNames = root.EnumerateDirectories();
// New instance of thread ProgressWindow.
ProgressWindow progress = new ProgressWindow();
progress.CallUpdateThread();
foreach (DirectoryInfo element in directoriesNames)
{
// Query all subdirectories and count everything with the in the configuration made settings.
if (!element.Attributes.ToString().Contains("System"))
{
// Now we insert the configuration we applied.
String fileExtension = null;
if (Properties.Settings.Default._configPhoto)
{
fileExtension = "*.jpg";
}
if (Properties.Settings.Default._configWordDocument)
{
fileExtension = "*.odt";
}
FileInfo[] jpgList = element.GetFiles(fileExtension, SearchOption.AllDirectories);
// set the size of the progress bar
progress.ProgressbarLength = jpgList.Count();
// Now we go through all our results and save them to our backup folder.
foreach (FileInfo tmp in jpgList)
{
string sourceFilePath = tmp.FullName;
string destFilePath = PATHTOBACKUP + "\\" + tmp.Name;
progress.IsThreadRunning = true;
try
{
System.IO.File.Copy(sourceFilePath, destFilePath, true);
}
catch (IOException ioe)
{
MessageBox.Show(ioe.Message);
}
}
}
}
// progress.IsThreadRunning = false;
}
catch (UnauthorizedAccessException e)
{
MessageBox.Show(e.Message);
}
}
It's pretty obvious that I have to do this after this function
System.IO.File.Copy(sourceFilePath, destFilePath, true);
But how do I report this to my ProgressWindow?
I really hope I explained it well enough, not sure if I'm missing something important.
Thanks in advance guys

Here is a compact example of the key components:
Clicking button starts new thread worker
Progress is done by file lengths, not by number of files
BeginInvoke used to update the progress bar (avoid cross Thread exception)
ProgressBar pb = new ProgressBar() { Minimum = 0, Maximum = 100 };
Button btn = new Button();
btn.Click += delegate {
Thread t = new Thread(() => {
DirectoryInfo dir = new DirectoryInfo("C:\\temp\\");
var files = dir.GetFiles("*.txt");
long totalLength = files.Sum(f => f.Length);
long length = 0;
foreach (var f in files) {
length += f.Length;
int percent = (int) Math.Round(100.0 * length / totalLength);
pb.BeginInvoke((Action) delegate {
pb.Value = percent;
});
File.Copy(f.FullName, "...");
}
});
t.IsBackground = true;
t.Start();
};

Related

Cancelling task in C#

I have a Form with two buttons (Start , Stop).
When I press Start Button a Task is initialized and a function is called that keeps running until Stop button is pressed.
But When I press Stop button Form freezes. why?
I have copied snippet from StackOverflow but it still freezes Form.
So tell me how to Cancel Task properly?
public partial class Form1 : Form
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private Task _task;
public Form1()
{
InitializeComponent();
}
//Funtion that runs when Task is initialized.
private void EventLoop(CancellationToken token)
{
//Code here..
while (!token.IsCancellationRequested)
{
//Code here..
}
if (token.IsCancellationRequested)
{
MessageBox.Show("Operation Complete..!!");
}
}
//Function for Start Button.
private void Start_Click(object sender, EventArgs e)
{
_task = Task.Factory.StartNew(() => EventLoop(_cts.Token), _cts.Token);
}
//Function for Stop button.
private void Stop_Click(object sender, EventArgs e)
{
_cts.Cancel();
}
}
Similar Example from MSDN:
var compute = Task.Factory.StartNew(() =>
{
return SumRootN(j);
}, tokenSource.Token);`
Form After Stop button is pressed.
token.IsCancellationRequested is true .
Full EventLoop() Function.
private void EventLoop(CancellationToken token)
{
SerialPort sp = new SerialPort();
string text, batch, part, courseName;
text = batch = part = courseName = "";
int courseId = 0;
this.Invoke((MethodInvoker)delegate()
{
text = portCB.SelectedItem.ToString();
batch = comboBox2.SelectedItem.ToString();
part = comboBox3.SelectedItem.ToString();
courseName = comboBox1.SelectedItem.ToString();
progressBar1.Value = 20;
using (Model1 db = new Model1())
{
courseId = db.Courses.Where(c => c.Course_name.ToUpper() == courseName.ToUpper()).Select(c => c.Course_Id).Single();
}
});
sp.PortName = text;
sp.BaudRate = 9600;
sp.Open();
while (!token.IsCancellationRequested)
{
text = sp.ReadLine();
if (text.Contains("Found ID #"))
{
this.Invoke((MethodInvoker)delegate()
{
textBox2.Clear();
textBox2.Text = "Getting Registation ID.\n";
progressBar1.Value = 60;
});
string splitText = text.Split('#')[1];
int end = splitText.IndexOf(' ');
int id = int.Parse(splitText.Substring(0, end));
using (Model1 db = new Model1())
{
var result = db.Students.Where(s => s.Reg_Id == id && s.Batch == batch && s.Class == part).Select(s => s).SingleOrDefault();
if (result != null)
{
Attendance a = new Attendance();
a.Course_Id = courseId;
a.Student_Id = id;
a.Status = "P";
a.Date = DateTime.Today.Date;
a.Batch = batch;
a.Part = part;
db.Attendances.Add(a);
string message = "";
if (db.SaveChanges() != 0)
{
message = "Attendance Uploaded..!!\n";
}
else
{
message = "Attendance Not Uploaded ..!!\n";
}
this.Invoke((MethodInvoker)delegate()
{
progressBar1.Value = 100;
textBox2.AppendText(message);
});
}
else
{
this.BeginInvoke((MethodInvoker)delegate()
{
textBox2.AppendText("Student Does not belong to Specified Batch Or Part..\n");
});
}
}
}
else
{
this.Invoke((MethodInvoker)delegate()
{
textBox2.AppendText("No Match Found..!! \n");
});
}
this.Invoke((MethodInvoker)delegate()
{
textBox1.AppendText(text + "\n");
});
}
sp.Close();
// This exception will be handled by the Task
// and will not cause the program to crash
if (token.IsCancellationRequested)
{
//MessageBox.Show("Operation Comptele..!!");
}
}
You call MessageBox.Show("Operation Complete..!!"); in the progress of cancellation. This is highly not recommended, not talking about that you are calling an UI operation from other than the UI thread.
Comment the MessageBox.Show("Operation Complete..!!"); line out
* Edit *
Question author's comment on his original question, found the mistake, which line was removed from the post. Here are my conclusion:
Always try to isolate an issue, and reproduce in its purest form. During that process you will probably diagnose and find the issues itself :-).
So if the code with issue is long to post, it is definitely not the way just deleting lines then post it. The way is deleting lines and see if the issue exist, with other words: Isolating the issue in its purest reproducable form
please use async call on your task
private async void Start_Click(object sender, EventArgs e)
{
_task = Task.Factory.StartNew(() => EventLoop(_cts.Token), _cts.Token);
await task;
}

Stop thread from working after it fills the queue

To stop The thread based on a flag Finesedequeue In the below thread method I did a loop in the stop button(its purpose is to stop this thread after it fills the queue named _textFromFilesQueue ) and the main problem in this is that my form is freezing
Here is my code:
The Threads Method:
public static bool Finisedqueue ;
public void Read(string inputDirectoryPath)
{
try
{
Thread.CurrentThread.IsBackground = true;
Finisedqueue = false;
Console.WriteLine("thread 1 started");
if (Form1.IsStarted)
{
Thread.Sleep(3000);
var dir = new DirectoryInfo(inputDirectoryPath);
FileInfo[] f = dir.GetFiles("*.txt");
if (f.Length > 0)
{
Console.WriteLine("{0} directory contains {1} Files",inputDirectoryPath,f.Length);
int counter = 0;
foreach (System.IO.FileInfo fi in f)
{
counter++;
var filePath = Path.Combine(fi.DirectoryName, fi.Name);
string textFromFile = File.ReadAllText(filePath);
_textFromFilesQueue.Enqueue(textFromFile);
Console.WriteLine("The text inside file number {0} is : {1}",counter,textFromFile);
fi.Delete();
Console.WriteLine("deleting file number {0} from input folder",counter);
}
Finisedqueue = true;
Console.WriteLine("finished Deleting files");
}
else
{
Console.WriteLine("{0} Has no files inside it ",inputDirectoryPath);
}
}
}
catch (Exception excep)
{
Console.WriteLine("An error occurred: '{0}'", excep);
throw(excep);
}
}
The Stop Button Click:
private void stop_Click(object sender, EventArgs e)
{
while (!Thread1.Finisedqueue)
{
Console.WriteLine("Queue from First Thread is still Enqueuing data");
}
Thread1Timer.Dispose();
button3.Enabled = true;
textBox3.Enabled = true;
}
> the main problem in this is that my form is freezing
IF any body can help me in this Thank you very much
When busy waiting it is a good idea to sleep, so that you give other threads a chance to execute.
while (!Thread1.Finisedqueue)
{
Thread.Sleep(100);
Console.WriteLine("Queue from First Thread is still Enqueuing data");
}
You can also check out the Task class
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx which is nice for asynchronous operations such as file IO and is awaitable using the built-in async/await functionality.
private Queue<string> q = new Queue<string>();
private Task readFilesTask;
public void ReadFiles(string inputDirectory)
{
var files = Directory.GetFiles(inputDirectory, "*.txt", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
var textFromFile = File.ReadAllText(file);
q.Enqueue(textFromFile);
File.Delete(file);
}
}
private void start_Click(object sender, EventArgs e)
{
readFilesTask = Task.Run(() => ReadFiles("//path/to/dir"));
}
private void stop_Click(object sender, EventArgs e)
{
readFilesTask.Wait();
//readFilesTask.Wait(1000); // if you want kill the task after waiting for 1000 milliseconds
//Enable buttons
}

c# threads and cannot call invoke

I have start c# a few time ago, so there is somethings that are totally diferent from java, a few time ago I face a problem about a thread changing the UI (add rows to a DataGridView) and I found that I had to call the method Invoke to make that happen. I did and all works fine, but now I'm facing a new problem. So, I have a frame that will display some labels added dynamically and in Java i would like this:
Thread t = new Thread() {
public void run() {
while (true) {
// for each label
for (it = labels.iterator(); it.hasNext();) {
JLabel lb = it.next();
if (lb.getLocation().x + lb.getWidth() < 0) {
if (msgsRemover.contains(lb.getText().toString())) {
it.remove();
MyPanel.this.remove(lb);
msgsRemover.remove(lb.getText().toString());
} else {
// if there is no message to be removed, this will just continue
// going to the end of the queue
MyPanel.this.remove(lb);
MyPanel.this.add(lb);
}
MyPanel.this.repaint();
MyPanel.this.validate();
}
lb.setLocation(lb.getLocation().x - 3, 0);
}
MyPanel.this.repaint();
try {
SwingUtilities.invokeAndWait(running);
sleep(30);
} catch (InterruptedException ex) {
Logger.getLogger(MyPanel.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(MyPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
But in C# I have a problem when the thread does:
MyPanel.this.remove(lb);
MyPanel.this.add(lb);
So I did:
if (lb.Location.X + lb.Width < 0) {
if (msgsRemover.Contains(lb.Text.ToString())) {
labels.Remove(label);
this.Invoke(new MethodInvoker(() => { this.Controls.Remove(lb); }));
msgsRemover.Remove(lb.Text.ToString());
} else {
// if there is no message to be removed, this will just continue
// going to the end of the queue
this.Invoke(new MethodInvoker(() => { this.Controls.Remove(lb); }));
this.Invoke(new MethodInvoker(() => { this.Controls.Add(lb); }));
}
this.Invoke(new MethodInvoker(() => { this.Refresh(); }));
But know I'm getting an error called "Can not call Invoke or BeginInvoke on a control until the window handle has been created."
I have searched for solutions but I didn't find out what can I do to solve this.
Thank you in advance for your help!
Edit: start the thread is the last thing I do in the constructor... There is the code:
public MyPanel(Color corLabel, Color back, Font text){
this.color = corLabel;
this.backg = back;
this.textFont = text;
this.Width = 500000;
texto = new LinkedList<string>();
msgs = new LinkedList<MensagemParaEcra>();
labels = new LinkedList<Label>();
var it = labels.GetEnumerator();
var it2 = msgsRemover.GetEnumerator();
this.FlowDirection = FlowDirection.LeftToRight;
this.BackColor = backg;
this.Size = new Size(500000, 30);
this.Refresh();
startThread();
}
You must start the thread after the control has a handle created in order to be able to do Invoke, the easiest way to do that is override the OnHandleCreated method and start your thread there instead.
public MyPanel(Color corLabel, Color back, Font text)
{
this.color = corLabel;
this.backg = back;
this.textFont = text;
this.Width = 500000;
texto = new LinkedList<string>();
msgs = new LinkedList<MensagemParaEcra>();
labels = new LinkedList<Label>();
var it = labels.GetEnumerator();
var it2 = msgsRemover.GetEnumerator();
this.FlowDirection = FlowDirection.LeftToRight;
this.BackColor = backg;
this.Size = new Size(500000, 30);
this.Refresh();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
startThread();
}
The error you are receiving indicates that the target Window hasn't fully created yet. Probably the constructor hasn't finished. Try to hook up to one of the default events (Load, Show etc.) of the target window and make the invoke calls after these are handled.

Want to use Task Parallel Library with progress reporting for updating database

i developed a application where multiple connection string are stored. i just iterate in for loop and connect each db and execute sql against each db. in this way i update multiple database with bulk sql statement.
Now i need to use now Task Parallel Library to update multiple db simultaneously instead of updating one after one in loop. i also want to handle exception and also want to show multiple progress bar for multiple database operation. pause & resume functionality should be there.
when i will click on button then multiple db connection will be open and for each task a new progress bar will be added in my form. each progress bar will show each db operation progress. when any task will be finish then that corresponding progress bar will be removed from form.
any one can guide me with sample code how to do it using TPL.
here i got one code which update one progressbar but i need to update multiple progress bar.
int iterations = 100;
ProgressBar pb = new ProgressBar();
pb.Maximum = iterations;
pb.Dock = DockStyle.Fill;
Controls.Add(pb);
Task.Create(delegate
{
Parallel.For(0, iterations, i =>
{
Thread.SpinWait(50000000); // do work here
BeginInvoke((Action)delegate { pb.Value++; });
});
});
UPDATE Question
i did it in this way. code works but all progress bar value increase sequentially.
i have one form and one user control in winform apps. please have a look at my code and tell me what is wrong there.
main for code
public partial class Main : Form
{
public Main()
{
InitializeComponent();
this.DoubleBuffered = true;
}
private void btnStart_Click(object sender, EventArgs e)
{
Progress ucProgress = null;
Dictionary<string, string> dicList = new Dictionary<string, string>();
dicList.Add("GB", "conn1");
dicList.Add("US", "conn2");
dicList.Add("DE", "conn3");
fpPanel.Controls.Clear();
Task.Factory.StartNew(() =>
{
foreach (KeyValuePair<string, string> entry in dicList)
{
ucProgress = new Progress();
ucProgress.Country = entry.Key;
ucProgress.DBConnection = entry.Value;
fpPanel.BeginInvoke((MethodInvoker)delegate
{
fpPanel.Controls.Add(ucProgress);
ucProgress.Process();
});
//fpPanel.Controls.Add(ucProgress);
System.Threading.Thread.SpinWait(5000000);
}
});
}
private void Main_Resize(object sender, EventArgs e)
{
this.Invalidate();
}
private void Main_Paint(object sender, PaintEventArgs e)
{
using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle,
Color.WhiteSmoke,
Color.LightGray,
90F))
{
e.Graphics.FillRectangle(brush, this.ClientRectangle);
}
}
}
user control code
public partial class Progress : UserControl
{
public Progress()
{
InitializeComponent();
lblMsg.Text = "";
pbStatus.Minimum = 0;
pbStatus.Maximum = 100;
}
public string Country { get; set; }
public string DBConnection { get; set; }
public string Sql { get; set; }
public void SetMessage(string strMsg)
{
lblMsg.Text = strMsg;
}
public void Process()
{
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
lblMsg.BeginInvoke((MethodInvoker)delegate
{
lblMsg.Text = "Connecting country " + Country;
});
pbStatus.BeginInvoke((MethodInvoker)delegate
{
pbStatus.Value = 30;
});
System.Threading.Thread.SpinWait(50000000);
//***********
lblMsg.BeginInvoke((MethodInvoker)delegate
{
lblMsg.Text = "executing sql for country " + Country;
});
pbStatus.BeginInvoke((MethodInvoker)delegate
{
pbStatus.Value = 60;
});
System.Threading.Thread.SpinWait(50000000);
//***********
lblMsg.BeginInvoke((MethodInvoker)delegate
{
lblMsg.Text = "sql executed successfully for country " + Country;
});
pbStatus.BeginInvoke((MethodInvoker)delegate
{
pbStatus.Value = 100;
});
System.Threading.Thread.SpinWait(50000000);
});
//System.Threading.Thread.SpinWait(50000000); // do work here
}
}
perhaps it can be starting point. Handling pause/resume depends from your needs and can be tweaked.
var cancellationTokenSource = new CancellationTokenSource();
var cancellation = cancellationTokenSource.Token;
void UpdateDatabases(IEnumerable<...> databases, CancellationToken cancellation)
{
foreach(db in databases)
{
//create as many ProgressBar instances as databases you want to update
//check if ProgressBar exist, then return it and reuse, otherwise create new
ProgressBar pb = new ProgressBar();
pb.Maximum = iterations;
pb.Dock = DockStyle.Fill;
Controls.Add(pb);
//start thread for every database/progress bar
Task.Factory.StartNew(progressBar =>
{
var start = (ProgressBar)progressBar).Value; //use last value in case of pause
Parallel.For(start, iterations,
new ParallelOptions(){CancellationToken = cancellation}
(i, loopState) =>
{
if (loopState.ShouldExitCurrentIteration)
return;
//perhaps check loopState.ShouldExitCurrentIteration inside worker method
Thread.SpinWait(50000000); // do work here
BeginInvoke((Action)delegate { ((ProgressBar)progressBar).Value++; });
});
},
pb, cancellation)
.ContinueWith(task =>
{
//to handle exceptions use task.Exception member
var progressBar = (ProgressBar)task.AsyncState;
if (!task.IsCancelled)
{
//hide progress bar here and reset pb.Value = 0
}
},
TaskScheduler.FromCurrentSynchronizationContext() //update UI from UI thread
);
}
}
//.........
//Call
UpdateDatabases(databases, cancellation)
//To suspend, call
cancellationTokenSource.Cancel();
//To resume - simply call UpdateDatabases again
cancellationTokenSource = new CancellationTokenSource();
cancellation = cancellationTokenSource.Token;
UpdateDatabases(databases, cancellation)
Update
I've reviewed your code. Take a look at the revisited code and adapt it to your needs. Main mistakes - mess with closures and creating the Progress from non-ui thread. To enable parallel processing you can use Parallel.ForEach (see MSND for possible overloads). Also the design looks little bit strange for me(you're mixing data and logic in the Progress ). From UI perspective it's also strange that progress bars will appear in order of processing but not in original order as they are in list (it will be a problem if you decide to sort the list alphabetically)
I hope it help.
main for code
private void btnStart_Click(object sender, EventArgs e)
{
Progress ucProgress = null;
Dictionary<string, string> dicList = new Dictionary<string, string>();
dicList.Add("GB", "conn1");
dicList.Add("US", "conn2");
dicList.Add("DE", "conn3");
fpPanel.Controls.Clear();
Func<KeyValuePair<string, string>, object> createProgress = entry =>
{
var tmp = new Progress {Country = entry.Key, DBConnection = entry.Value};
fpPanel.Controls.Add(tmp);
return tmp;
};
Task.Factory.StartNew(() =>
{
//foreach (KeyValuePair<string, string> entry in dicList)
Parallel.ForEach(dicList,
entry =>
{
//create and add the Progress in UI thread
var ucProgress = (Progress)fpPanel.Invoke(createProgress, entry);
//execute ucProgress.Process(); in non-UI thread in parallel.
//the .Process(); must update UI by using *Invoke
ucProgress.Process();
System.Threading.Thread.SpinWait(5000000);
});
});
}
user control code
public void Process()
{
//uiScheduler - Not used
//var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
//The Task is not necessary because the Process() called from Parallel.ForEach
//Task.Factory.StartNew(() =>
//{
//BeginInvoke or Invoke required
lblMsg.BeginInvoke((MethodInvoker)delegate
{
lblMsg.Text = "Connecting country " + Country;
});
pbStatus.BeginInvoke((MethodInvoker)delegate
{
pbStatus.Value = 30;
});
System.Threading.Thread.SpinWait(50000000);
//***********
lblMsg.BeginInvoke((MethodInvoker)delegate
{
lblMsg.Text = "executing sql for country " + Country;
});
pbStatus.BeginInvoke((MethodInvoker)delegate
{
pbStatus.Value = 60;
});
System.Threading.Thread.SpinWait(50000000);
//***********
lblMsg.BeginInvoke((MethodInvoker)delegate
{
lblMsg.Text = "sql executed successfully for country " + Country;
});
pbStatus.BeginInvoke((MethodInvoker)delegate
{
pbStatus.Value = 100;
});
System.Threading.Thread.SpinWait(50000000);
//});
//System.Threading.Thread.SpinWait(50000000); // do work here
}

C# Thread dataGridView and Loading

Im trying to load a dataGridView which takes some time, so ive come up with an idea of hiding the datagridview and put an image over the top which says Loading... when finished, the image goes away and the datagrid reappears. Ive tried to do this using threading but having no luck.
Can somebody tell me if i am approaching this in the right way?
Label loadingText = new Label();
PictureBox loadingPic = new PictureBox();
private void TelephoneDirectory_Load(object sender, EventArgs e)
{
dataGridView1.Visible = false;
Thread i = new Thread(LoadImage);
i.Start();
Thread t = new Thread(LoadData);
t.Start();
}
void LoadImage()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(LoadImage));
}
else
{
loadingPic.Image = Properties.Resources.ReportServer;
loadingPic.Location = new Point(0, 0);
loadingPic.Name = "loadingPic";
loadingPic.Dock = DockStyle.Fill;
loadingPic.SizeMode = PictureBoxSizeMode.CenterImage;
loadingText.Text = "Loading, please wait...";
loadingText.Name = "loadingText";
loadingText.TextAlign = ContentAlignment.MiddleCenter;
loadingText.Size = new Size(this.Size.Width, 30);
loadingText.Font = new System.Drawing.Font("Segoe UI", 9);
loadingText.Location = new Point(0, (this.Size.Height / 2 + 10));
this.Controls.AddRange(new Control[] { loadingPic, loadingText });
loadingText.BringToFront();
}
}
private void LoadData()
{
if (dataGridView1.InvokeRequired)
{
dataGridView1.Invoke(new MethodInvoker(this.LoadData));
}
else
{
DirectorySearcher sea = null;
DirectoryEntry dir = null;
DirectoryEntry d = null;
string domainname = System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain().Name;
domainname = domainname.Replace(".", ",DC=");
try
{
dir = new DirectoryEntry();
dir.Path = "LDAP://DC=" + domainname;
sea = new DirectorySearcher(dir);
sea.Filter = "(&(objectClass=user)(extensionAttribute1=1)(telephoneNumber=*))";
sea.PageSize = 2000;
SearchResultCollection res = sea.FindAll();
foreach (SearchResult result in res)
{
//DO ALL MY STUFF
dataGridView1.Rows.Add(row);
}
}
catch { }
LoadDataComplete();
}
}
void LoadDataComplete()
{
PictureBox loadingGraphic = this.Controls["loadingPic"] as PictureBox;
Label LoadingLabel = this.Controls["loadingText"] as Label;
DataGridView dataGrid = this.Controls["dataGridView1"] as DataGridView;
dataGrid.Visible = true;
LoadingLabel.Visible = false;
LoadingLabel.Dispose();
loadingGraphic.Visible = false;
loadingGraphic.Dispose();
dataGridView1.Sort(dataGridView1.Columns[0], ListSortDirection.Ascending);
dataGridView1.ClearSelection();
}
Spawning threads for this might not be the best idea, you could use ThreadPool or BackgroundWorker
Loading image should be fast enough that you could just do it synchronously, so make sure you actually need to do some operation on the other thread before you actually need it.
Ask yourself questions like: what if actually my image will load later then my dataTable? ("but it loads faster every time I checked" is not an valid argument when talking about threads)
At the beginning of your methods you are using .Invoke which basically means "wait for UI thread and invoke my code on it synchronously" which bombards your whole idea.
Try something like this:
Load image synchronously
Use ThreadPool to load your DataTable in it, but without using .Invoke
When it's loaded and you need to interact with UI -> then put your code in .Invoke()
Pseudocode coud look like this:
private void TelephoneDirectory_Load(object sender, EventArgs e)
{
dataGridView1.Visible = false;
LoadImage();
ThreadPool.QueueUserWorkItem(new WaitCallback(o => LoadData()));
}
void LoadData()
{
//...Do loading
//but don't add rows to dataGridView
if (dataGridView1.InvokeRequired)
{
//Invoke only the ui-interaction code
dataGridView1.Invoke(new MethodInvoker(this.LoadDataComplete));
}
}
void LoadDataComplete()
{
foreach (SearchResult result in res)
{
//DO ALL MY STUFF
//If do all my stuff is compute intensive and doesn't require UI,
//put it before Invoke() (like here)
dataGridView1.Rows.Add(row);
}
//Rest of code
}

Categories

Resources