Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Hive.Server.Console/HiveServerManagementConsole.cs @ 1509

Last change on this file since 1509 was 1509, checked in by msteinbi, 15 years ago

Added Interfaces for Execution Engine (#572)

File size: 21.3 KB
RevLine 
[794]1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.ComponentModel;
25using System.Data;
26using System.Drawing;
27using System.Linq;
28using System.Text;
29using System.Windows.Forms;
[831]30using HeuristicLab.Hive.Contracts.Interfaces;
31using HeuristicLab.Hive.Contracts.BusinessObjects;
[904]32using HeuristicLab.Hive.Contracts;
[794]33
[1089]34namespace HeuristicLab.Hive.Server.ServerConsole {
[794]35
[1030]36  public delegate void closeForm(bool cf, bool error);
[794]37
38  public partial class HiveServerManagementConsole : Form {
39
40    public event closeForm closeFormEvent;
41
[1136]42    #region private variables
43    private ResponseList<ClientGroup> clients = null;
44    private ResponseList<ClientInfo> clientInfo = null;
45    private ResponseList<Job> jobs = null;
[844]46
[1449]47    private Dictionary<Guid, ListViewGroup> clientObjects;
48    private Dictionary<Guid, ListViewItem> clientInfoObjects;
49    private Dictionary<Guid, ListViewItem> jobObjects;
[1261]50
[1136]51    private Job currentJob = null;
52    private ClientInfo currentClient = null;
[1148]53    private string nameCurrentJob = "";
54    private string nameCurrentClient = "";
[1136]55    private bool flagJob = false;
56    private bool flagClient = false;
[1098]57
[1483]58    private List<Changes> changes = new List<Changes>();
59
[1136]60    private ToolTip tt = new ToolTip();
61    #endregion
62
[794]63    public HiveServerManagementConsole() {
64      InitializeComponent();
[1037]65      AddClients();
66      AddJobs();
[1018]67      timerSyncronize.Start();
[844]68    }
[831]69
[1136]70    /// <summary>
71    /// event on Ticker
72    /// </summary>
73    /// <param name="obj"></param>
74    /// <param name="e"></param>
[1267]75    private void TickSync(object obj, EventArgs e) {
[1483]76      if (!updaterWoker.IsBusy) {
77        updaterWoker.RunWorkerAsync();
78      }
[1018]79    }
80
[1136]81    /// <summary>
82    /// Adds clients to ListView and TreeView
83    /// </summary>
[1037]84    private void AddClients() {
[1030]85      try {
[1449]86        clientObjects = new Dictionary<Guid, ListViewGroup>();
87        clientInfoObjects = new Dictionary<Guid, ListViewItem>();
[1030]88        IClientManager clientManager =
89          ServiceLocator.GetClientManager();
[831]90
[1030]91        clients = clientManager.GetAllClientGroups();
92        lvClientControl.Items.Clear();
93        int count = 0;
[1483]94        List<Guid> inGroup = new List<Guid>();
[1030]95        foreach (ClientGroup cg in clients.List) {
96          ListViewGroup lvg = new ListViewGroup(cg.Name, HorizontalAlignment.Left);
[1483]97          foreach (ClientInfo ci in cg.Resources) {
98            ListViewItem item = null;
99            if ((ci.State == State.offline) || (ci.State == State.nullState)) {
100              item = new ListViewItem(ci.Name, 3, lvg);
101            } else {
102              item = new ListViewItem(ci.Name, count, lvg);
103            }
104            item.Tag = ci.Id;
[1261]105            lvClientControl.Items.Add(item);
106            clientInfoObjects.Add(ci.Id, item);
[1030]107            count = (count + 1) % 3;
[1483]108            inGroup.Add(ci.Id);
[1495]109
[1030]110          }
[1483]111          lvClientControl.BeginUpdate();
[1030]112          lvClientControl.Groups.Add(lvg);
[1483]113          lvClientControl.EndUpdate();
[1261]114          clientObjects.Add(cg.Id, lvg);
[1030]115        } // Groups
116
117        clientInfo = clientManager.GetAllClients();
[1483]118        ListViewGroup lvunsorted = new ListViewGroup("no group", HorizontalAlignment.Left);
[1030]119        foreach (ClientInfo ci in clientInfo.List) {
[1483]120          bool help = false;
121          foreach (Guid client in inGroup) {
122            if (client == ci.Id) {
123              help = true;
124              break;
125            }
126          }
127          if (!help) {
128            ListViewItem item = null;
129            if ((ci.State == State.offline) || (ci.State == State.nullState)) {
130              item = new ListViewItem(ci.Name, 3, lvunsorted);
131            } else {
132              item = new ListViewItem(ci.Name, count, lvunsorted);
133            }
134            item.Tag = ci.Id;
135            lvClientControl.Items.Add(item);
136            count = (count + 1) % 3;
137          }
[831]138        }
[1483]139        lvClientControl.BeginUpdate();
[1030]140        lvClientControl.Groups.Add(lvunsorted);
[1483]141        lvClientControl.EndUpdate();
[1136]142        if (flagClient) {
143          ClientClicked();
144        }
[1018]145      }
[1030]146      catch (Exception ex) {
147        closeFormEvent(true, true);
148        this.Close();
149      }
[1018]150    }
[956]151
[1483]152
153    List<ListViewGroup> jobGroup;
[1136]154    /// <summary>
155    /// Adds jobs to ListView and TreeView
156    /// </summary>
[1037]157    private void AddJobs() {
[1030]158      try {
[1449]159        jobObjects = new Dictionary<Guid, ListViewItem>();
[1030]160        IJobManager jobManager =
161          ServiceLocator.GetJobManager();
162        jobs = jobManager.GetAllJobs();
[1018]163
[1030]164        lvJobControl.Items.Clear();
[1018]165
[1037]166        ListViewGroup lvJobCalculating = new ListViewGroup("calculating", HorizontalAlignment.Left);
[1030]167        ListViewGroup lvJobFinished = new ListViewGroup("finished", HorizontalAlignment.Left);
[1037]168        ListViewGroup lvJobPending = new ListViewGroup("pending", HorizontalAlignment.Left);
[1483]169
170        jobGroup = new List<ListViewGroup>();
171        jobGroup.Add(lvJobCalculating);
172        jobGroup.Add(lvJobFinished);
173        jobGroup.Add(lvJobPending);
[1495]174
[1030]175        foreach (Job job in jobs.List) {
[1037]176          if (job.State == State.calculating) {
[1126]177            ListViewItem lvi = new ListViewItem(job.Id.ToString(), 0, lvJobCalculating);
[1261]178            jobObjects.Add(job.Id, lvi);
[1483]179
180            //lvJobControl.Items.Add(lvi);
[1495]181
[1126]182            lvi.ToolTipText = (job.Percentage * 100) + "% of job calculated";
[1037]183          } else if (job.State == State.finished) {
[1126]184            ListViewItem lvi = new ListViewItem(job.Id.ToString(), 0, lvJobFinished);
[1261]185            jobObjects.Add(job.Id, lvi);
[1483]186            //lvJobControl.Items.Add(lvi);
[1030]187          } else if (job.State == State.offline) {
[1126]188            ListViewItem lvi = new ListViewItem(job.Id.ToString(), 0, lvJobPending);
[1261]189            jobObjects.Add(job.Id, lvi);
[1483]190            //lvJobControl.Items.Add(lvi);
[1030]191          }
192        } // Jobs
[1483]193        lvJobControl.BeginUpdate();
194        foreach (ListViewItem lvi in jobObjects.Values) {
195          lvJobControl.Items.Add(lvi);
196        }
[1037]197        lvJobControl.Groups.Add(lvJobCalculating);
[1030]198        lvJobControl.Groups.Add(lvJobFinished);
[1037]199        lvJobControl.Groups.Add(lvJobPending);
[1483]200        lvJobControl.EndUpdate();
[1136]201        if (flagJob) {
202          JobClicked();
203        }
[1030]204      }
205      catch (Exception ex) {
206        closeFormEvent(true, true);
207        this.Close();
208      }
[1018]209    }
210
[1136]211    /// <summary>
212    /// if one client is clicked, a panel is opened with the details
213    /// </summary>
214    private void ClientClicked() {
[1148]215      int i = 0;
[1495]216      while (clientInfo.List[i].Id.ToString() != nameCurrentClient) {
[1148]217        i++;
218      }
219      currentClient = clientInfo.List[i];
[1136]220      pbClientControl.Image = ilClientControl.Images[0];
221      lblClientName.Text = currentClient.Name;
222      lblLogin.Text = currentClient.Login.ToString();
[1164]223      lblState.Text = currentClient.State.ToString();
[1136]224    }
225
226    /// <summary>
227    /// if one job is clicked, a panel is opened with the details
228    /// </summary>
229    private void JobClicked() {
[1148]230      int i = 0;
231      while (jobs.List[i].Id.ToString() != nameCurrentJob) {
232        i++;
233      }
234      lvSnapshots.Enabled = false;
235      currentJob = jobs.List[i];
[1136]236      pbJobControl.Image = ilJobControl.Images[0];
237      lblJobName.Text = currentJob.Id.ToString();
238      progressJob.Value = (int)(currentJob.Percentage * 100);
239      lblProgress.Text = (int)(currentJob.Percentage * 100) + "% calculated";
[1495]240      lblUserCreatedJob.Text = currentJob.UserId.ToString() + /* currentJob.User.Name + */ " created Job";
[1173]241      lblJobCreated.Text = "Created at " + currentJob.DateCreated;
[1136]242      if (currentJob.ParentJob != null) {
243        lblParentJob.Text = currentJob.ParentJob.Id + " is parent job";
[1495]244      } else {
245        lblParentJob.Text = "";
[1136]246      }
[1173]247      lblPriorityJob.Text = "Priority of job is " + currentJob.Priority;
[1136]248      if (currentJob.Client != null) {
249        lblClientCalculating.Text = currentJob.Client.Name + " calculated Job";
[1495]250        lblJobCalculationBegin.Text = "Startet calculation at " + currentJob.DateCalculated;
251
[1173]252        if (currentJob.State == State.finished) {
253          IJobManager jobManager =
254            ServiceLocator.GetJobManager();
[1509]255          ResponseObject<JobResult> jobRes = jobManager.GetLastJobResultOf(currentJob.Id, false);
[1173]256          lblJobCalculationEnd.Text = "Calculation ended at " + jobRes.Obj.DateFinished;
257        }
[1495]258      } else {
259        lblClientCalculating.Text = "";
260        lblJobCalculationBegin.Text = "";
261        lblJobCalculationEnd.Text = "";
262      }
[1173]263      if (currentJob.State != State.offline) {
[1148]264        lvSnapshots.Enabled = true;
[1495]265      } else {
266        lvSnapshots.Enabled = false;
[1148]267      }
[1136]268    }
269
[1483]270    private void Refresh() {
271      foreach (Changes change in changes) {
272        if (change.Types == Type.Job) {
273          RefreshJob(change);
274        } else if (change.Types == Type.Client) {
275          RefreshClient(change);
276        } else if (change.Types == Type.ClientGroup) {
277          RefreshClientGroup(change);
278        }
279      }
280    }
281
282    private void RefreshJob(Changes change) {
283      if (change.ChangeType == Change.Update) {
284        for (int i = 0; i < lvJobControl.Items.Count; i++) {
285          if (lvJobControl.Items[i].Text == change.ID.ToString()) {
[1495]286            if (nameCurrentJob == change.ID.ToString()) {
287              JobClicked();
288            }
[1483]289            State state = jobs.List[change.Position].State;
290            System.Diagnostics.Debug.WriteLine(lvJobControl.Items[i].Text.ToString());
291            if (state == State.finished) {
292              lvJobControl.Items[i].Group = jobGroup[1];
293              System.Diagnostics.Debug.WriteLine("finished");
294            } else if (state == State.calculating) {
295              lvJobControl.Items[i].Group = jobGroup[0];
296              System.Diagnostics.Debug.WriteLine("calculating");
297            } else if (state == State.offline) {
298              lvJobControl.Items[i].Group = jobGroup[2];
299              System.Diagnostics.Debug.WriteLine("offline");
300
301            }
302            lvJobControl.Refresh();
303          }
304        }
305      } else if (change.ChangeType == Change.Create) {
306        ListViewItem lvi = new ListViewItem(
307          change.ID.ToString(), 0, jobGroup[2]);
308        jobObjects.Add(change.ID, lvi);
309        lvJobControl.Items.Add(lvi);
310
311      } else if (change.ChangeType == Change.Delete) {
312        jobObjects.Remove(change.ID);
313        for (int i = 0; i < lvJobControl.Items.Count; i++) {
314          if (change.ID.ToString() == lvJobControl.Items[i].Text.ToString()) {
315            lvJobControl.Items[i].Remove();
316            break;
317          }
318        }
319      }
320    }
321
322    private void RefreshClient(Changes change) {
323      if (change.ChangeType == Change.Update) {
324        for (int i = 0; i < lvClientControl.Items.Count; i++) {
325          if (lvClientControl.Items[i].Tag.ToString() == change.ID.ToString()) {
[1495]326            if (nameCurrentClient == change.ID.ToString()) {
327              ClientClicked();
328            }
[1483]329            State state = clientInfo.List[change.Position].State;
330            System.Diagnostics.Debug.WriteLine(lvClientControl.Items[i].Text.ToString());
331            if ((state == State.offline) || (state == State.nullState)) {
332              lvClientControl.Items[i].ImageIndex = 3;
333            } else {
334              lvClientControl.Items[i].ImageIndex = 1;
335            }
336            lvClientControl.Refresh();
337          }
338        }
339
340
341      } else if (change.ChangeType == Change.Create) {
[1495]342
[1483]343      } else if (change.ChangeType == Change.Delete) {
344        clientInfoObjects.Remove(change.ID);
345        for (int i = 0; i < lvClientControl.Items.Count; i++) {
346          if (change.ID.ToString() == lvClientControl.Items[i].Text.ToString()) {
347            lvClientControl.Items[i].Remove();
348            break;
349          }
350        }
351
352      }
353    }
354
355    private void RefreshClientGroup(Changes change) {
356
357    }
358
[1148]359    #region Eventhandlers
[1136]360    /// <summary>
[794]361    /// Send event to Login-GUI when closing
362    /// </summary>
363    /// <param name="sender"></param>
364    /// <param name="e"></param>
[1037]365    private void Close_Click(object sender, EventArgs e) {
[794]366      if (closeFormEvent != null) {
[1030]367        closeFormEvent(true, false);
[794]368      }
369      this.Close();
370    }
371
372    /// <summary>
373    /// Send evnt to Login-GUI when closing
374    /// </summary>
375    /// <param name="sender"></param>
376    /// <param name="e"></param>
377    private void HiveServerConsoleInformation_FormClosing(object sender, FormClosingEventArgs e) {
378      if (closeFormEvent != null) {
[1030]379        closeFormEvent(true, false);
[794]380      }
381    }
[956]382
[1136]383    private void AddJob_Click(object sender, EventArgs e) {
[1018]384      AddJobForm newForm = new AddJobForm();
[956]385      newForm.Show();
[1483]386      //newForm.addJobEvent += new addDelegate(updaterWoker.RunWorkerAsync);
[956]387    }
388
[1098]389    private void OnLVClientClicked(object sender, EventArgs e) {
[1495]390      nameCurrentClient = lvClientControl.SelectedItems[0].Tag.ToString();
[1136]391      flagClient = true;
[1098]392      ClientClicked();
393    }
394
[1136]395    private void OnLVJobControlClicked(object sender, EventArgs e) {
[1148]396      nameCurrentJob = lvJobControl.SelectedItems[0].Text;
[1136]397      flagJob = true;
398      JobClicked();
[1098]399    }
400
[1126]401    private void lvJobControl_MouseMove(object sender, MouseEventArgs e) {
402      if ((lvJobControl.GetItemAt(e.X, e.Y) != null) &&
403        (lvJobControl.GetItemAt(e.X, e.Y).ToolTipText != null)) {
404        tt.SetToolTip(lvJobControl, lvJobControl.GetItemAt(e.X, e.Y).ToolTipText);
405      }
406    }
[1261]407
408    private void updaterWoker_DoWork(object sender, DoWorkEventArgs e) {
[1483]409
[1495]410      changes.Clear();
[1261]411      IClientManager clientManager =
412          ServiceLocator.GetClientManager();
[1495]413
[1483]414      #region ClientInfo
415      ResponseList<ClientInfo> clientInfoOld = clientInfo;
[1261]416      clientInfo = clientManager.GetAllClients();
[1483]417
418      IDictionary<int, ClientInfo> clientInfoOldHelp;
419
420      CloneList(clientInfoOld, out clientInfoOldHelp);
421
422      GetDelta(clientInfoOld.List, clientInfoOldHelp);
423      #endregion
424
425      #region Clients
426      ResponseList<ClientGroup> clientsOld = clients;
[1495]427
[1483]428      clients = clientManager.GetAllClientGroups();
429
430      IDictionary<int, ClientGroup> clientsOldHelp;
431
432      CloneList(clientsOld, out clientsOldHelp);
433
434      GetDelta(clientsOld.List, clientsOldHelp);
435      #endregion
436
437      #region Job
438      ResponseList<Job> jobsOld = jobs;
439      IJobManager jobManager =
440          ServiceLocator.GetJobManager();
441
442      jobs = jobManager.GetAllJobs();
443
444      IDictionary<int, Job> jobsOldHelp;
445      CloneList(jobsOld, out jobsOldHelp);
446
447      GetDelta(jobsOld.List, jobsOldHelp);
448
449      #endregion
450
451      foreach (Changes change in changes) {
452        System.Diagnostics.Debug.WriteLine(change.ID + " " + change.ChangeType);
453      }
454
455    }
456    #endregion
457
458    #region Helper methods
459
460    private void CloneList(ResponseList<Job> oldList, out IDictionary<int, Job> newList) {
461      newList = new Dictionary<int, Job>();
462      for (int i = 0; i < oldList.List.Count; i++) {
463        newList.Add(i, oldList.List[i]);
464      }
465    }
466
467    private void CloneList(ResponseList<ClientInfo> oldList, out IDictionary<int, ClientInfo> newList) {
468      newList = new Dictionary<int, ClientInfo>();
[1495]469      for (int i = 0; i < oldList.List.Count; i++) {
[1483]470        newList.Add(i, oldList.List[i]);
471      }
472    }
473
474    private void CloneList(ResponseList<ClientGroup> oldList, out IDictionary<int, ClientGroup> newList) {
475      newList = new Dictionary<int, ClientGroup>();
476      for (int i = 0; i < oldList.List.Count; i++) {
477        newList.Add(i, oldList.List[i]);
478      }
479    }
480
481    private bool IsEqual(ClientInfo ci1, ClientInfo ci2) {
482      if (ci2 == null) {
483        return false;
484      }
485      if (ci1.Id.Equals(ci2.Id)) {
486        return true;
487      } else return false;
488    }
489
490    private void GetDelta(IList<ClientInfo> oldClient, IDictionary<int, ClientInfo> helpClients) {
491      bool found = false;
492
[1495]493      for (int i = 0; i < clientInfo.List.Count; i++) {
[1483]494        ClientInfo ci = clientInfo.List[i];
495        for (int j = 0; j < oldClient.Count; j++) {
496          ClientInfo cio = oldClient[j];
497          if (ci.Id.Equals(cio.Id)) {
498            found = true;
499            if (ci.State != cio.State) {
500              changes.Add(new Changes { Types = Type.Client, ID = ci.Id, ChangeType = Change.Update, Position = i });
501            }
502            int removeAt = -1;
503            foreach (KeyValuePair<int, ClientInfo> kvp in helpClients) {
504              if (cio.Id.Equals(kvp.Value.Id)) {
505                removeAt = kvp.Key;
506                break;
507              }
508            }
509            if (removeAt >= 0) {
510              helpClients.Remove(removeAt);
511            }
512            break;
513          }
[1261]514        }
[1483]515        if (found == false) {
516          changes.Add(new Changes { Types = Type.Client, ID = ci.Id, ChangeType = Change.Create });
517        }
518        found = false;
[1261]519      }
[1483]520      foreach (KeyValuePair<int, ClientInfo> kvp in helpClients) {
521        changes.Add(new Changes { Types = Type.Client, ID = kvp.Value.Id, ChangeType = Change.Delete, Position = kvp.Key });
522      }
523
[1261]524    }
[1483]525
526    private void GetDelta(IList<ClientGroup> oldClient, IDictionary<int, ClientGroup> helpClients) {
527
528      bool found = false;
529      for (int i = 0; i < clients.List.Count; i++) {
530        ClientGroup cg = clients.List[i];
531        for (int j = 0; j < oldClient.Count; i++) {
532          ClientGroup cgo = oldClient[j];
533          if (cg.Id.Equals(cgo.Id)) {
534            found = true;
535            foreach (Resource resource in cg.Resources) {
536              foreach (Resource resourceold in cgo.Resources) {
537                if (resource.Id.Equals(resourceold.Id)) {
538                  if (resourceold.Name != resource.Name) {
539                    changes.Add(new Changes { Types = Type.Client, ID = cg.Id, ChangeType = Change.Update, Position = i });
540                  }
541                }
542              }
543            }
544            for (int k = 0; k < helpClients.Count; k++) {
545              if (cgo.Id.Equals(helpClients[k].Id)) {
546                helpClients.Remove(k);
547                break;
548              }
549            }
550            break;
551          }
552        }
553        if (found == false) {
554          changes.Add(new Changes { Types = Type.ClientGroup, ID = cg.Id, ChangeType = Change.Create });
555        }
556        found = false;
557      }
558      foreach (KeyValuePair<int, ClientGroup> kvp in helpClients) {
559        changes.Add(new Changes { Types = Type.ClientGroup, ID = kvp.Value.Id, ChangeType = Change.Delete, Position = kvp.Key });
560      }
561    }
562
563    private void GetDelta(IList<Job> oldJobs, IDictionary<int, Job> helpJobs) {
564      bool found = false;
[1495]565      for (int i = 0; i < jobs.List.Count; i++) {
[1483]566        Job job = jobs.List[i];
567        for (int j = 0; j < oldJobs.Count; j++) {
568
569          Job jobold = oldJobs[j];
570
571          if (job.Id.Equals(jobold.Id)) {
572
573            found = true;
574            if (job.State != State.offline) {
575              if (!IsEqual(job.Client, jobold.Client)) {
576                changes.Add(new Changes { Types = Type.Job, ID = job.Id, ChangeType = Change.Update, Position = i });
577              } else if (job.State != jobold.State) {
578                changes.Add(new Changes { Types = Type.Job, ID = job.Id, ChangeType = Change.Update, Position = i });
579              }
580            } else if (job.DateCalculated != jobold.DateCalculated) {
581              changes.Add(new Changes { Types = Type.Job, ID = job.Id, ChangeType = Change.Update, Position = i });
582            }
583
584            int removeAt = -1;
585            foreach (KeyValuePair<int, Job> kvp in helpJobs) {
586              if (job.Id.Equals(kvp.Value.Id)) {
587                removeAt = kvp.Key;
588                break;
589              }
590            }
591            if (removeAt >= 0) {
592              helpJobs.Remove(removeAt);
593            }
594            break;
595          }
596
597        }
598        if (found == false) {
599          changes.Add(new Changes { Types = Type.Job, ID = job.Id, ChangeType = Change.Create });
600          System.Diagnostics.Debug.WriteLine("new Job: " + job.Id);
601        }
602        found = false;
603      }
604      foreach (KeyValuePair<int, Job> kvp in helpJobs) {
605        changes.Add(new Changes { Types = Type.Job, ID = kvp.Value.Id, ChangeType = Change.Delete, Position = kvp.Key });
606        System.Diagnostics.Debug.WriteLine("delete Job: " + kvp.Value.Id);
607      }
608    }
609
610
[1136]611    #endregion
[1483]612
613    private void updaterWoker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
614      Refresh();
615    }
616
617
[794]618  }
[844]619}
Note: See TracBrowser for help on using the repository browser.