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.