000

Index Labels

Showing Progress Window When Running A Long Process

.
It is very often a command that does a complicated drawing/entities manipulation could take fair amount of time. During the command execution, it would be desired that something visual shows in AutoCAD to indicate the processing is in progress.

One option to do it is to use AutoCAD's progress meter, of course. However, many programmer would choose to show a small window with a progress bar. With this approach, the window can not only display a progress bar, but also display messages regarding the process execution.

This article presents a reusable progress window component that can be easily plugged into a long-running drawing process execution method.

The basic idea here is to define an interface (ILongProcessCommand) that raises events at beginning, ending and progressing of a process/command. Then the drawing manipulating process would be wrapped in a class that implements the said interface. The progress window component then will contain a reference of the drawing manipulating class (as the type of ILongProcessCommand) and subscribe the events (CommandStarted, CommandEnded and CommandProgress). The actual progress window will be shown, closed and updated in the event handler.

Since the goal is to create a reusable/pluggable progress window component, I created 2 projects in a VS solution:

1. Project "ProgressWindow"

As afore-mentioned, an interface is defined. Along with the interface are delegates and classes used for event raising/handling. Here is the code:

using System;

namespace ProgressWindow
{
public interface ILongProcessCommand
{
event CommandStartedEventHandler CommandStarted;
event CommandEndedEventHandler CommandEnded;
event CommandProgressEventHandler CommandProgress;
}

public delegate void CommandStartedEventHandler(
object sender, CommandStartEventArgs e);

public delegate void CommandEndedEventHandler(
object sender, EventArgs e);

public delegate void CommandProgressEventHandler(
object sender, CommandProgressEventArgs e);

public class CommandStartEventArgs : System.EventArgs
{
public string WindowTitle { set; get; }
public int ProgressMinValue { set; get; }
public int ProgressMaxValue { set; get; }
public int PrograssInitValue { set; get; }
public System.Windows.Forms.ProgressBarStyle ProgressBarStyle { set; get; }

public CommandStartEventArgs()
{
WindowTitle = "Command in Progress";
ProgressMinValue = 0;
ProgressMaxValue = 0;
ProgressBarStyle = System.Windows.Forms.ProgressBarStyle.Continuous;
}
}

public class CommandProgressEventArgs : System.EventArgs
{
public int ProgressValue { set; get; }
public string ProgressTitle { set; get; }
public string ProgressMessage { set; get; }

public CommandProgressEventArgs()
{
ProgressValue = 0;
ProgressTitle = "";
ProgressMessage = "";
}
}
}

Then, a window form is added. It has a progress bar and 2 labels. The form's ControlBox property is set to False, so that it canot be closed by user accidently. See picture below:


Here is the code of the form:

using System.Windows.Forms;

namespace ProgressWindow
{
public partial class dlgProgress : Form
{
public dlgProgress()
{
InitializeComponent();
}

public string ProgressTitleMessage
{
set { lblTitle.Text = value; }
get { return lblTitle.Text; }
}

public string ProgressDetailMessage
{
set { lblMessage.Text = value; }
get { return lblMessage.Text; }
}

public int ProgressMinValue
{
set { pBar.Minimum = value; }
get { return pBar.Minimum; }
}

public int ProgressMaxValue
{
set { pBar.Maximum=value;}
get { return pBar.Maximum;}
}

public int ProgressValue
{
set { pBar.Value = value; }
get { return pBar.Value; }
}
}
}

Finally, I added a class "ProgressIndicator", shown as following:

using System;

