5 Replies - 1494 Views - Last Post: 11 February 2012 - 03:21 AM Rate Topic: -----

#1 scolty  Icon User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 259
  • Joined: 27-April 11

Progressbar only updating on second call.

Posted 10 February 2012 - 09:06 AM

Hey guys,
Im having some serious problems with this progressbar (nearly 10hrs now) and im hoping one of you smart guys will be able to point out where i have screwed up.

The issue im having is as follows: The 2nd code block has the following loop
//Begin checking the DataTypes of each row element.
                    while (rowInfo.currentRow <= final && rowInfo.currentRow >= initial && reader.Peek() > -1)


this is where i itteraate through each element in a row. Before going to the next row, the following line is called.
//Call event to update progressbar.
                        IncUpdateProgressBar(this, rowInfo);
                        rowInfo.currentRow++;


which is suppose to call the last method in that code block. Once called, it then fire an event. This event then invokes the incrementPB(object sender, IncrementValue e) method which should increment the progress bar on the form.

HERES THE ISSUE: On the first pass through this method, the value gets updated, but the green bar doesnt move. On the second pass through this method (ie finished reading the second line of the file) the progressbar value = correct value YET the green bar has only moved to the 1st position (ie where it should have been after the first pass.

when everything finishes, the progressbar jumps to 100%.

If anyone has any ideas as to why this is happening and where i have screwed up i would really appreciate hearing them cause this is driving me nuts.

Form code, 2 separate classes.
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 Frazer.CSV.Check;
using Frazer.CSV.LoadDataTable;
using Frazer.CSV.FilePath;
using Frazer.CSV.Passer;

namespace CSV_Parser
{
    public partial class CSVAppForm : Form
    {
        public CSVAppForm()
        {
            InitializeComponent();
        }

        //Browse button clicked event.
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            LoadNewFile();
        }

        //Method to update log TextBox.
        public void Logger(string input)
        {
            ....
        }

        //Allows the user to specify a file from a browse window.
        private void LoadNewFile()
        {
            ....
        }

        //Property for csv file path.
        public string filePath
        {
            ....
        }

        //Property for number of columns in data file.
        public int numberOfColumns
        {
            ....
        }

        //Updates number of TextBoxes and ComboBoxes visible to the user.
        private void numColCount_ValueChanged(object sender, EventArgs e)
        {
            ....   
        }

        //Declaration of enum for the column data types.
        public enum columnDataTypes { DateTime, Decimal, Double, Float, Int };

        //Creates an enum array of the data types secified by the user on the "Selection" tab.
        public int[] DataTypeEnum(string[] columnDataNames)
        {
            ....
        }

        //Verifies input data on "Selection" tab and then check the file.
        private void btnCheckData_Click(object sender, EventArgs e)
        {
            string[] columnDataTypes = new string[numberOfColumns];

            //Confirm whether all visible data type ComboBoxes contained a data type. 
            if (ColumnDataTypes(ref columnDataTypes)) 
            {
                //Update log.
                ....
            }
            else
            {
                ....
                return;
            }

            //Declare an array which contains the same number of elements as columns specified by the user.
            string[] headerNames = new string[numberOfColumns];
            
            //ComboBox and TextBoxes contain information.
            if (HeaderNames(ref headerNames))
            {
                //Update log.
                ....
            }
            else
            {
                //Refocus on the "Selection" tab and display an error message.
                ....
                return;
            }



            //Class library object.
            Check checkFilePath = new Check();


            //Check file path ends with correct extension.
            if (checkFilePath.CorrectExtension(filePath)) { }
            else
            {
                ....
                return;
            }


            //Check file exists.
            if (checkFilePath.FileExists(filePath)) { }
            else
            {
                ....
                return;
            }


            //Check user has access to the stated file.
            try
            {
                bool accessToFile = checkFilePath.AccessToFile(filePath);
            }
            catch (Exception AccessException)
            {
                ....
                return;
            }
            
            //Obtain enum array of data types.
            int[] columnIntegerArray = DataTypeEnum(columnDataTypes); 

//Where objects for the progressbar are created.

            //Construct a verification object in preparation for file verification.
            Verification preDataTypeChecking = new Verification();
            ProgressBarChanged progBarChanged = new ProgressBarChanged(this.progBarVerifyData);
            progBarChanged.subscribeToEvent(preDataTypeChecking);

            try
            {
                if (preDataTypeChecking.CheckDataTypes(columnIntegerArray, (int)numInitialRow.Value, (int)numFinalRow.Value, filePath, txtErrorLogFilePath.Text, numberOfColumns) == true)
                {
                    MessageBox.Show("IT WORKED");
                }
                else
                {
                    MessageBox.Show("It Failed");
                }
            }
            catch (Exception ex)
            {
                ....
            }

        }

        //Creates a string array of the column data types and checks each visible ComboBox has been filled.
        public bool ColumnDataTypes(ref string[] columnDataTypes)
        {
            int count = 0;
            int i = 0;

            foreach (Control control in tabPage1.Controls)
            {
                ....
            }
            return true;
        }


        //Itterate through TextBoxes and add to array if they are unique.
        public bool HeaderNames(ref string[] headerNames)
        {
            int counter = 0;
            {
                 ...
            }

            //Return true if the TextBoxes are unique.
            return true;
        }

    }

    public class ProgressBarChanged
    {
        ProgressBar statusBar;

        public ProgressBarChanged(ProgressBar pb)
        {
            statusBar = pb;
            statusBar.Value = 0;
        }

        public void subscribeToEvent(Verification test)
        {
            test.UpDateProgressBar += new EventHandler<IncrementValue>(incrementPB);
        }
        
//This Method is not updating the progressbar correctly.
        public void incrementPB(object sender, IncrementValue e)
        {  
            int x = (e.currentRow);
            double x2 = double.Parse(e.currentRow.ToString());
            double y = x2/2;


            double percentage = y * 100;

            statusBar.Value = int.Parse(Math.Truncate(percentage).ToString());
            statusBar.Refresh();
        }
    }
}




