how to keep WPF window refreshed and responsive

how to keep WPF window refreshed and responsive

adam.krug
Advocate Advocate
3,772 Views
7 Replies
Message 1 of 8

how to keep WPF window refreshed and responsive

adam.krug
Advocate
Advocate

Is it possible to show and keep refreshed a WPF window, with indeterminate progress bar and an "abort" button while Revit is processing an external command?

 

Now I'm in a situation where the window is frozen, the bar is not animating, I can't click on abort because the cursor is busy, the UI is not responding.

 

I use .Show() method to display it.

0 Likes
Accepted solutions (1)
3,773 Views
7 Replies
Replies (7)
Message 2 of 8

RPTHOMAS108
Mentor
Mentor
Accepted solution

With WPF you can still call

 

Windows.Forms.Application.DoEvents()


That will update the window. It's the most straightforward way, although I've never liked calling it repeatedly but it is the suggested way and it works.

 

Elsewhere outside of the API you would start the window on a separate thread, get the dispatcher of that window and invoke a method to update the WPF window indirectly but that would require the separate thread.

 

I actually have a separate progress bar executable which starts a filewatcher looks for an xml file and then uses that to update the progress. You can even communicate the other way i.e. when abort button is pressed it creates an XML file containing GUID which the Revit API process is always on the lookout for within each iteration. To be fair there is a bit of a lag in this approach.

 

 Also explored pipes but that again realistically requires separate threads to keep the stream alive. 

Message 3 of 8

adam.krug
Advocate
Advocate

Thanks for a quick reply!

 

DoEvents() seems the easiest way, although maybe not the most elegant.

 

I was actually thinking of having a similar approach, as a last resort - a separate app with just the progress bar. From Revit API I would only make it visible and hide it again, but I wasn't sure how to handle the abort.

 

Is my understanding correct - you just use an XML file for the communication between Revit and the progress bar? If you click abort, then you change some value inside XML and Revit API is checking that value on every iteration. Except the I/O lags, are there no problems with "file is used by another process" exceptions or other with this approach?

0 Likes
Message 4 of 8

RPTHOMAS108
Mentor
Mentor

To avoid file lock I use multiple xml files with name generated from GUID's then at the end and start of the process I clear them up.

 

For stop instruction I include a GUID value within a field of the xml files that the progress bar reads for progress. If abort is hit then the progress bar executable outputs an xml file containing this GUID. Since the Revit process originated this GUID it knows what to look for and that the occurrence of it is to stop the process. So you check for it during each iteration on the Revit side. You must include the GUID so that the files left behind for any reason in previous runs are ignored by Revit (i.e. the Revit process is looking for stop instruction with specific GUID it generated at start of current process like a key). Otherwise it could just stop immediately if it reads a previous stop instruction left behind.

 

My experience is that you should use .DoEvents instead, far easier and definitely more responsive. There used to be a problem with Revit in that it would crash at the end of a long running external command if a window was shown at the end. Therefore I still use this approach to show a log display at the end to avoid this problem. I believe the issue has now been rectified so there is no reason to do it but I've not extensively verified that myself.

 

 

Message 5 of 8

adam.krug
Advocate
Advocate

Many thanks for your explanation!

0 Likes
Message 6 of 8

mwagnerfrey6
Contributor
Contributor

@RPTHOMAS108 

 

I'm doing a similar thing with a FileSystemWatcher and was hoping you could explain your process a little more. I have a WPF window that relies on shared parameters so I am watching the shared parameters file and updating the window when changed are made. It all seems like the event is firing and the viewmodel is updated but then my window takes about 10s to show the changes. It is not unresponsive or frozen during this time and I am performing very small tasks, which do implement INotifyPropertyChanged.

 

I am thinking it is related to Revits "Idling" maybe? I am currently trying to create my window on Revit's UI thread to hopefully reduce the need for threads to coordinate but I'm sure theres an easier way. Calling "DoEvents()" did not work in my case unless I did so improperly.

 

Thanks,

0 Likes
Message 7 of 8

RPTHOMAS108
Mentor
Mentor

I think the example I noted above was different in that the progress bar window was contained in a separate executable so had complete separation from Revit.

 

It was for a batch printing operation so after each view was done Revit would drop an xml file into a temporary location and the progress bar executable would monitor that using a file system watcher. It would then update it's window controls based on the content of that latest file.

 

Going the other way the cancel button on the progress bar executable would drop a file into the temporary location with a cancel flag and Revit would check for this on each iteration i.e. start of next view to process.

 

It was a bit of an extreme solution created due to an issue I was having at the time.

 

What you have sounds like a binding issue. When you use INotifyPropertyChanged do you raise the event with the property name when you make changes that affect that property? 

 

 

0 Likes
Message 8 of 8

mwagnerfrey6
Contributor
Contributor

@RPTHOMAS108 

Thank you very much for the speedy reply. I did end up figuring out the problem.

 

Because I had some issues with thread communication I implemented what was effectively a ConcurrentObservableCollection I found online, which did fix my issues at that moment and got event to fire. However it turns out that it was responsible for the delay. I'm still not exactly sure why, and it's Clear() method did work instantly, but Add() seemed to be waiting for some "push" that Revit gave it after 10s.

 

I went back to a regular ObservableCollection and used MyWindow.Dispatcher.Invoke() which ended up working.

0 Likes