namespace ProgressWindow
{
public class ProgressIndicator : IDisposable
{
private ILongProcessCommand _cmdObject = null;
private dlgProgress _window = null;

public ProgressIndicator(ILongProcessCommand cmdObj)
{
_cmdObject = cmdObj;

WireCommandEvents();
}

#region IDisposable Members

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

#endregion

private void WireCommandEvents()
{
if (_cmdObject != null)
{
_cmdObject.CommandStarted +=
new CommandStartedEventHandler(_cmdObject_CommandStarted);

_cmdObject.CommandEnded +=
new CommandEndedEventHandler(_cmdObject_CommandEnded);

_cmdObject.CommandProgress +=
new CommandProgressEventHandler(_cmdObject_CommandProgress);
}
}

void _cmdObject_CommandStarted(object sender, CommandStartEventArgs e)
{
if (_window!=null) _window.Dispose();

_window = new dlgProgress();
_window.Text = e.WindowTitle;
_window.ProgressMinValue = e.ProgressMinValue;
_window.ProgressMaxValue = e.ProgressMaxValue;
_window.ProgressValue = e.PrograssInitValue;

Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog(_window);

}

void _cmdObject_CommandProgress(object sender, CommandProgressEventArgs e)
{
if (_window == null) return;

_window.ProgressTitleMessage = e.ProgressTitle;
_window.ProgressDetailMessage = e.ProgressMessage;
_window.ProgressValue = e.ProgressValue;

_window.Refresh();
System.Windows.Forms.Application.DoEvents();
}

void _cmdObject_CommandEnded(object sender, EventArgs e)
{
if (_window == null) return;

_window.Close();
}
}
}

Notice the constructor of the class take an ILongProcessCommand type as its argument and wire itself to the events of the ILongProcessCommand object. As long as the ILongProcessCommand fires an event (CommandStarted, CommandEnded, or CommandProgress), this class will show/close/update the progress window accordingly.

That is all for the reusable/pluggable progress window component. After building the project, it is ready to be used in drawing manipulating process as progress indicator.

2. Project "ProgressWindowSample"

This project is a regulaer AutoCAD managed DLL project. I added the first project as reference. I created a class "TestProcess". It has a method that is supposed to do some time consuming drawing data manipulating. To make things simple, I simply call Editor.SelectAll() to get ObjectId of all entities in the drawing into an ObjectIdCollection and then loop through each ObjectId to open eacg entity. If the drawing contains tens of thousands entities, the process would take a while. The kind of process would be good candidate process we want to show a progress indicator during the process.

Here is the code of class "TestProcess":

using System;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;

using ProgressWindow;

namespace ProgressWindowSample
{
public class TestProcess : ILongProcessCommand
{
private ObjectIdCollection _objIds = null;
private Document _dwg;

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

#region ILongProcessCommand Members

public event CommandStartedEventHandler CommandStarted;

public event CommandEndedEventHandler CommandEnded;

public event CommandProgressEventHandler CommandProgress;

#endregion

public void DoSomeWork()
{
PromptSelectionResult res = _dwg.Editor.SelectAll();
if (res.Status != PromptStatus.OK) return;

_objIds = new ObjectIdCollection(res.Value.GetObjectIds());

using (ProgressIndicator progress=new ProgressIndicator(this))
{
//Raise CommandStarted event that causes progress window to show
if (CommandStarted != null)
{
CommandStartEventArgs args = new CommandStartEventArgs();
args.WindowTitle = "Process All Entities in Current Drawing";
args.ProgressMaxValue = _objIds.Count;
args.ProgressMinValue = 0;
args.PrograssInitValue = 0;

CommandStarted(this, args);
}

ProcessDrawing(_objIds);

//Raise CommandEnded event that closes progress widow
if (CommandEnded != null)
{
CommandEnded(this, EventArgs.Empty);
}
}
}

private void ProcessDrawing(ObjectIdCollection ids)
{
int count = 0;
int total=ids.Count;

using (DocumentLock lck = _dwg.LockDocument())
{
using (Transaction tran =
_dwg.Database.TransactionManager.StartTransaction())
{
foreach (ObjectId id in ids)
{
count++;
string entClass = id.ObjectClass.Name;

if (CommandProgress != null)
{
CommandProgressEventArgs args = new CommandProgressEventArgs();
args.ProgressTitle = "Processing entity: " + entClass;
args.ProgressMessage =
count + " out of " + total + " entities processed...Please wait...";

args.ProgressValue = count;

CommandProgress(this, args);
}

Entity ent = (Entity)tran.GetObject(id, OpenMode.ForRead);

//Do something....
for (int i = 0; i < 10000; i++)
{
int x = i + 1000;
x = 0;
}
}

tran.Commit();
}
}
}
}
}

