Showing Progress for long code execution in AutoCAD

Showing Progress for long code execution in AutoCAD


More often than not we need to write code to loop through large amount of data set, such as entities in a selection set or even entire ModelSpace/PaperSpace of a huge drawing. This process may take a while to complete. It is a common practice to show a progress bar during this lengthy processing to let user know that AutoCAD is busy processing data.

With AutoCAD .NET API, one can quite easily uses the built-in Autodesk.AutoCAD.Runtime.ProgressMeter object to show progress of lengthy executing process. However, from my experience of using ProgressMeter, it often does not show a satisfactory progressing visual effect. The processing effect shown by the ProgressMeter for exact long processing operation done by the exact code could be different from one AutoCAD version to another version, and in many cases, the progress meter simply does not get refreshed during the lengthy processing.

A few years back, I wrote an article on showing a progress window for a long running process in AutoCAD, where I used a modeless window/dialog box to host a progress bar. The purpose was to separate the actual lengthy operation and the visual progress effect in different component, so that the code for progress bar can be easily reused as a component. Again, it has been proving that using modeless window/dialog box has the same unstable progressing visual effect. That is the progress bar sometimes is not refreshed when AutoCAD is busy of intense processing.

I guess, using either AutoCAD built-in ProgressMeter, or ProgressBar hosted in a modeless window, we, as .NET API programmer, do not have much control on how AutoCAD handles its UI update.

The only way to guarantee the UI that shows promptly updated progress bar is to host the progress bar in a modal window/dialog box.

So, I decided to update/rebuild my progress bar code component by using a modal form. Here I used similar approach to use an Interface separating the actual code that does the lengthy AutoCAD processing and the code to display progressing bar.

