thread progressBar in WinForms – Also in C#

Después de una interesante conversación entorno a las diferencias de lenguaje “entre VB i C#” con mi amigo Jose María y de frente a la reiterativa discusión sobre la sintaxis… aquí os dejo el último ejemplo en C# para que lo comparéis con su anterior en VB.

Realmente pensáis que hay tanta diferencia como para continuar discutiendo cual mejor?, para poder apreciar los matices de cada uno de los lenguajes, como mínimo uno tiene que ser “bilingüe” y practicante pues en otro caso la discusión esta fuera de criterio…

El debate está servido! Jajajaja!. 

 

using System;
using System.Windows.Forms;
using System.Threading;
 
namespace ThreadProgressBar
{
    public partial class frmMain : Form
    {
        System.Timers.Timer tmr = new System.Timers.Timer(1000);
        delegate void myDeleg(Form toClose);
        Form currentPrgBar = null;
        Boolean createPrgBar = false;
        Boolean disposePrgBar = false;
 
        public frmMain()
        {
            InitializeComponent();
        }
 
        /// When form load
        private void frmMain_Load(object sender, EventArgs e)
        {
            tmr.Elapsed += tmr_Elapsed;
            tmr.Interval = 100;
            tmr.Start();
        }
 
        /// When running button is presed
        private void simulateLoad(object sender, EventArgs e)
        {
            createPrgBar = true;
            btnRunning.Text = "Load in progress";
            for (int i = 0; i < 10000; i++)
            {
                btnRunning.Text = "Running loop :" + i.ToString();
                btnRunning.Refresh();
                Application.DoEvents();
            }
            btnRunning.Text = "Run New Loop";
            disposePrgBar = true;
        }
 
        /// watch if any process are running to create a new progress bar
        void tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (createPrgBar & (currentPrgBar == null))
            {
                Thread myThread = new Thread(onFlyProgressBar);
                myThread.Start();
                createPrgBar = false;
            }
            else
            {
                if (disposePrgBar)
                {
                    closeForm(currentPrgBar);
                    disposePrgBar = false;
                }
            }
        }
 
        /// Create new 'processing' progressBar
        void onFlyProgressBar()
        {
            Label lblMessage = new Label 
            {
                Dock = DockStyle.Top,
                Text = "Procesing, please wait..",
                TextAlign = System.Drawing.ContentAlignment.MiddleCenter,
                Font = new System.Drawing.Font("Microsoft Sans Serif"14,
                                                System.Drawing.FontStyle.Bold,
                                                System.Drawing.GraphicsUnit.Point)                                                
            };
 
            ProgressBar prgBar = new ProgressBar
            {
                Height = 15,
                Dock = DockStyle.Bottom,
                Style = ProgressBarStyle.Marquee
            };
 
            currentPrgBar = new Form()
            {         
                Width = 300, Height = 50,
                StartPosition = FormStartPosition.CenterScreen,
                ControlBox = false,
                FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
            };
            currentPrgBar.Controls.AddRange(new Control[] { lblMessage, prgBar });
            currentPrgBar.LostFocus += recoverFocus;
            Application.Run(currentPrgBar);
        }
 
        void recoverFocus(object sender, EventArgs e)
        {
            currentPrgBar.TopMost = true;
        }
 
        /// Make current prgBar topmost if lost focus
        void closeForm(Form toClose)
        {
            if (toClose == nullreturn;
            if (toClose.InvokeRequired)
            {
                toClose.Invoke(new myDeleg(closeForm), toClose);
            }
            else
            {
                toClose.Close();
                currentPrgBar = null;
            }
        }
    }
}


Que tengáis una excelente semana!
PepLluis,

thread progressBar in WinForms

En el último post y en “WinForms”, comentamos como en algunas circunstancias se nos produce una excepción de ‘Subproceso Anulado’ cuando creamos/destruimos hilos en una misma función.