Class in class library.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Configuration;
using System.Globalization;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using Frazer.CSV.Passer;

namespace Frazer.CSV.Check
{
    public class Verification
    {
        //Declare event which will be used to update the progress bar.
        public event EventHandler<IncrementValue> UpDateProgressBar;

        public bool CheckDataTypes(int[] columnDataType, int initial, int final, string filePath, string errorLogFilePath, int numberOfColumns)
        {
            IncrementValue rowInfo = new IncrementValue();

            
            rowInfo.currentRow  = 1; 
            int noOfErrors = 0;
            string line = null;
            string[] data = null;
            bool testParse = false;

            //Declaring out parameters for tryparse() method. No specific use, just there to stop errors.
            DateTime dateTimeResult;
            Decimal decimalResult;
            Double doubleResult;
            float floatResult;
            int intResult;


            //Parameters for DateTime tryparse.
            CultureInfo culture = CultureInfo.CreateSpecificCulture("en-GB");
            DateTimeStyles styles = DateTimeStyles.None;

            //StringBuilder for writting any errors to the error log specified on the "Properties" tab.
            StringBuilder errorLogBuilder = new StringBuilder();

            //noOfErrors is a place holder for the number of actual errors found.
            String errorLogHeader = string.Format("Error log file: {0}. {1}{1}This error log contains: noOfErrors. {2}", filePath, Environment.NewLine, Environment.NewLine);
            errorLogBuilder.AppendLine(errorLogHeader);


            using (StreamReader reader = File.OpenText(filePath))
            {
                #region
                try
                {
                    //Begin checking the DataTypes of each row element.
                    while (rowInfo.currentRow <= final && rowInfo.currentRow >= initial && reader.Peek() > -1)
                    {
                        //Read new line.
                        line = reader.ReadLine();

                        //Split the current line
                        data = line.Split(',');

                        for (int x = 0; x < data.Length; x++)
                        {
                            switch (columnDataType[x])
                            {
                                ....
                            }
                        }
                        //Call event to update progressbar.
                        IncUpdateProgressBar(this, rowInfo);
                        rowInfo.currentRow++;
                    }
                }
                #endregion

                catch (IndexOutOfRangeException indexEx)
                {
                    ....
                }

                catch (Exception ex)
                {
                    ....
                }
            }
            createLog(noOfErrors, errorLogFilePath, errorLogBuilder);


            //Determine return value, true = no errors.
            if (noOfErrors > 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        


        //Constructs the error messages.
        private void updateLogger(ref StringBuilder errorLogBuilder, int rowCount, int x, ref int noOfErrors)
        {
            ....
        }

        //Construct the final log, no exception thrown.
        private void createLog(int noOfErrors, string errorLogFilePath, StringBuilder errorLogBuilder)
        {
            ....
        }

        //Construct the final log, exception thrown.
        private void createLog(int noOfErrors, string errorLogFilePath, StringBuilder errorLogBuilder, int rowCount, string errorMessage)
        {
            ....
        }

        protected virtual void IncUpdateProgressBar(object sender, IncrementValue e)
        {
            EventHandler<IncrementValue> TempHandler = UpDateProgressBar;

            //Avoid possible race condition.
            if (TempHandler != null)
            {
                TempHandler(this, e);
            }
        }
    }
}




Custom class in class library.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Frazer.CSV.Passer
{
    public class IncrementValue : EventArgs
    {
        private int currentRowNow = 1;
        public int currentRow
        {
            set
            {
                currentRowNow = value;
            }
            get
            {
                return currentRowNow;
            }
        }

        private int maxValueNow;
        public int maxValue
        {
            set
            {
                maxValue = value;
            }
            get
            {
                return maxValueNow;
            }
        }
    } 
}



Is This A Good Question/Topic? 0
  • +

Replies To: Progressbar only updating on second call.

#2 janne_panne  Icon User is offline

