Progress bar flickering during update in AutoCAD 2021...

Progress bar flickering during update in AutoCAD 2021...

rickjohnston
Advocate Advocate
3,104 Views
9 Replies
Message 1 of 10

Progress bar flickering during update in AutoCAD 2021...

rickjohnston
Advocate
Advocate

Hi All.

I have code in a DLL that executes updating database records of objects. The process is sometime very time consuming based upon how many objects will be processed. So to inform the user something is really happening I created a progress bar form that has a label describing what is happening and the progressbar that shows how far along in the entire process. Works well with 2 exceptions:

1. I cannot get the dialog to center on the parent AutoCAD window. The form object property StartPosition = CenterParent (This only works if ShowDialog() is called to open form but I cannot have this a modal form). As well I tried in code just before pbForm.Show(); as: pbForm.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; Neither works it opens right in line with the menu button I press to execute the command. (I'm not overly concerned with this issue it is just annoying depending on the size of the ACAD window and also seems affected by screen resolution)

 

2.  During execution the form label and bar update properly but the form flickers badly during the update of the form objects. Or I am assuming that is the cause of this issue?

 

Any help appreciated as I have seen a lot of old threads regarding progress bars

 

Cheers!

Rick...

 

0 Likes
3,105 Views
9 Replies
Replies (9)
Message 2 of 10

Norman_Yuan
Mentor
Mentor

It looks like you are using WinForm to show the progress bar. Here are a couple of things I want to say:

 

1. You should not use Form.ShowDialog()/Show() to show forms in AutoCAD .NET plugin. ALWAYS use Application.ShowModalDialog()/ShowModelessDialog().

 

2. Using modeless dialog would not achieve the desired visual effect (that is, the flickering you have seen). Event using the available ProgressMeter class in API would not guarantee a satisfied visual effect.

 

Using modal form would guarantee you the visual effect, where the code can force Form/Control.Refresh(). Of cause you do not want to tie your AutoCAD business processing to a modal UI. This is where an interface comes into play that effectively separate the UI and AutoCAD's long operation process, yet makes both work together. See one of my articles on this issue:

 

https://drive-cad-with-code.blogspot.com/2015/04/showing-progress-for-long-code.html 

 

Unfortunately, the video clip link in the article would not work in newer web browsers because of being flash video. But you can download the code and run it yourself to see the result.

 

HTH

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 10

rickjohnston
Advocate
Advocate

Thanks @Norman_Yuan

 

Certainly not the expected answer but I somewhat get your point.

 

The thing that is I guess confusing me is how to incorporate this into my code because this seems like a daunting task to decipher and plug into my code? I have the DLL in 1 namespace where all the heavy lifting is done and simply references the progress bar form and controls to update.

Anyway. I'll keep plugging along here... I appreciate the insight!

 

Rick...

0 Likes
Message 4 of 10

Norman_Yuan
Mentor
Mentor

Well, when you say:

"...I have the DLL in 1 namespace where all the heavy lifting is done and simply references the progress bar form and controls to update...", I assume you have structured the code well/following good coding practice that the heavy lifting code is not mixed with UI (the form with progress bar). If somehow the processing code is mixed with the form/progress bar, it is not a good coding practice, not to mention it does not provide satisfied visual effect (when using modeless form) anyway. 

 

With my sample code, the heavy lifting code only needs to implement the ILongProcessingObject interface, which only requires minimum code update (to raise processing start/progress/end) events, which would then drive the UI update. With my long time experience of doing AutoCAD .NET programming, only modal form can provide stable visual progress feedback; not a modeless form , nor ProgressMeter. Then, if you use modal form, one must separate the UI with the actual processing code, so that the UI can be re-used by any long processing operation. Hence the ILongProcessingObject.

 

In my practice, I place the form code the ILongProcessingObject code in a shared generic CAD supporting DLL project, so that it can be referenced in other projects that whenever I run into a process code that may take a few more seconds to run, I just implement the ILongProcessingObject interface in the process class, and the progress UI is there!

 

Just want to say: I have been there where you are, done what you do, and ditched modeless UI for progress bar. Still use ProgressMeter occasionally. Anything longer than 10s, I use this modal form for progress, until recently moved to better WPF UI (with similar interface logic).

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 10

rickjohnston
Advocate
Advocate

Thanks again @Norman_Yuan I really appreciate the comments and insight!

I like to think I practice good coding techniques as I as you do hate recreating code that is similar! I like to be in the "scale" not recreate...I'm lazy! 🙂 which I was told early on is the basis of a good coder! 🙂

 

I'm almost there...

 

Thanks again

Rick...

0 Likes
Message 6 of 10

rickjohnston
Advocate
Advocate

Well I am at a loss as to what is going on with the code. My heavy lifting code work without issue until I add in the UI (progress bar)? The heavy lifting code is actually for M3D and thus requires WorkflowPlugin type class. If I do not use calling the UI code works as expected with UI I get: System.NullReference Exception on Document.Map.WriteMessage("Write progress...") which is simply trying to write to command prompt for history purposes. Object reference not set to an instance of an object at Autodesk.Map.IM.Workflows.WorkflowPlugIn.get_Document(). This tells me that the using statements maybe be getting left out when UI is opened? Actually anything interacting with AutoCAD is throwing same exception as I commented out the above line to get further it fails on them like: FeatureClass gpsImportClass = Document.Connection.FeatureClasses["GPS_IMPORT"]; which is simply returning a feature class layer of objects.

 

Any thoughts? I'm simply at a loss here now.

 

TIA!

Rick...

0 Likes
Message 7 of 10

Norman_Yuan
Mentor
Mentor

Since you did not provide code to show how the processing code triggers/interacts UI, it is very difficult for others to suggest something meaningful enough, especially when your process code involves AutoCAD Map IM API, which, AFAIK, is not very widely used.

 

But at least you can try this: instead of using Document.Map.WriteMessage(....), you can simply use plain AutoCAD API's

 

MdiActiveDocument.Editor.WriteMessage(....), to see what happens. If it still cause similar error (I guess it would be very likely), that means your way of hooking up the processing execution to the UI somehow leads to null MdiActiveDocument, hence the hooking mechanism/code isn't correct.

 

Again, just wild guess without seeing the code.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 8 of 10

rickjohnston
Advocate
Advocate

Thanks @Norman_Yuan Firstly all I do is M3D coding just that never required a modal dialog do to flickering issues. Anyway, yes expecting that it is not hooked up correctly but all your code runs in the handler and I cannot do that due to requiring code to run as a WorkflowPlugin class. Unfortunately I am helping a friend here and the code is for a customer and is proprietary and cannot get their permission to share but hopefully this gives enough insight without exposing specific code. (NOTE: I was asked to try and implement a UI as this process can take an hour or so to run and there is nothing indicating to the user anything is happening and at what stage it is at (4 stages I want to report).

So 2 projects 1 "the heavy lifting code" as WorkflowPlugin class and the UI project (your original code) and is referenced project to WorkflowPlugin code. The code must be called from a Workflow command that is executed as such:

Sub Run
' Original no UI function call
'   Me.RunMethod("MyUtils.dll", "MyUtils.TheWorkflows", "ImportUpdate") 
' UI function call
   Me.RunMethod("MyUtils.dll", "MyUtils.TheWorkflows", "StartCommand")
End Sub

So StartCommand() is new in the heavy lifting file (as WorkflowPlugin class) as well as ImportUpdate() function that does all the work.

namespace MyUtils
{
    public class TheWorkflows : WorkflowPlugIn
    {

    public void StartCommand()
    {
       ProgressBarHandler pbHandler = new ProgressBarHandler();
       pbHandler.StartCommand();
    }
    public void ImportUpdate(LongProgressStarted ProgressStarted, LongProgressProgressed ProgressProgressed, EventHandler ProgressEnded, EventHandler ProgressCloseRequested)
    {
        try
        {
         ... run process ...
        }
         exception code {...}
    }

The handler code:

        public void StartCommand()
        {
            //            _processingType = LongProcessingType.Type1;

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

        public event LongProgressStarted ProgressStarted;
        public event LongProgressProgressed ProgressProgressed;
        public event EventHandler ProgressEnded;
        public event EventHandler ProgressCloseRequested;

//      Called from the interface implementation
        public void importUpdate()
        {
            TheWorkflows theWFs = new TheWorkflows();
            theWFs.ImportUpdatePoles(ProgressStarted, ProgressProgressed, ProgressEnded, ProgressCloseRequested);
//            theWFs.ImportUpdatePoles();
        }
    public interface ILongProcessingObject
    {
        event LongProgressStarted ProgressStarted;
        event LongProgressProgressed ProgressProgressed;
        event EventHandler ProgressEnded;
        event EventHandler ProgressCloseRequested;
        void importUpdate();
    }

 

I know this is difficult to follow and it is okay to not reply to this post...for now I am going to implement the status bar method of displaying something for him.

 

TIA!

Rick...

 

0 Likes
Message 9 of 10

rickjohnston
Advocate
Advocate

Hi all.

So some quick questions here as I have finally implemented a good looking progress bar during my long operations complete with switching between Blocks style and Marquee style when I do not have any way to determine how long a process will take. I've been searching intensely online and not finding any answers...

Anyway, my questions are:

1. Regarding Marquee style what controls the length of the of the scrolling bar? It appears to be by default about 30% of the actual PB length? Like so: Marquee PB.jpg

It scrolls completely across before starting again...this leads me to my next question.

2. I see many PBs using a Marquee style in ACAD that are several smaller size "blocks" that scroll and fade out at the tail as it moves across the PB control? How can this be accomplished? Like so:

PB_Blocks_Marquee Example.jpg I like this look better than above!

3. My PB as I said is working as expected with 1 noted exception. I print a label like n of nnn processed... and when label updated I set PB.Value = n and first time through PB.Maximum = nnn. Again it all looks great until the last one the label updates like nnn of nnn processed... but the PB.Value despite being equal to PB.Maximum the control never fully fills in? I have set DoEvents() forced PB control Refresh() and nothing label updates control will not but again only on the very last one. Grasping at straws I'm going to try PerformStep(); to see if this will give different results...I am skeptical though...

4. Isn't the "Blocks" style supposed to be similar to screenshot #2 of the block marquee only no tail fading? When I set to Blocks (or Continuous for that matter) there is no difference between those 2 styles?

5. Finally, I have a closing label that simply informs the user the processes (there are multiple loops) are 100% complete...I'm simply updating the label, however upon doing this and this only the PB control goes to 0 (nothing filled in anymore)? I even tried to force the PB.Value = PB.Maximum and refresh DoEvents() and again label updates control does not and why it is going to "empty" is completely baffling me. I even put a length ...Thread.Sleep(10000) to see if there is a refresh delay and that does nothing most of the time, however I have seen the control jump to 50% filled? Again makes no sense.

 

The code is very minimal and label and control are only things I am updating

 

TIA!

Cheers!

Rick...

0 Likes
Message 10 of 10

rickjohnston
Advocate
Advocate

Everyone to question #3 on PB control not updating completely on last item solution (or workaround) was to use PBControl.PerformStep() over PBControl.Value = PBControl.Maximum  the last portion of the control completely fills in now.

 

Now that said trying to resolve my question #5 like so:

PBLabel.Text = msgText;

PBLabel.Refresh();

PBControl.Step = 100;

PBControl.PerformStep();

PBControl.Refresh();

Application.DoEvents();

int val = PBControl.Value; (breakpoint here to see that the control really should be 100% filled...all values match)

// System.Threading.Thread.Sleep(5000); (commented this out as it didnt help any)

So this above code doesn't work like the long loop updates are doing...

 

Thanks again for any suggestions!

Cheers!

Rick...

0 Likes