Notice that this class implements ILongProcessCommand interface. It is the resposibility of this class to raise appropriate event using the drawing data process (in this case, in public method "DoSomeWork()"), and pass suitable data to the EventArgs.

Now, let's put everything together in a command method to see the actual effect:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

namespace ProgressWindowSample
{
public class TestCommand
{
[CommandMethod("DoWork")]
public static void RunLongCommand()
{
Document dwg = Autodesk.AutoCAD.ApplicationServices.
Application.DocumentManager.MdiActiveDocument;

try
{
TestProcess prog = new TestProcess(dwg);
prog.DoSomeWork();

dwg.Editor.WriteMessage("\nProcess completed");
}
catch (System.Exception ex)
{
dwg.Editor.WriteMessage("\nError: " + ex.Message);
}
}
}
}

Now, build the project, start AutoCAD, netload "ProgressWindowSample.dll". Open a drawing with a lot of entities in it and enter command "DoWork". This video clip shows the progress window's effect.

As you can see, the ProgressIndicator component knows nothing about the actual long processing and only responds to the ILongProcessCommand's events to show/close/update progress window. As long as you wrap the long running CAD operation into a class that implements ILongProcessCommand and fire up appropriate event when needed, the progress window will automatically present the progress.

Blog Archive

Labels

3D Modeling 3D Sketch Inventor AI Design AI in Manufacturing AI Tools Architecture Artificial Intelligence AutoCAD AutoCAD advice AutoCAD Basics AutoCAD Beginners AutoCAD Civil3D AutoCAD commands AutoCAD efficiency AutoCAD features AutoCAD File Management AutoCAD Layer AutoCAD learning AutoCAD print settings AutoCAD productivity AutoCAD Teaching AutoCAD Techniques AutoCAD tips AutoCAD training. AutoCAD tricks AutoCAD Tutorial AutoCAD workflow AutoCAD Xref Autodesk Autodesk 2025 Autodesk AI Tools Autodesk AutoCAD Autodesk Fusion 360 Autodesk Inventor Autodesk Inventor Frame Generator Autodesk Inventor iLogic Autodesk Recap Autodesk Revit Autodesk Software Autodesk Video Automation Automation Tutorial Basic Commands Basics Beginner Beginner Tips BIM BIM Implementation Block Editor ByLayer CAD comparison CAD Design CAD File Size Reduction CAD line thickness CAD Optimization CAD Productivity CAD software clean CAD file cleaning command Cloud Collaboration command abbreviations Construction Technology Contraints Create resizable blocks CTB STB Data Reference Data Shortcut design software Design Workflow Digital Design Digital Twin Drafting Standards Drawing Automation Dref Dynamic Block Dynamic Block AutoCAD Dynamic Blocks Dynamic doors Dynamic windows eco design editing commands energy efficiency Engineering Engineering Design Engineering Innovation Engineering Technology engineering tools Excel Express Tools External Reference Fast Structural Design Fusion 360 Generative Design green building Grips heavy CAD file Heavy CAD Files iLogic Industry 4.0 Insight Inventor API Inventor Drawing Template Inventor Frame Generator Inventor Graphics Issues Inventor IDW Inventor Tips Keyboard Shortcuts Learn AutoCAD Machine Learning in CAD maintenance command Management Manufacturing Innovation Metal Structure ObjectARX .NET API Organization OVERKILL OVERKILL AutoCAD Palette PDF Plot Style AutoCAD Practice Drawing Printing Quality professional printing Professional Tips PTC Creo PURGE PURGE AutoCAD ReCap reduce CAD file size Resizable Block Revit Revit Best Practices Revit Workflow Ribbon screen shortcut keys Shortcuts Siemens NX Sketch Small Firms Smart Block Smart Factory SolidWorks Steel Structure Design sustainability Sustainable Manufacturing toolbar Tutorial User Interface (UI) Workbook Workspace XLS Xref