Kean Walmsley

July 2009

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Twitter Updates

    follow me on Twitter



    « "Custom Objects Masterclass" webcast recording available for download | Main | Updating a specific attribute inside a folder of AutoCAD drawings using RealDWG from .NET »

    August 06, 2007

    A handy .NET class to help manage long operations in AutoCAD

    This post was almost called "Generating Koch fractals in AutoCAD using .NET - Part 3", following on from Parts 1 & 2 of the series. But by the time I'd completed the code, I realised it to be of more general appeal and decided to provide it with a more representative title.

    I started off by adding a progress meter and an escape key handler to the code in the last post. Then, while refactoring the code, I decided to encapsulate the functionality in a standalone class that could be dropped into pretty much any AutoCAD .NET project (although I've implemented it in C#, as usual).

    So what we have is a new class called LongOperationManager, which does the following:

    • Displays and updates a progress meter (at the bottom left of AutoCAD's window)
      • Allowing you to set an arbitrary message and total number of operations
    • Listens for "escape" in case the user wants to interrupt the current operation

    Here's the class implementation:

    public class LongOperationManager :

      IDisposable, System.Windows.Forms.IMessageFilter

    {

      // The message code corresponding to a keypress

      const int WM_KEYDOWN = 0x0100;


      // The number of times to update the progress meter

      // (for some reason you need 600 to tick through

      //  for each percent)

      const int progressMeterIncrements = 600;


      // Internal members for metering progress

      private ProgressMeter pm;

      private long updateIncrement;

      private long currentInc;


      // External flag for checking cancelled status

      public bool cancelled = false;


      // Constructor


      public LongOperationManager(string message)

      {

        System.Windows.Forms.Application.

          AddMessageFilter(this);

        pm = new ProgressMeter();

        pm.Start(message);

        pm.SetLimit(progressMeterIncrements);

        currentInc = 0;

      }


      // System.IDisposable.Dispose


      public void Dispose()

      {

        pm.Stop();

        pm.Dispose();

        System.Windows.Forms.Application.

          RemoveMessageFilter(this);

      }


      // Set the total number of operations


      public void SetTotalOperations(long totalOps)

      {

        // We really just care about when we need

        // to update the timer

        updateIncrement =

          (totalOps > progressMeterIncrements ?

            totalOps / progressMeterIncrements :

            totalOps

          );

      }


      // This function is called whenever an operation

      // is performed


      public bool Tick()

      {

        if (++currentInc == updateIncrement)

        {

          pm.MeterProgress();

          currentInc = 0;

          System.Windows.Forms.Application.DoEvents();

        }

        // Check whether the filter has set the flag

        if (cancelled)

          pm.Stop();


        return !cancelled;

      }


      // The message filter callback


      public bool PreFilterMessage(

        ref System.Windows.Forms.Message m

      )

      {

        if (m.Msg == WM_KEYDOWN)

        {

          // Check for the Escape keypress

          System.Windows.Forms.Keys kc =

            (System.Windows.Forms.Keys)(int)m.WParam &

            System.Windows.Forms.Keys.KeyCode;


          if (m.Msg == WM_KEYDOWN &&

              kc == System.Windows.Forms.Keys.Escape)

          {

            cancelled = true;

          }


          // Return true to filter all keypresses

          return true;

        }

        // Return false to let other messages through

        return false;

      }

    }

    In terms of how to use the class... first of all you create an instance of it, setting the string to be shown on the progress meter (just like AutoCAD's ProgressMeter class). As the LongOperationManager implements IDisposable, then at the end you should either call Dispose or manage it's scope with the using() statement.

    I chose to separate the setting of the total number of operations to be completed from the object's construction, as in our example we need to an initial pass before we know how many objects we're working with (and we want to at least put the label on the progress meter while we perform that initial pass).

    Then we just call the Tick() method whenever we perform an operation - this updates the progress meter and checks for use of the escape key. The idea is that you set the total number of operations and then call Tick() for each one of those individual operations - the class takes care of how often it needs to update the progress meter. If it finds escape has been used, the Tick() method will return false.

    That's about it, aside from the fact you can also query the "cancelled" property to see whether escape has been used.

    Here's the basic approach:

    LongOperationManager lom =

      new LongOperationManager("Fractalizing entities");

    using (lom)

    {

      ...

      lom.SetTotalOperations(totalOps);

      ...

      while (true)

      {

        ...

        if (!lom.Tick())

        {

          ed.WriteMessage("\nFractalization cancelled.\n");

          break;

        }

      }

    }

    Here's the code integrated into the previous example, with the significant lines in red:

        1 using Autodesk.AutoCAD.ApplicationServices;

        2 using Autodesk.AutoCAD.DatabaseServices;

        3 using Autodesk.AutoCAD.EditorInput;

        4 using Autodesk.AutoCAD.Runtime;

        5 using Autodesk.AutoCAD.Geometry;

        6 using System.Collections.Generic;

        7 using System;

        8

        9 namespace Kochizer

       10 {

       11   public class Commands

       12   {

       13     // We generate 4 new entities for every old entity

       14     // (unless a complex entity such as a polyline)

       15

       16     const int newEntsPerOldEnt = 4;

       17

       18     [CommandMethod("KA")]

       19     public void KochizeAll()

       20     {

       21       Document doc =

       22         Application.DocumentManager.MdiActiveDocument;

       23       Database db = doc.Database;

       24       Editor ed = doc.Editor;

       25

       26       // Acquire user input - whether to create the

       27       // new geometry to the left or the right...

       28

       29       PromptKeywordOptions pko =

       30         new PromptKeywordOptions(

       31           "\nCreate fractal to side (Left/<Right>): "

       32         );

       33       pko.Keywords.Add("Left");

       34       pko.Keywords.Add("Right");

       35

       36       PromptResult pr =

       37         ed.GetKeywords(pko);

       38       bool bLeft = false;

       39

       40       if (pr.Status != PromptStatus.None &&

       41           pr.Status != PromptStatus.OK)

       42         return;

       43

       44       if ((string)pr.StringResult == "Left")

       45         bLeft = true;

       46

       47       // ... and the recursion depth for the command.

       48

       49       PromptIntegerOptions pio =

       50         new PromptIntegerOptions(

       51           "\nEnter recursion level <1>: "

       52         );

       53       pio.AllowZero = false;

       54       pio.AllowNegative = false;

       55       pio.AllowNone = true;

       56

       57       PromptIntegerResult pir =

       58         ed.GetInteger(pio);

       59       int recursionLevel = 1;

       60

       61       if (pir.Status != PromptStatus.None &&

       62           pir.Status != PromptStatus.OK)

       63         return;

       64

       65       if (pir.Status == PromptStatus.OK)

       66         recursionLevel = pir.Value;

       67

       68       // Create and add our long operation handler

       69       LongOperationManager lom =

       70         new LongOperationManager("Fractalizing entities");

       71

       72       using (lom)

       73       {

       74         // Note: strictly speaking we're not recursing,

       75         // we're iterating, but the effect to the user

       76         // is the same.

       77

       78         Transaction tr =

       79           doc.TransactionManager.StartTransaction();

       80         using (tr)

       81         {

       82           BlockTable bt =

       83             (BlockTable)tr.GetObject(

       84               db.BlockTableId,

       85               OpenMode.ForRead

       86             );

       87           using (bt)

       88           {

       89             // No need to open the block table record

       90             // for write, as we're just reading data

       91             // for now

       92

       93             BlockTableRecord btr =

       94               (BlockTableRecord)tr.GetObject(

       95                 bt[BlockTableRecord.ModelSpace],

       96                 OpenMode.ForRead

       97               );

       98             using (btr)

       99             {

      100               // List of changed entities

      101               // (will contain complex entities, such as

      102               // polylines"

      103

      104               ObjectIdCollection modified =

      105                 new ObjectIdCollection();

      106

      107               // List of entities to erase

      108               // (will contain replaced entities)

      109

      110               ObjectIdCollection toErase =

      111                 new ObjectIdCollection();

      112

      113               // List of new entitites to add

      114               // (will be processed recursively or

      115               // assed to the open block table record)

      116

      117               List<Entity> newEntities =

      118                 new List<Entity>(

      119                   db.ApproxNumObjects * newEntsPerOldEnt

      120                 );

      121

      122               // Kochize each entity in the open block

      123               // table record

      124

      125               foreach (ObjectId objId in btr)

      126               {

      127                 Entity ent =

      128                   (Entity)tr.GetObject(

      129                     objId,

      130                     OpenMode.ForRead

      131                   );

      132                 Kochize(

      133                   ent,

      134                   modified,

      135                   toErase,

      136                   newEntities,

      137                   bLeft

      138                 );

      139               }

      140

      141               // The number of operations is...

      142               //  The number of complex entities multiplied

      143               //  by the recursion level (they each get

      144               //  "kochized" once per level,

      145               //  even if that's a long operation)

      146               // plus

      147               //  (4^0 + 4^1 + 4^2 + 4^3... + 4^n) multiplied

      148               //  by the number of db-resident ents

      149               // where n is the recursion level. Phew!

      150

      151               long totalOps =

      152                     modified.Count * recursionLevel +

      153                     operationCount(recursionLevel) *

      154                     toErase.Count;

      155               lom.SetTotalOperations(totalOps);

      156

      157               // If we need to loop,

      158               // work on the returned entities

      159

      160               while (--recursionLevel > 0)

      161               {

      162                 // Create an output array

      163

      164                 List<Entity> newerEntities =

      165                   new List<Entity>(

      166                     newEntities.Count * newEntsPerOldEnt

      167                   );

      168

      169                 // Kochize all the modified (complex) ents

      170

      171                 foreach (ObjectId objId in modified)

      172                 {

      173                   if (!lom.Tick())

      174                   {

      175                     ed.WriteMessage(

      176                       "\nFractalization cancelled.\n"

      177                     );

      178                     break;

      179                   }

      180

      181                   Entity ent =

      182                     (Entity)tr.GetObject(

      183                       objId,

      184                       OpenMode.ForRead

      185                     );

      186                   Kochize(

      187                     ent,

      188                     modified,

      189                     toErase,

      190                     newerEntities,

      191                     bLeft

      192                   );

      193                 }

      194

      195                 // Kochize all the non-db resident entities

      196                 if (!lom.cancelled)

      197                 {

      198                   foreach (Entity ent in newEntities)

      199                   {

      200                     if (!lom.Tick())

      201                     {

      202                       ed.WriteMessage(

      203                         "\nFractalization cancelled.\n"

      204                       );

      205                       break;

      206                     }

      207                     Kochize(

      208                       ent,

      209                       modified,

      210                       toErase,

      211                       newerEntities,

      212                       bLeft

      213                     );

      214                   }

      215                 }

      216

      217                 // We now longer need the intermediate ents

      218                 // previously output for the level above,

      219                 // we replace them with the latest output

      220

      221                 newEntities.Clear();

      222                 newEntities = newerEntities;

      223               }

      224

      225               lom.Tick();

      226

      227               if (!lom.cancelled)

      228               {

      229                 // Erase each replaced db-resident ent

      230

      231                 foreach (ObjectId objId in toErase)

      232                 {

      233                   Entity ent =

      234                     (Entity)tr.GetObject(

      235                       objId,

      236                       OpenMode.ForWrite

      237                     );

      238                   ent.Erase();

      239                 }

      240

      241                 // Add the new entities

      242

      243                 btr.UpgradeOpen();

      244                 foreach (Entity ent in newEntities)

      245                 {

      246                   btr.AppendEntity(ent);

      247                   tr.AddNewlyCreatedDBObject(ent, true);

      248                 }

      249               }

      250               tr.Commit();

      251             }

      252           }

      253         }

      254       }

      255     }

      256

      257     static long

      258     operationCount(int nRecurse)

      259     {

      260       if (1 >= nRecurse)

      261         return 1;

      262       return

      263         (long)Math.Pow(

      264           newEntsPerOldEnt,

      265           nRecurse - 1

      266         )

      267         + operationCount(nRecurse - 1);

      268     }

      269

      270     // Dispatch function to call through to various per-type

      271     // functions

      272

      273     private void Kochize(

      274       Entity ent,

      275       ObjectIdCollection modified,

      276       ObjectIdCollection toErase,

      277       List<Entity> toAdd,

      278       bool bLeft

      279     )

      280     {

      281       Line ln = ent as Line;

      282       if (ln != null)

      283       {

      284         Kochize(ln, modified, toErase, toAdd, bLeft);

      285         return;

      286       }

      287       Arc arc = ent as Arc;

      288       if (arc != null)

      289       {

      290         Kochize(arc, modified, toErase, toAdd, bLeft);

      291         return;

      292       }

      293       Polyline pl = ent as Polyline;

      294       if (pl != null)

      295       {

      296         Kochize(pl, modified, toErase, toAdd, bLeft);

      297         return;

      298       }

      299     }

      300

      301     // Create 4 new lines from a line passed in

      302

      303     private void Kochize(

      304       Line ln,

      305       ObjectIdCollection modified,

      306       ObjectIdCollection toErase,

      307       List<Entity> toAdd,

      308       bool bLeft

      309     )

      310     {

      311       // Get general info about the line

      312       // and calculate the main 5 points

      313

      314       Point3d pt1 = ln.StartPoint,

      315               pt5 = ln.EndPoint;

      316       Vector3d vec1 = pt5 - pt1,

      317               norm1 = vec1.GetNormal();

      318       double d_3 = vec1.Length / 3;

      319       Point3d pt2 = pt1 + (norm1 * d_3),

      320               pt4 = pt1 + (2 * norm1 * d_3);

      321       Vector3d vec2 = pt4 - pt2;

      322

      323       if (bLeft)

      324         vec2 =

      325           vec2.RotateBy(

      326             Math.PI / 3, new Vector3d(0, 0, 1)

      327           );

      328       else

      329         vec2 =

      330           vec2.RotateBy(

      331             5 * Math.PI / 3, new Vector3d(0, 0, 1)

      332           );

      333       Point3d pt3 = pt2 + vec2;

      334

      335       // Mark the original to be erased

      336

      337       if (ln.ObjectId != ObjectId.Null)

      338         toErase.Add(ln.ObjectId);

      339

      340       // Create the first line

      341

      342       Line ln1 = new Line(pt1, pt2);

      343       ln1.SetPropertiesFrom(ln);

      344       ln1.Thickness = ln.Thickness;

      345       toAdd.Add(ln1);

      346

      347       // Create the second line

      348

      349       Line ln2 = new Line(pt2, pt3);

      350       ln2.SetPropertiesFrom(ln);

      351       ln2.Thickness = ln.Thickness;

      352       toAdd.Add(ln2);

      353

      354       // Create the third line

      355

      356       Line ln3 = new Line(pt3, pt4);

      357       ln3.SetPropertiesFrom(ln);

      358       ln3.Thickness = ln.Thickness;

      359       toAdd.Add(ln3);

      360

      361       // Create the fourth line

      362

      363       Line ln4 = new Line(pt4, pt5);

      364       ln4.SetPropertiesFrom(ln);

      365       ln4.Thickness = ln.Thickness;

      366       toAdd.Add(ln4);

      367     }

      368

      369     // Create 4 new arcs from an arc passed in

      370

      371     private void Kochize(

      372       Arc arc,

      373       ObjectIdCollection modified,

      374       ObjectIdCollection toErase,

      375       List<Entity> toAdd,

      376       bool bLeft

      377     )

      378     {

      379       // Get general info about the arc

      380       // and calculate the main 5 points

      381

      382       Point3d pt1 = arc.StartPoint,

      383               pt5 = arc.EndPoint;

      384       double length = arc.GetDistAtPoint(pt5),

      385             angle = arc.StartAngle;

      386       Vector3d full = pt5 - pt1;

      387

      388       Point3d pt2 = arc.GetPointAtDist(length / 3),

      389               pt4 = arc.GetPointAtDist(2 * length / 3);

      390

      391       // Mark the original to be erased

      392

      393       if (arc.ObjectId != ObjectId.Null)

      394         toErase.Add(arc.ObjectId);

      395

      396       // Create the first arc

      397

      398       Point3d mid = arc.GetPointAtDist(length / 6);

      399       CircularArc3d tmpArc =

      400         new CircularArc3d(pt1, mid, pt2);

      401       Arc arc1 = circArc2Arc(tmpArc);

      402       arc1.SetPropertiesFrom(arc);

      403       arc1.Thickness = arc.Thickness;

      404       toAdd.Add(arc1);

      405

      406       // Create the second arc

      407

      408       mid = arc.GetPointAtDist(length / 2);

      409       tmpArc.Set(pt2, mid, pt4);

      410       if (bLeft)

      411         tmpArc.RotateBy(Math.PI / 3, tmpArc.Normal, pt2);

      412       else

      413         tmpArc.RotateBy(5 * Math.PI / 3, tmpArc.Normal, pt2);

      414       Arc arc2 = circArc2Arc(tmpArc);

      415       arc2.SetPropertiesFrom(arc);

      416       arc2.Thickness = arc.Thickness;

      417       toAdd.Add(arc2);

      418

      419       // Create the third arc

      420

      421       tmpArc.Set(pt2, mid, pt4);

      422       if (bLeft)

      423         tmpArc.RotateBy(5 * Math.PI / 3, tmpArc.Normal, pt4);

      424       else

      425         tmpArc.RotateBy(Math.PI / 3, tmpArc.Normal, pt4);

      426       Arc arc3 = circArc2Arc(tmpArc);

      427       arc3.SetPropertiesFrom(arc);

      428       arc3.Thickness = arc.Thickness;

      429       toAdd.Add(arc3);

      430

      431       // Create the fourth arc

      432

      433       mid = arc.GetPointAtDist(5 * length / 6);

      434       Arc arc4 =

      435         circArc2Arc(new CircularArc3d(pt4, mid, pt5));

      436       arc4.SetPropertiesFrom(arc);

      437       arc4.Thickness = arc.Thickness;

      438       toAdd.Add(arc4);

      439     }

      440

      441     Arc circArc2Arc(CircularArc3d circArc)

      442     {

      443       Point3d center = circArc.Center;

      444       Vector3d normal = circArc.Normal;

      445       Vector3d refVec = circArc.ReferenceVector;

      446       Plane plane = new Plane(center, normal);

      447       double ang = refVec.AngleOnPlane(plane);

      448       return new Arc(

      449         center,

      450         normal,

      451         circArc.Radius,

      452         circArc.StartAngle + ang,

      453         circArc.EndAngle + ang

      454       );

      455     }

      456

      457     private void Kochize(

      458       Polyline pl,

      459       ObjectIdCollection modified,

      460       ObjectIdCollection toErase,

      461       List<Entity> toAdd,

      462       bool bLeft

      463     )

      464     {

      465       pl.UpgradeOpen();

      466

      467       if (pl.ObjectId != ObjectId.Null &&

      468           !modified.Contains(pl.ObjectId))

      469       {

      470         modified.Add(pl.ObjectId);

      471       }

      472

      473       for(int vn = 0; vn < pl.NumberOfVertices; vn++)

      474       {

      475         SegmentType st = pl.GetSegmentType(vn);

      476         if (st != SegmentType.Line && st != SegmentType.Arc)

      477           continue;

      478

      479         double sw = pl.GetStartWidthAt(vn),

      480               ew = pl.GetEndWidthAt(vn);

      481

      482         if (st == SegmentType.Line)

      483         {

      484           if (vn + 1 == pl.NumberOfVertices)

      485             continue;

      486

      487           LineSegment2d ls = pl.GetLineSegment2dAt(vn);

      488           Point2d pt1 = ls.StartPoint,

      489                   pt5 = ls.EndPoint;

      490           Vector2d vec = pt5 - pt1;

      491           double d_3 = vec.Length / 3;

      492           Point2d pt2 = pt1 + (vec.GetNormal() * d_3),

      493                   pt4 = pt1 + (vec.GetNormal() * 2 * d_3);

      494           Vector2d vec2 = pt4 - pt2;

      495

      496           if (bLeft)

      497             vec2 = vec2.RotateBy(Math.PI / 3);

      498           else

      499             vec2 = vec2.RotateBy(5 * Math.PI / 3);

      500

      501           Point2d pt3 = pt2 + vec2;

      502

      503           pl.AddVertexAt(++vn, pt2, 0, sw, ew);

      504           pl.AddVertexAt(++vn, pt3, 0, sw, ew);

      505           pl.AddVertexAt(++vn, pt4, 0, sw, ew);

      506         }

      507         else if (st == SegmentType.Arc)

      508         {

      509           CircularArc3d ca = pl.GetArcSegmentAt(vn);

      510           double oldBulge = pl.GetBulgeAt(vn);

      511

      512           // Build a standard arc and use that for the calcs

      513

      514           Arc arc = circArc2Arc(ca);

      515

      516           // Get the main 5 points

      517

      518           Point3d pt1 = arc.StartPoint,

      519                   pt5 = arc.EndPoint;

      520

      521           double ln = arc.GetDistAtPoint(pt5);

      522           Point3d pt2 = arc.GetPointAtDist(ln / 3),

      523                   pt4 = arc.GetPointAtDist(2 * ln / 3);

      524

      525           Point3d mid = arc.GetPointAtDist(ln / 2);

      526

      527           CircularArc3d tmpArc =

      528             new CircularArc3d(pt2, mid, pt4);

      529           if (bLeft)

      530             tmpArc.RotateBy(5*Math.PI/3, tmpArc.Normal, pt4);

      531           else

      532             tmpArc.RotateBy(Math.PI / 3, tmpArc.Normal, pt4);

      533

      534           Point3d pt3 = tmpArc.StartPoint;

      535

      536           // Now add the new segments, setting the bulge

      537           // for the existing one and the new ones to a third

      538           // (as the segs are a third as big as the old one)

      539

      540           CoordinateSystem3d ecs = pl.Ecs.CoordinateSystem3d;

      541           Plane pn = new Plane(ecs.Origin, pl.Normal);

      542           double bu = oldBulge / 3;

      543

      544           pl.SetBulgeAt(vn, bu);

      545           pl.AddVertexAt(

      546             ++vn, pt2.Convert2d(pn), bu, sw, ew);

      547           pl.AddVertexAt(

      548             ++vn, pt3.Convert2d(pn), bu, sw, ew);

      549           pl.AddVertexAt(

      550             ++vn, pt4.Convert2d(pn), bu, sw, ew);

      551         }

      552       }

      553       pl.DowngradeOpen();

      554     }

      555

      556     public class LongOperationManager :

      557       IDisposable, System.Windows.Forms.IMessageFilter

      558     {

      559       // The message code corresponding to a keypress

      560       const int WM_KEYDOWN = 0x0100;

      561

      562       // The number of times to update the progress meter

      563       // (for some reason you need 600 to tick through

      564       //  for each percent)

      565       const int progressMeterIncrements = 600;

      566

      567       // Internal members for metering progress

      568       private ProgressMeter pm;

      569       private long updateIncrement;

      570       private long currentInc;

      571

      572       // External flag for checking cancelled status

      573       public bool cancelled = false;

      574

      575       // Constructor

      576

      577       public LongOperationManager(string message)

      578       {

      579         System.Windows.Forms.Application.

      580           AddMessageFilter(this);

      581         pm = new ProgressMeter();

      582         pm.Start(message);

      583         pm.SetLimit(progressMeterIncrements);

      584         currentInc = 0;

      585       }

      586

      587       // System.IDisposable.Dispose

      588

      589       public void Dispose()

      590       {

      591         pm.Stop();

      592         pm.Dispose();

      593         System.Windows.Forms.Application.

      594           RemoveMessageFilter(this);

      595       }

      596

      597       // Set the total number of operations

      598

      599       public void SetTotalOperations(long totalOps)

      600       {

      601         // We really just care about when we need

      602         // to update the timer

      603         updateIncrement =

      604           (totalOps > progressMeterIncrements ?

      605             totalOps / progressMeterIncrements :

      606             totalOps

      607           );

      608       }

      609

      610       // This function is called whenever an operation

      611       // is performed

      612

      613       public bool Tick()

      614       {

      615         if (++currentInc == updateIncrement)

      616         {

      617           pm.MeterProgress();

      618           currentInc = 0;

      619           System.Windows.Forms.Application.DoEvents();

      620         }

      621         // Check whether the filter has set the flag

      622         if (cancelled)

      623           pm.Stop();

      624

      625         return !cancelled;

      626       }

      627

      628       // The message filter callback

      629

      630       public bool PreFilterMessage(

      631         ref System.Windows.Forms.Message m

      632       )

      633       {

      634         if (m.Msg == WM_KEYDOWN)

      635         {

      636           // Check for the Escape keypress

      637           System.Windows.Forms.Keys kc =

      638             (System.Windows.Forms.Keys)(int)m.WParam &

      639             System.Windows.Forms.Keys.KeyCode;

      640

      641           if (m.Msg == WM_KEYDOWN &&

      642               kc == System.Windows.Forms.Keys.Escape)

      643           {

      644             cancelled = true;

      645           }

      646

      647           // Return true to filter all keypresses

      648           return true;

      649         }

      650         // Return false to let other messages through

      651         return false;

      652       }

      653     }

      654   }

      655 }

      656

    Here's the source the source file for download.

    TrackBack

    TrackBack URL for this entry:
    http://www.typepad.com/services/trackback/6a00d83452464869e200e39336a2cf8834

    Listed below are links to weblogs that reference A handy .NET class to help manage long operations in AutoCAD:

    Comments

    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search