Firstly, I define an Interface that will be used by the progress bar component. This Interface object tells the progress bar component when a lengthy processing starts and end, stimulate the progress bar to progress: all of these are done through vents defined in this Interface. The Interface also define an action (method), which the progress bar component calls as soon as the progress bar UI shows. See code below (including custom EventHandler and EventArgs used by those Events:

using System;

namespace ShowProgressBar
{
    public interface ILongProcessingObject
    {
        event LongProcessStarted ProcessingStarted;
        event LongProcessingProgressed ProcessingProgressed;
        event EventHandler ProcessingEnded;
        event EventHandler CloseProgressUIRequested;
        void DoLongProcessingWork();
    }

    public class LongProcessStartedEventArgs : EventArgs
    {
        private int _loopCount;
        private string _description;
        private bool _canStop;

        public LongProcessStartedEventArgs(
            string description, int loopCount = 0, bool canStop = false)
        {
            _loopCount = loopCount;
            _description = description;
            _canStop = canStop;
        }

        public int LoopCount
        {
            get { return _loopCount; }
        }

        public string Description
        {
            get { return _description; }
        }

        public bool CanStop
        {
            get { return _canStop; }
        }
    }

    public class LongProcessingProgressEventArgs : EventArgs
    {
        private string _progressDescription;
        private bool _cancel = false;

        public LongProcessingProgressEventArgs(string progressDescription)
        {
            _progressDescription = progressDescription;
        }

        public string ProgressDescription
        {
            get { return _progressDescription; }
        }

        public bool Cancel
        {
            set { _cancel = value; }
            get { return _cancel; }
        }
    }

    public delegate void LongProcessStarted(
        object sender, LongProcessStartedEventArgs e);

    public delegate void LongProcessingProgressed(
        object sender, LongProcessingProgressEventArgs e);
}

Then here the progress bar component:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShowProgressBar
{
    public class ProcessingProgressBar : IDisposable
    {
        private dlgProgress _dlg = null;
        private ILongProcessingObject _executingObject;

        public ProcessingProgressBar(ILongProcessingObject executingObj)
        {
            _executingObject = executingObj;
        }

        public void Start()
        {
            _dlg = new dlgProgress(_executingObject);
            _dlg.ShowDialog();
        }

        public void Dispose()
        {
            if (_dlg!=null)
            {
                _dlg.Dispose();
            }
        }
    }
}

As the code show, it is really simple with one public method Start(), which shows a modal dialog box where the progress bar is hosted. This component implements IDispose(), so that when it is disposed, the instance of dialog box (Windows Form) is disposed. The most important part of this component is that it holds an instance of ILongProcessingObject class: as soon as the progress bar form shows, the Start() method of this ILongProcessingObject gets called, and its events subscribed by the progress bar form stimulate the progress bar to progress and close the form when processing is done.

The dialog box form has 4 controls on it: 2 labels to show executing process message/description, a progress bar, and a button labelled a "Stop". Here is the code behind the form:

using System;
using System.Windows.Forms;

namespace ShowProgressBar
{
    public partial class dlgProgress : Form
    {
        private ILongProcessingObject _executingObject = null;
        private bool _stop = false;
        private bool _isMarquee = false;
        private int _loopCount = 0;

        public dlgProgress()
        {
            InitializeComponent();
        }

        public dlgProgress(
            ILongProcessingObject executingObj)
            : this()
        {
            _executingObject = executingObj;

            _executingObject.ProcessingStarted +=
                new LongProcessStarted(ExecutingObject_ProcessStarted);
            _executingObject.ProcessingProgressed +=
                new LongProcessingProgressed(ExecutingObject_Progressed);
            _executingObject.ProcessingEnded +=
                new EventHandler(ExecutingObject_ProcessEnded);
            _executingObject.CloseProgressUIRequested +=
               new EventHandler(ExecutingObject_CloseProgressUIRequested);
        }

        private void ExecutingObject_CloseProgressUIRequested(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.OK;
        }

        private void ExecutingObject_ProcessEnded(object sender, EventArgs e)
        {
            pBar.Value = 0;
            lblTitle.Text = "";
            lblDescription.Text = "";
            this.Refresh();
        }

        private void ExecutingObject_ProcessStarted(
            object sender, LongProcessStartedEventArgs e)
        {

            if (e.LoopCount == 0)
            {
                pBar.Style = ProgressBarStyle.Marquee;
                lblDescription.Text = "Please wait...";
            }
            else
            {
                pBar.Style = ProgressBarStyle.Continuous;
                pBar.Minimum = 0;
                pBar.Maximum = e.LoopCount;
                pBar.Value = 0;
                lblDescription.Text = "";
                _loopCount = e.LoopCount;
            }

            _isMarquee = e.LoopCount == 0;
            btnStop.Visible = e.CanStop;
            lblTitle.Text = e.Description;
            Application.DoEvents();
            this.Refresh();
        }

        private void ExecutingObject_Progressed(
            object sender, LongProcessingProgressEventArgs e)
        {
            if (!_isMarquee)
            {
                pBar.Value++;
            }
 
            lblDescription.Text = e.ProgressDescription;
            lblDescription.Refresh();

            Application.DoEvents();
            if (_stop)
            {
                e.Cancel = true;
            }
        }

        private void dlgProgress_Shown(object sender, EventArgs e)
        {
            Application.DoEvents();

            if (_executingObject == null)
            {
                this.DialogResult = DialogResult.Cancel;
            }
            else
            {
                _executingObject.DoLongProcessingWork();
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            _stop = true;
        }
    }
}

Sometimes, a long processing may not have determined loop count, or the processing loop only ends with certain condition been met. In the code showing here, I assume if the LongProcessStartedEventArg.LoopCount is 0, then it means the processing loop is unknown, so I set the ProgressBars Style property to Marquee.

That is all the code for the progress bar component that can be used easily with my AutoCAD data processing objects, as long as they implement ILongProcessingObject Interface. Here is an example class MyCadDataHandler, in which I let it do 2 lengthy tasks:

  • one with known loop count: looping through drawings ModelSpace for each entity and do something with it
  • one without known loop count: looping through drawings ModelSpace to find BlockReferences with certain name, and if the count of the found BlockReferences reach a given number the looping stops

Here is he code of class MyCadDataHandler:

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

namespace ShowProgressBar
{
    public class MyCadDataHandler : ILongProcessingObject
    {
        private enum LongProcessingType
        {
            Type1=0,
            Type2=1,
        }

        private Document _dwg;
        private LongProcessingType _processingType = LongProcessingType.Type1;

        public MyCadDataHandler(Document dwg)
        {
            _dwg = dwg;
        }

        #region public methods

        public void DoWorkWithKnownLoopCount()
        {
            _processingType = LongProcessingType.Type1;

            using (var progress=new ProcessingProgressBar(this))
            {
                progress.Start();
            }
        }

        public void DoWorkWithUnknownLoopCount()
        {
            _processingType = LongProcessingType.Type2;

            using (var progress = new ProcessingProgressBar(this))
            {
                progress.Start();
            }
        }

        #endregion

        #region Implementing ILongProcessingObject interface

        public event LongProcessStarted ProcessingStarted;

        public event LongProcessingProgressed ProcessingProgressed;

        public event EventHandler ProcessingEnded;

        public event EventHandler CloseProgressUIRequested;

        public void DoLongProcessingWork()

visit link download

Popular posts from this blog

SolidWorks 2017 Premium Full Crack

Game School Minecraft

Resident Evil Revelations Raid Mode Custom Parts