Después de conversar sobre el tema, y hablando con uno de vosotros sobre un escenario donde en ‘WinForm’ clásico nunca ha sido capaz de visualizar una ‘progress bar’ independientemente del form principal y aun pensando que deben existir montón de ejemplos J me he decidido a juguetear un ratito para ver como lo haría yo…

Este es el resultado (no sé si muy bueno, pero funciona.)

De todas maneras se me ocurre, que en próximos post podríamos hablar de cómo hacerlo usando “async”  “await” y “task”. Que os parece?

Si lo consideráis interesante no dudéis pedirme el equivalente en C#.

In the last post we are discussing around «WinForms» and some exceptions when create / destroy threads inside the same function.

After talking more about this, and share with somebody of you who are using classic ‘WinForm’ and has never been able to display a ‘progress bar’ independently of the main form, I think that we can found a plenty of examples on the web, But I decided tinkering for a while to see how I’d do it …

This is the result (not sure if is a very good sample, but it works.)

Anyway, it occurs to me that in next post could talk about how to do it using «async» «await» and «task». What you think?

Do not hesitate to ask me for the equivalent code in C #.

Espero vuestros comentarios!

Imports System.Threading
Public Class Main
 
    Private WithEvents tmr As New System.Timers.Timer(1000)
    Private currentPrgBar As Form
    Private createPrgBar As Boolean = False
    Private disposePrgBar As Boolean = False
 
    Delegate Sub myDeleg(toClose As Form)
 
    ''' When running button is presed
    Private Sub simulateLoad(sender As Object, e As EventArgsHandles btnRunning.Click
        createPrgBar = True
        btnRunning.Text = "Load in progress"
        For index = 1 To 10000
            btnRunning.Text = "Running loop :" + index.ToString
            btnRunning.Refresh()
            Application.DoEvents()
        Next
        btnRunning.Text = "Run New loop"
        disposePrgBar = True
    End Sub
 
    ''' watch if any process are running to create a new progress bar
    Private Sub tmr_Tick(sender As Object, e As EventArgsHandles tmr.Elapsed
        If createPrgBar And (currentPrgBar Is NothingThen
            Dim myThread As New Thread(AddressOf onFlyStatusBarForm)
            myThread.Start()
            createPrgBar = False
        Else
            If disposePrgBar Then
                closeForm(currentPrgBar)
                disposePrgBar = False
            End If
        End If
    End Sub
    ''' When form load
    Private Sub Main_Load(sender As Object, e As EventArgsHandles MyBase.Load
        tmr.Interval = 100
        tmr.Start()
    End Sub
    ''' When main form closes
    Private Sub Main_FormClosing(sender As Object, e As FormClosingEventArgsHandles MyBase.FormClosing
        closeForm(currentPrgBar)
    End Sub
 
    ''' Create new 'processing' progressBar
    Sub onFlyStatusBarForm()
        Dim lblMessage As New Label With {.Dock = DockStyle.Top,
                                          .Text = "Procesing, please wait..",
                                          .TextAlign = ContentAlignment.MiddleCenter,
                                          .Font = New System.Drawing.Font("Microsoft Sans Serif"14.0!,
                                                                          System.Drawing.FontStyle.Bold,
                                                                          System.Drawing.GraphicsUnit.Point,
                                                                          CType(0Byte))
                                         }
        Dim prgBar As New ProgressBar With {.Height = 15, .Dock = DockStyle.Bottom, .Style = ProgressBarStyle.Marquee}
 
        currentPrgBar = New Form() With {.Width = 300, .Height = 50,
                               .StartPosition = FormStartPosition.CenterScreen,
                               .ControlBox = False,
                               .FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
                              }
 
        currentPrgBar.Controls.AddRange(New Control() {lblMessage, prgBar})
        AddHandler currentPrgBar.LostFocus, AddressOf recoverFocus
        Application.Run(currentPrgBar)
    End Sub
 
    ''' Make current prgBar topmost if lost focus
    Private Sub recoverFocus(sender As Object, e As EventArgs)
        currentPrgBar.TopMost = True
    End Sub
 
    ''' Close current 'progressBar'
    Sub closeForm(toClose As Form)
        If toClose Is Nothing Then Exit Sub
        If toClose.InvokeRequired Then
            toClose.Invoke(New myDeleg(AddressOf closeForm), toClose)
        Else
            toClose.Close()
            currentPrgBar = Nothing
        End If
    End Sub
 
End Class

wpf Snnipeds

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;

using System.Data;
using System.Data.SqlClient;

using System.Windows.Forms;

/// for
/// foreach
/// switch
/// invoke
/// Serial Port
/// Timer

namespace wpfTest1
{
    /// <summary>
    /// Lógica de interacción para MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        static System.IO.Ports.SerialPort my_Serial = new System.IO.Ports.SerialPort();
        DispatcherTimer dispatcherTimer;
        DataGridView dgv = new System.Windows.Forms.DataGridView();
        Label dataRcv;
        Label frameR;
        DataSet ds;
        /// Main Windows
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            initwinFormsParts();
            initSerialPortCombo();
            initSerialPort();
            initGradients();
            initDispatcherTimer();
            cmbSerialPort.SelectionChanged += cmbSerialPort_SelectionChanged;
        }

        #region windForms integration sample
        /// <summary>
        /// windForms integration sample
        /// </summary>
        void initwinFormsParts()
        {
            dataRcv = new System.Windows.Forms.Label();
            dataRcv.ForeColor = System.Drawing.Color.Red;
            dataRcv.Text = «dataRcv»;
            frameR = new System.Windows.Forms.Label();
            frameR.Top = dataRcv.Top + dataRcv.Height + 5;
            frameR.ForeColor = System.Drawing.Color.White;
            frameR.Text = «received data»;
            formsHost.Child = dataRcv;
            formsHost2.Child = frameR;
            formsHost3.Child = dgv;
        }
        #endregion

        #region snippet initialize serialPorts
        /// <summary>
        /// snippet for serialPorts
        /// </summary>
        Boolean serialPortFound = false;
        void initSerialPort()
        {
            try
            {
                my_Serial.PortName = cmbSerialPort.SelectedValue.ToString();
                my_Serial.DataReceived += receiveData;
                my_Serial.Open();
                serialPortFound = true;
            }
            catch (Exception)
            {
                serialPortFound = false;
            }
        }

        void cmbSerialPort_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            serialPortFound = false;
            my_Serial.Close();
            initSerialPort();
        }
        void initSerialPortCombo()
        {
            cmbSerialPort.ItemsSource = System.IO.Ports.SerialPort.GetPortNames();
            cmbSerialPort.SelectedIndex = 0;
        }
        #endregion

        #region snippet initialize dispatcherTimer
        /// <summary>
        /// snippet initialize dispatcherTimer
        /// </summary>
        void initDispatcherTimer()
        {
            dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
            dispatcherTimer.Tick += dispatcherTimer_Tick;
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 50);
            dispatcherTimer.Start();
        }
        #endregion

        #region Snippet initialize red / green gradient
        Brush blueGradien;
        LinearGradientBrush redGradien = new LinearGradientBrush();
        LinearGradientBrush greenGradien = new LinearGradientBrush();
        /// <summary>
        /// Snippet for red / green gradient
        /// </summary>
        void initGradients()
        {
            blueGradien = txLed.Fill;
            // reg Gradient
            redGradien.StartPoint = new Point(1,0.5);
            redGradien.EndPoint = new Point(0,0.5);
            redGradien.GradientStops.Add(new GradientStop(Colors.Black, 0));
            redGradien.GradientStops.Add(new GradientStop(Colors.Red, 1));
            greenGradien.StartPoint = new Point(1, 0.5);
            greenGradien.EndPoint = new Point(0, 0.5);
            greenGradien.GradientStops.Add(new GradientStop(Colors.Black, 0));
            greenGradien.GradientStops.Add(new GradientStop(Colors.Green, 1));
        }
        #endregion

        #region SerialPort write/read
        /// <summary>
        /// send data if serial port found
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        int writesNum;
        void sendData(object sender, RoutedEventArgs e)
        {
            if (serialPortFound)
            {
                my_Serial.Write(«\n» + DateTime.Now.ToString() + » Hello From my wpf app !!\r»);
                writesNum++;
            }
            else
            {
                dataRcv.Text = my_Serial.PortName + » Is not Available»;
            }
        }

        /// <summary>
        /// serial port data Received
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        string buffer;
        string frame;
        int tramNum;
        int buffNum;
        int diffMem;
        void receiveData(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            RxLed.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate { RxLed.Fill = greenGradien; }));
            buffNum++;
            buffer += my_Serial.ReadExisting();
            dataRcv.Invoke(new Action(() => dataRcv.Text = «(Numero de Recepcions : » + buffNum.ToString() + «) » + buffer));
            diffMem = writesNum – tramNum;
            int posI = buffer.IndexOf(«\n»);
            int posF = buffer.IndexOf(«\r»);
            if (posF > 0)
            {
                frame = buffer.Substring(posI + 1, posF – 1);
                buffer = buffer.Substring(posF + 1, buffer.Length – (posF + 1));
                frameR.Invoke(new Action(() => frameR.Text = «Sense: » + dispatcherTimer.Interval.TotalMilliseconds.ToString() + «ms (Trama º: » + tramNum.ToString() + «, de: » + writesNum.ToString() + «,dif: » +diffMem.ToString()+») » + frame));
                tramNum++;
            }
            RxLed.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate { RxLed.Fill = blueGradien; }));
        }
        #endregion

        #region dispatcherTimer task
        /// <summary>
        /// dispatcherTimer task
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        int stable = 0;
        void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            if ((bool)chkConti.IsChecked)
            {
                txLed.Fill = redGradien;
                refresh(RxLed);
                // autosense sendData interval
                if ((writesNum – tramNum) != diffMem)
                {
                    dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)dispatcherTimer.Interval.TotalMilliseconds + 10);
                }
                else
                {
                    stable++;
                    if (stable > 50 && dispatcherTimer.Interval.Milliseconds > 10)
                    {
                        stable = 0;
                        dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)dispatcherTimer.Interval.TotalMilliseconds – 5);
                    }
                }
                sendData(null, null);
                txLed.Fill = blueGradien;
                refresh(RxLed);
            }
        }
        #endregion

        #region Button task’s
        /// <summary>
        /// simulate a task calling process function prototipe
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        int numerofReentrys = 0;
        async void simulateTaskButton_Click(object sender, RoutedEventArgs e)
        {
            numerofReentrys++;
            wndMain.Title = «wndMain – in process : » + numerofReentrys.ToString();
            refresh(wndMain);
            Task newProc = Task.Factory.StartNew(process);
            await newProc;
            myButton.Content = «End Process»;
            numerofReentrys–;
            if (numerofReentrys > 0)
            {
                wndMain.Title = «wndMain – in process : » + numerofReentrys.ToString();
            }
            else
            {
                wndMain.Title = «wndMain – No process pending»;
            }
            refresh(wndMain);
        }
        /// <summary>
        /// Create new form
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        private void CreateForm_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.Form _myForm = new System.Windows.Forms.Form();
            System.Windows.Forms.ComboBox txt = new System.Windows.Forms.ComboBox();
            System.Windows.Forms.Label lbl = new System.Windows.Forms.Label();
            txt.Top = 100; txt.Left = 100;
            txt.Text = «Hola»;
            txt.Items.Add(«Uno»);
            txt.Items.Add(«Dos»);
            txt.Visible = true;
            lbl.Text = «HOLA!!!»;
            txt.SelectedIndexChanged += txt_SelectedIndexChanged;
            _myForm.Name = DateTime.Now.ToString();
            _myForm.Controls.Add(txt);
            _myForm.Controls.Add(lbl);
            _myForm.Show();
        }
        /// <summary>
        /// enumButton click
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        private void enumForm_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.FormCollection fmrs = System.Windows.Forms.Application.OpenForms;
            foreach (System.Windows.Forms.Form Ofmrs in fmrs)
            {
                MessageBoxResult result = System.Windows.MessageBox.Show(Ofmrs.Name);
            }
        }
        /// <summary>
        /// saveButton click
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        private void saveButton_Click(object sender, RoutedEventArgs e)
        {
            SqlAdap.Update(ds, «Sample»);
        }
        #endregion

        #region «common functions»
        /// <summary>
        /// simulate process iteration
        /// </summary>
        void process()
        {
            for (int index = 0; index < 10000; index++)
            {
                myButton.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate { myButton.Content = index.ToString(); }));
                System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
            }
        }

        /// <summary>
        /// refresh wpf element
        /// </summary>
        /// <param name=»element»></param>
        private Action _delegate = delegate() { };
        private void refresh(UIElement element)
        {
            this.Dispatcher.Invoke(DispatcherPriority.Render, _delegate);
            if (System.Windows.Application.Current != null)
            {
                System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
            }
        }
        /// <summary>
        /// catch for combo box selection chaged
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        void txt_SelectedIndexChanged(object sender, EventArgs e)
        {
            System.Windows.Forms.MessageBox.Show(«Hey!!»);
        }
        #endregion

        #region dataLayer to populate datagrid from Azure db/local file
        string Server = «tcp:afj66tfey7.database.windows.net,1433»;
        string DbName = «testCase»;
        string UserName = «Master@afj66tfey7«;
        string UserPwd = «Password13»;

        SqlConnection SqlConn;
        SqlDataAdapter SqlAdap;
        /// <summary>
        /// dataLayer to populate datagrid from Azure db/local file
        /// </summary>
        /// <param name=»sender»></param>
        /// <param name=»e»></param>
        void populateData(object sender, RoutedEventArgs e)
        {
            if ((bool)chkData.IsChecked)
            {
                SqlConn = new SqlConnection(«Server=» + Server + «;Database=» + DbName +
                    «;User ID=» + UserName + «;Password=» + UserPwd +
                    «;Trusted_Connection=False;Encrypt=True;Connection Timeout=30»);
                chkData.Content = «switch to local Data»;
            }
            else
            {
                SqlConn = new SqlConnection(@»Data Source=(LocalDB)\v11.0;AttachDbFilename=» +
                    System.Environment.CurrentDirectory.ToString() +
                     @»\testCase.mdf;Integrated Security=True;Connect Timeout=30″);
                chkData.Content = «switch to Azure Data»;
            }
            try
            {
                if (SqlConn.State == ConnectionState.Closed)
                {
                    SqlConn.Open();
                }
                SqlAdap = new SqlDataAdapter(«Select * From Sample», SqlConn);
                ds = new DataSet();
                SqlCommandBuilder builder = new SqlCommandBuilder(SqlAdap);
                SqlAdap.Fill(ds, «Sample»);
                dgv.DataSource = ds.Tables[«Sample»].DefaultView;
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show(ex.Message);
            }
        }

        #endregion

    }
}

Subproceso Anulado (ThreadAbordException)

Siguiendo casualmente algunos enlaces referentes a la ejecución de forms en diferentes hilos, algunos escenarios y de forma esporádica lanzan errores inesperados haciendo referencia a “thread abord exception” y “subproceso anulado” me decidí investigar un poquito más y…

Se deben diferenciar dos tipos

a) Unhandled exceptions descritas en : http://msdn.microsoft.com/es-es/library/system.windows.forms.application.threadexception(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

b) Las “threatAbordException” son mucho más sencillas de resolver, eso si no debéis caer en el error de intentar capturarlos en un “exception” genérico J

»’ <summary>

»’ 

»’ </summary>

»’ <remarks></remarks>

Sub Loading()

    Try

        Application.Run(New frmLoading)

    Catch ex As ThreadAbortException

        ‘ Nothing to do :-))

    End Try

End Sub

 

Espero que os sea útil, si es que es vuestro caso J
PepLluis,