Capture view image and force screen refresh continuously

By Xiaodong Liang

A few months ago, one developer tried to change view state (such as change object color/transparency, hide some objects etc), and save the capture image file per view in a loop. This happens continuously. But, view state is not updated on screen as expected at once. e.g. the item color is set to red, but the item is still the older color in the screen (wrong), while the item in the snapshot is red (correct). He wanted to know the way to force screen refresh.

My colleaguge Toshiaki Isezaki spent some time to invesigate with engineer team and worked out the solutions. I am just writing down his achievements.

The Navisworks screen refresh happens when the UI message loop delivers a paint event. If you have some code running that sits in a loop doing lots of work (for example rendering 100 images to file) then there’s no way for the UI message loop to deliver a paint event.

In theory, there’s a couple of ways to approach this

#1. Break the process up. When the button is pressed hide everything and subscribe to Application.Idle event. When the idle event fires make one item visible and create a picture. Control returns to UI message loop after each picture created, paint event is delivered, Navisworks redraws then idle gets raised again.

#2. Manually force a redraw during the loop. Navisworks API doesn’t directly support this – you would need to use Windows level API. For example, run the message loop until all events processed or find view window and manually send it a paint message.

#3. Display the rendered image yourself. For example, add some space to the dialog and after creating the picture, load the image in and show it. This also saves having to render the image twice.

By experiment, the working solutions with Navisworks plugin are #1 and #3.

Solution #1: code should use a modeless dialog and code should look like:

OnButtonPressed

· Hide everything
· Subscribe to idle event
· DoOnePicture
OnIdleEvent
· DoOnePicture
· If this is last picture unsubscribe from idle event, run end of process code

The idle event happens when there is no more processing, so don’t do all the processing in one go.

The following is the snippet from the attached demo project. Download Capture2013

publicpartialclassForm1 : Form

{

 

    // index to record the the count

    // which has been performed with hidden

    staticint m_Index = 0;

    //the items collection to be hidden

    staticCollection<ModelItemCollection> m_hiddens =

        newCollection<ModelItemCollection>();

 

    [DllImport("user32.dll")]

    staticexternbool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);

 

    [DllImport("user32.dll")]

    staticexternbool UpdateWindow(IntPtr hWnd);

 

    public Form1()

    {

        InitializeComponent();

    }

 

    privatevoid button1_Click(object sender, EventArgs e)

    {

        m_hiddens.Clear();

 

        foreach (ModelItem item in

            Autodesk.Navisworks.Api.Application.

            ActiveDocument.Models.RootItemDescendantsAndSelf)

        {

            if (item.IsLayer)

            {

                // to make simpler, only check the item which is layer           

                ModelItemCollection hidden = newModelItemCollection();

                hidden.Add(item);

                //add this item to the hidden collection

                m_hiddens.Add(hidden);

                //perform hidden on the items

                // in this case, it means to hide everything

                Autodesk.Navisworks.Api.Application.

                    ActiveDocument.Models.SetHidden(hidden, true);

            }

        }

        //Subscribe to idle event

        Autodesk.Navisworks.Api.Application.Idle +=

            newEventHandler<EventArgs>(Idle_EventHandler);

    }

 

    staticvoid Idle_EventHandler(object sender, EventArgs e)

    {

        //get handle of Navisworks window

        IntPtr hWnd = Autodesk.Navisworks.Api.Application.Gui.MainWindow.Handle;

         

        if (m_Index < m_hiddens.Count)

        {

            // if this is NOT the last view we need to create image

 

            // make one item visible

            Autodesk.Navisworks.Api.Application.ActiveDocument.

                Models.SetHidden(m_hiddens.ElementAt(m_Index), false);

 

            // inform Navisworks screen refreshing

            InvalidateRect(hWnd, IntPtr.Zero, true);

            UpdateWindow(hWnd);

 

            //Create the picture

            ComApi.InwOpState10 state;

            state = ComApiBridge.ComApiBridge.State;

 

            stdole.IPictureDisp img;

            IntPtr hbitmap;

            System.Drawing.Bitmap bm = null;

 

            Autodesk.Navisworks.Api.Document doc = Autodesk.Navisworks.Api.Application.ActiveDocument;

            double h = doc.ActiveView.Height;

            double w = doc.ActiveView.Width;

            img = (stdole.IPictureDisp)state.CreatePicture(state.CurrentView, null, w, h);

            if (img.Handle != 0)

            {

                string strName = "c:\\Temp\\TestImages\\" + m_Index.ToString() + "_" + m_hiddens.ElementAt(m_Index).First.DisplayName + ".png";

                hbitmap = new System.IntPtr(img.Handle);

                bm = System.Drawing.Bitmap.FromHbitmap(hbitmap);

                bm.Save(strName, ImageFormat.Png);

                bm.Dispose();

            }

            img = null;

 

            //increase the index

            m_Index++;

        }

        else

        {

            //If this is last picture unsubscribe from idle event,

            // run end of process code

            Autodesk.Navisworks.Api.Application.Idle -=

                newEventHandler<EventArgs>(Idle_EventHandler);

            //clear the hidden collection, for next use

            m_hiddens.Clear();

            //reset index

            m_Index = 0;

        }

    }  

}

 

I will introduce the solution #3 in the other article because it is not only for the question in this article, but also could be a good sample to do simulation in the custom dialog.


Comments

One response to “Capture view image and force screen refresh continuously”

  1. Did you ever managed to find the time to write solution #3 ?

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading