?
Solved

c# - show progress bar in winform app

Posted on 2012-12-31
10
Medium Priority
?
6,098 Views
Last Modified: 2013-01-03
Hello experts,

I have a winform app that reads in txt files and loads the contents to a datagrid.  This process could take 1 to 30+ seconds depending on size of file.  When large files are used the entire winform UI gets locked up.  I guess I need to move the process to another thread and display a progress bar.   I have been reading about the System.Threading and backgroundworker class however, most of those examples online have a for loop with a counter for demostations.  Looking for a real example?  

Here is sample code...looking for help to add in threading
 //open file button click event
private void btnOpenFile_Click(object sender, EventArgs e)
{
    Cursor.Current = Cursors.WaitCursor;
     ....
    //show dialog, load xml to grid
    OpenFile(true, true, false);
    ....
    Cursor.Current = Cursors.Default;
}        
//
private void OpenFile(bool openDialog, bool loadGrid, bool selectXls)
{
   ...
   //method that could take up to 30 seconds to complete
   //where should thread go and how to update progress bar
   LoadDataToGrid(fileName);
}
//get data from file and then bind to gridview
private void LoadDataToGrid(string fileName)
{
    ...
    myDataTable = new DataTable();
    //this method take the most time
    myDataTable = myObj.PopulateDataTable(fileName, myDataTable);
    ...
    //bind to datagrid view
    //this line can take 1-5 seconds when the PopulateDataTable method takes a long time
    dgv.DataSource = myDataTable;

    // Initialize basic DataGridView properties.
    InitializeDataGridView();
    ...
}

Open in new window


PopulateDataTable is method in a seperate class file \ dll
 Where should the threading be handled and how does the progress bar get updated if processing is handled in seperate dll \ class file?

Thanks in advance and happy new year!
0
Comment
Question by:j420exe1
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
  • 2
  • +1
10 Comments
 
LVL 44

Assisted Solution

by:AndyAinscow
AndyAinscow earned 400 total points
ID: 38733153
Yes, you do need threading to stop the UI getting locked.

>>most of those examples online have a for loop with a counter for demostations.
What you have is a 'real' example.  If your file contains 1000 lines then every 10 lines is 1% which you can display on the progress bar
0
 
LVL 40
ID: 38733692
A ProgressBar does not increment automatically. You need to send it a value everytime it needs to increment. This is why most examples use a For loop. The programmer knows how many loops he will do, an can thus feed the ProgressBar to advance it to the proper percentage of the job done.

If you want to update a ProgressBar in your situation, it would need to be done in PopulateDataTable. And not seeing the code, there is no way to even know if it would be possible, depending on the technique you use to populate the table. If it is through a DataAdapter and a Fill method for instance, you can't. Fill is a synchronous operation. Your code is not running while the method does its job. And even it if was, you would need to know the number of rows returned in order to set the ProgressBar properly, and you do not know that until the long job is completed. So you would not be able to advance the ProgressBar properly. If the DataTable is filled in a loop however, row by row, and if you have access to the code of PopulateDataTable, then something might be worked out.

Same thing holds true for the line where you set the DataSource, but for this one, there is no way to access the code and make it work with a ProgressBar. So no ProgressBar while you are filling a DataGrid through DataBinding.

Using a thread (or if you are in Visual Studio 2012, an Async methode which can be easier to program for somebody who is not used to threading) could make the UI responsive, but would not enable you to advance a progress bar.

The only solution you could have is one that we see too often. A Timer that you start just before doing the operation and that you use to increment the ProgressBar. But since you have no way to know beforehand how long the operation will take, you would have no way to give a proper value to the control, and you would end up with one of those stupid and useless bars. You probably have seen those that takes a minute to fill to 25% and suddenly jumps to 100%. Or bars that fill completely and then hang up for a very long time after they are filled up.

So, you have basically 2 solutions in my opinion.

A "Retrieving data. One moment please..." message displayed in a prominent position on the screen. You might want to change the cursor to a Wait cursor while this is displayed, to give an indication to the user that the thing is working. That is the most common use of the Wait cursor by the way: showing that something is happening when there is no way of controlling a Progress Bar.