  • WinRT Dev
  • member icon

Reputation: 429
  • View blog
  • Posts: 1,047
  • Joined: 09-June 09

Re: Progressbar only updating on second call.

Posted 10 February 2012 - 09:17 AM

Does your application freeze while it is inside the while loop? By the looks of it I'd say your problem is that you are doing everything in the same thread (UI thread). This means that when you are doing some heavy operations your UI won't update and it's not possible to interact with the UI until the operations complete.

So what you want is an asynchronous operation which runs in a separate thread and leaves UI thread "open". In UI thread you update your progress bar's value and in the background thread you do the operations which would otherwise freeze UI.
Was This Post Helpful? 1
  • +
  • -

#3 scolty  Icon User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 259
  • Joined: 27-April 11

Re: Progressbar only updating on second call.

Posted 10 February 2012 - 09:21 AM

Hey, thanks for replying.

No there is no freezing as such. Ive stepped through the program using F10 and the value of the progressbar does change to the correct value but the form doesnt change. Then execution just goes back into the class library again and continues executing as per normal.

I havent looked into threading at all atm. Will this require changing other things in the code or can i just state that that specific method should be run on the separate thread?

Thanks for ur help, i really appreciate it.
Was This Post Helpful? 0
  • +
  • -

#4 scolty  Icon User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 259
  • Joined: 27-April 11

Re: Progressbar only updating on second call.

Posted 10 February 2012 - 09:29 AM

one thing i forgot to add. I had constructed a simpler example, where everyting was in the form file rather than splitting some of the classes into the class library, and i hadnt experienced these problems.
Was This Post Helpful? 0
  • +
  • -

#5 janne_panne  Icon User is offline

  • WinRT Dev
  • member icon

Reputation: 429
  • View blog
  • Posts: 1,047
  • Joined: 09-June 09

Re: Progressbar only updating on second call.

Posted 11 February 2012 - 03:06 AM

I created a small sample and I noticed that it doesn't really matter if you use threads or not when it comes to updating progress bar, it gets updated anyways so I guess they have improved that part.

Could it be that your operation is so fast that it actually doesn't require the progress bar at all? You could try setting System.Threading.Thread.Sleep(1000); inside your loop to sleep for one second before calling IncUpdateProgressBar method. This should show you if the progress bar updates correctly if the operation takes longer time to calculate.

Also check that you correctly set your progress bar's Maximum value.

Here is the sample I created, perhaps it could help you in some way or teach a little bit about threading. If you want to test it, just create a form with progressbar (progressBar1), textbox (txtNumber) and two buttons (btnCount, btnCountWithThreads).
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;

namespace ThreadingSample
{
    public partial class Form1 : Form
    {
        protected Counter Counter { get; set; }

        public Form1()
        {
            InitializeComponent();
            // Initialize counter and progress bar
            this.Counter = new Counter();
            this.Counter.CountIncreased += new EventHandler<CountIncreasedEventArgs>(Counter_CountIncreased);
            progressBar1.Maximum = 10;
        }

        void Counter_CountIncreased(object sender, CountIncreasedEventArgs e)
        {
            // Create a new method which we can invoke to update UI correctly.
            Action doUiStuff = () =>
            {
                txtNumber.Text = e.CurrentCount.ToString();
                progressBar1.Value = e.CurrentCount;
            };
            // If Invoke is Required, we have to call Invoke method to update UI
            // If we just called doUiStuff in normal way without invoking, we would get 
            // an InvalidOperationException with message:
            // "Cross-thread operation not valid: Control 'txtNumber' accessed from a thread 
            // other than the thread it was created on."
            if (this.InvokeRequired)
                this.Invoke(doUiStuff);
            else
                doUiStuff();
        }

        private void btnCount_Click(object sender, EventArgs e)
        {
            // call sync counting
            this.Counter.CountToTen();
            MessageBox.Show("count sync finished");
        }

        private void btnCountWithThreads_Click(object sender, EventArgs e)
        {
            // Create a callback method which is called when CountToTenAsync finishes.
            Action callback = () => MessageBox.Show("count async finished");
            // call async counting
            this.Counter.CountToTenAsync(callback);
        }

        /// <summary>
        /// Clears the form by setting all values to initial values.
        /// </summary>
        private void ClearForm()
        {
            this.txtNumber.Text = "";
            this.progressBar1.Value = 0;
        }
    }

    /// <summary>
    /// Counter class. 
    /// Counts to ten with either async or sync operation
    /// </summary>
    public class Counter
    {
        /// <summary>
        /// Event which is called whenever CountToTen methods' count is increased
        /// </summary>
        public event EventHandler<CountIncreasedEventArgs> CountIncreased;

        private const int TARGET_NUMBER = 10;

        /// <summary>
        /// Raises CountIncreased event
        /// </summary>
        protected void OnCountIncreased(int current)
        {
            if (CountIncreased != null)
                CountIncreased(this, new CountIncreasedEventArgs(current, TARGET_NUMBER));
        }

        /// <summary>
        /// Counts to ten using asynchronous operation (does the operation in a new thread)
        /// </summary>
        /// <param name="countFinishedCallback">Callback which is called when counting finishes</param>
        public void CountToTenAsync(Action countFinishedCallback)
        {
            // Create a new thread which counts to ten.
            // Now the counting is running in separate thread than UI thread.
            System.Threading.Thread countThread = new System.Threading.Thread(() =>
            {
                this.CountToTen();
                if (countFinishedCallback != null)
                    countFinishedCallback();
            });
            // start the thread to do the actual counting.
            countThread.Start();
        }

        /// <summary>
        /// Counts to ten using synchronous operation
        /// </summary>
        public void CountToTen()
        {
            for (int number = 1; number <= TARGET_NUMBER; number++) {
                this.OnCountIncreased(number);
                System.Threading.Thread.Sleep(100);
            }
        }
    }

    /// <summary>
    /// EventArgs class for CountIncreased event
    /// </summary>
    public class CountIncreasedEventArgs : EventArgs
    {
        public CountIncreasedEventArgs(int current, int target)
        {
            this.CurrentCount = current;
            this.TargetCount = target;
        }

        /// <summary>
        /// Current count
        /// </summary>
        public int CurrentCount { get; set; }

        /// <summary>
        /// Number we are counting to
        /// </summary>
        public int TargetCount { get; set; }
    }
}



edit: In the sample you can see that there are some issues with updating: TextBox updates immediately while ProgressBar updates after about half a second.

I found this thread which discusses the "Slow Poke" progress bar of Windows 7:
http://social.msdn.m...c1-37531ee2782f

This post has been edited by janne_panne: 11 February 2012 - 03:13 AM

Was This Post Helpful? 1
  • +
  • -

#6 scolty  Icon User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 259
  • Joined: 27-April 11

Re: Progressbar only updating on second call.

Posted 11 February 2012 - 03:21 AM

Thanks very much, im in the process of checking out Jasons tutorial on multi threading and one or two of the books i have about it. Hopefully this will be sorted today.

Yes you are correct, the file reading will be very quick so maybe when i run it normally that will cause an issue(i will see about limiting the calls to the update progress bar method) thanks for the tip. I dont understand how f10 isnt allowing enough time for it to draw though, i cant press it very quickly.

Thanks again for your help.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1