Or go the multithread route, which would leave the UI available for the user to do something else while the long process runs, and react to the event that triggers when the thread has returned to inform the user with something like "The operation we launched a few minutes ago has terminated. Do you want to see the result?"
0
 
LVL 13

Accepted Solution

by:
Naman Goel earned 1600 total points
ID: 38734447
One way to achieve this is by using delegate.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private Button button1;
        private ProgressBar progressBar1;

        private delegate void ReadFileDelegate(string path);
    
        public Form1()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.progressBar1 = new System.Windows.Forms.ProgressBar();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(104, 190);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // progressBar1
            // 
            this.progressBar1.Location = new System.Drawing.Point(44, 47);
            this.progressBar1.Maximum = 500;
            this.progressBar1.Name = "progressBar1";
            this.progressBar1.Size = new System.Drawing.Size(208, 23);
            this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
            this.progressBar1.TabIndex = 1;
            // 
            // Form1
            // 
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.progressBar1);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.ResumeLayout(false);

        }

        private void button1_Click(object sender, EventArgs e)
        {
            progressBar1.Style = ProgressBarStyle.Marquee;
            new ReadFileDelegate(ReadFile).BeginInvoke("c:\\abc.txt", null, null);
        }

        private void WorkDone()
        {
            progressBar1.Style = ProgressBarStyle.Continuous;
            progressBar1.Value = 500;
        }
    

        private void ReadFile(string path)
        {
            using (StreamReader sr = new StreamReader(path))
            {
                sr.ReadToEnd();
            }

            this.Invoke(new MethodInvoker(WorkDone));
        }
    }
}

Open in new window


The only thing you need to do here is separate UI with reader logic for this is you can call  dgv.DataSource = myDataTable; and InitializeDataGridView() method in WorkDone method .
0
Prepare for your VMware VCP6-DCV exam.

Josh Coen and Jason Langer have prepared the latest edition of VCP study guide. Both authors have been working in the IT field for more than a decade, and both hold VMware certifications. This 163-page guide covers all 10 of the exam blueprint sections.

 

Author Comment

by:j420exe1
ID: 38736670
OK...I am removing the progress bar UI requirement.  

Educating myself on delegates vs backgroundworkers.  

@naman_goel:  in your code example...would your ReadFile function be my LoadDataToGrid function?
0
 
LVL 13

Expert Comment

by:Naman Goel
ID: 38736680
yes...
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 38736695
>>OK...I am removing the progress bar UI requirement.  

Why?  It is trivial to implement.  You say you already have sample code.  All you need to do (as I said initially) is replace the dummy increment by the real one you have when you loop through the file contents to load the grid.
0
 
LVL 13

Expert Comment

by:Naman Goel
ID: 38736727
Yeah you can implement Progress increment code too... but it's better to use progress bar style as marquee if you are not having much control on file reading logic.
0
 

Author Comment

by:j420exe1
ID: 38736853
I am running .net 3.5 (only for a few more months til we upgrade)  

I was trying to follow the delegate example above however, system.threading.tasks is not available to me.  What is available for 3.5?
0
 
LVL 13

Assisted Solution

by:Naman Goel
Naman Goel earned 1600 total points
ID: 38736901
you can use backgroundworker also
it's similar to delegate code
0
 

Author Closing Comment

by:j420exe1
ID: 38740328
backgroundworker with a progress bar based on file size...thanks!
0

Featured Post

Setting up LaraDock for Laravel

Learn how to set up LaraDock in a Laravel project - LaraDock gives us an easy way to run a Laravel application using Docker in a single command.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Many of us here at EE write code. Many of us write exceptional code; just as many of us write exception-prone code. As we all should know, exceptions are a mechanism for handling errors which are typically out of our control. From database errors, t…
This article aims to explain the working of CircularLogArchiver. This tool was designed to solve the buildup of log file in cases where systems do not support circular logging or where circular logging is not enabled
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
Suggested Courses

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question