In the application I'm working on, I've been trying to use the SaveFileDialog and OpenFileDialog for their customary purpose (saving and opening files.)
However, the application can't support modal forms/dialogs without crashing because too many time-sensitive things happen on the thread that owns the UI.
Rewriting the application to move those features off the UI-owning thread is not practical in the immediate term. To finish the feature I've been working on, I need a substitute for the two file dialogs, preferably a control that can live on a non-modal form.
Is there anything out there I can use that won't block my UI-owning thread?
too many time-sensitive things happen on the thread that owns the UI.
That is your real problem. .Net has very robust multi-threading support. I suggest you move these things elsewhere, unpleasant though it seems. It's probably less work than re-implementing the Open/Save dialogs and definitely will be less for your app in the long run.
The only existing option I know of is Dialog Workshop.NET, a commercial product. They have a set of dialogs that have the option of being modeless (or embedded directly into a windows form instead of a separate window).
However, I'd really think about trying to move your time sensitive logic into a separate thread, instead. Having a modeless dialog will potentially confuse users, since it will not behave the way a file dialog is supposed to behave. There are also other potential consequences to having a non-blockable UI.
Related
I'm developing a Revit add-on which performs some lengthy tasks. During the process, I want to display a simple WPF window with an indeterminate progress bar, a label to inform about current process and a button to enable aborting.
I already tried the most obvious ways of accomplishing that: creating a WPF window inside the add-on and displaying it, but the problem is that the UI gets frozen, no matter how I implement this. During some processes, the whole Revit UI gets frozen/white so I really wouldn't expect my embedded WPF window would behave normally in these conditions anyway.
The workaround I figured out was to have the WPF window as a separate app (EXE file) I could run from the add-on. I based my implementation on this example .
The good part of it is that it doesn't hang no matter what is happening with Revit.
The bad part is that the sequence of how Windows is queuing the calls of my separate WPF app is sometimes different from the sequence of these calls from my add-on. It sometimes results in a situation when the Revit process is over but the WPF window is still displayed (waiting for the final, closing call which had been apparently already executed, but then the app got reactivated with another, delayed call).
Preferably I would like to handle the WPF app the same way as you can i.e. handle an Excel application from .NET. You create an ExcelApp object, do what you want with it and dispose of in the end.
The problem is I don't have a clue of how to do this.
How should I expose the WPF app's API to my add-on?
Could it be possible to have the WPF app responsive and controlled from the Revit add-on at the same time? (user can still click the abort button, the indeterminate progress bar doesn't freeze)
The First thing to know is about interacting between two processes. there are some Standard approaches:
Interacting through Socket (Socket Programming)
Using Named PipeLines (Useful when your messages aren't so long)
There are some other predefined Libraries based on above techniques. Using a FileSystem Based method is not a reliable way to proof the outputs.
This was a part of your solution. The next step is to use Threading in your WPF application. I'm not familiar to Revit and I don't know how it works.
UI freezing is normal in a long running process. because UI is busy and it can't answer your requests (e.g Mouse Move, Click, ...). So using a Thread you can put your long running process into a separate place and wait for the response at the end of it.
There is a problem while using a Thread. Because you left your UI and started your long running process on a separate Thread, you can't directly access to your ProgressBar. In this situations you have to use ThreadDispacher. It's not a terrifying concept, it just a three line of codes that will adds to your callings.
for example:
Dispatcher.Invoke(() =>
{
ProgressBar.Value++;
});
Search for a Library to doing your IPC (Inter Process Communication) to get the result faster (or you can learn about above techniques to do it by your means) and next add a simple thread to your WPF application so you be able to Start, Pause and resume the running job based on the situation.
I am a fairly experienced WinForms developer. I have an MdiApplication that used to work well. However, recently the main shell of the application, for which we use ComponentOne RibbonForm, has been updated in a big way. This update did affect some of our other 3rd party components, which we established was due to ComponentOne's use of DoEvents() in their event code. I thought I had cleaned up all of the code causing problems but I now have found another...
When I have multiple MdiChildren open and select one of these in code from an button click event on the ribbon form via
document.Activate();
document.EditorControl.Select();
document.EditorControl.Focus();
the other open MdiChildren documents still have focus, that it the forms are highlighted and input is not set on the document I set in code. Two questions:
How can I ensure that the Form I want to make active is the only one that is active?
Linking to the above; setting one form as active using form.Activate() should deactivate the others MdiChildren, but it is not - how can I deactivate the other windows in code?
Thanks for your time.
[Too long for a comment]
I am sick to the hind teeth with fighting C1. Esp. the Ribbon. I have confirmed with their support that they do use DoEvents() which they use to yield on their Gui threads. I am now going to switch to DevExpress which should be straight forward for my MVC application...
C1's use of DoEvents() messes up the normal flow of your application. DoEvents() is asynchronous which means it terminates before the application has actually processed any outstanding events, so if you're using it in a procedure with many sequential statements, calling DoEvents() causes a huge disturbance whenever it's called. This is what I think we are seeing when we perform our MDI operations, but we can never be sure without the C1 source code.
I hope this helps.
I have something working but I'm not sure it is OK and if it will cause problems later on.
I have a Win32 C++ app which does some video processing and uses Direct3D for display (not my code). My task is replacing the GUI with a C# GUI.
I've made the C++ code into a dll.
I've created a GUI in C# and have a panel intended for the video-display. I pass this panel's HWND to the dll, and everything seems to work just fine.
But - that panel is being accessed by a thread other than the one it was created on, which shouldn't be done.
Part of me wants to "leave well enough alone" but this feels wrong and I suspect it will cause problems down the line. Such as when we want to catch click-events on the panel, which will be on the main or UI thread ... which created the panel.
ANy suggestions on how I should be doing this?
Thank you
Thanks
I think you already know the answer to this. Windows have thread affinity. All operations on a window should be performed from the thread that created it.
Given this rule, I can't imagine that the DLL is responsible for moving window access onto a different thread and presume it is your code that does it. The solution is for you to create and access the window in the same thread that calls the DLL.
The main problem I've seen with this technique is that your UI can flicker. If you are early enough in the process to use WPF you may want to take a look at D3DImage if not what you are doing now will mostly work.
How to show messagebox dialog which will not allow user to switch to another window as long as that dialog is not closed like shutdown dialog in windows XP using VB.NET or C# windows application
You can't easily prevent interaction with other applications even from a system-modal message box.
One option is to display a large transparent window behind your message box with the WS_EX_TOPMOST window style. That way it would appear that the other windows are interactive, but clicks would hit your transparent window instead.
You couldn't prevent Control+Alt+Delete though and you'd have to take extra steps to prevent Alt+Tab and such. Also other topmost windows could still compete for the top.
In other words, it's a pain to do and for good reason. As Raymond Chen would say, you may have the most awesome and important application in the world but if it were easy then all of the other applications that aren't as awesome and important as yours would be able to abuse it.
What you're looking for is called a system-modal dialog. This is in contrast to the more typical application-modal dialog, which only prevents the user from doing anything else in your application until they dismiss the dialog. A system-modal dialog extends this prohibition to the entire system and prevents the user from doing anything else at all with their computer until they've dismissed your dialog.
This was possible under 16-bit Windows (versions 3.x and earlier), but this functionality was removed when 32-bit Windows rolled onto the scene (as far back as Windows 95 and NT 3.5). Presumably, there were some vaguely more technical reasons that this capability was now denied to application programmers, but its absence also meant the end to widespread abuse of this feature by developers who thought their application was the only important thing the user could possibly be doing on their computer. (Some of those "vaguely more technical reasons" are related to better support for multitasking and the obsolescence of the "one program—one focus" paradigm.)
Raymond Chen answers the question definitively in a forum post made to this thread:
Win32 doesn't have system modal dialogs any more. All dialogs are modal to their owner.
If you want to simulate such functionality now (and it's highly recommended that you not do so, because it wasn't good programming practice before, and it's particularly alien to users now), you'll have to rely on a hack. This means your solution won't be fool-proof and could be easily bypassed by a knowledgeable or experienced user.
My recommendation is to seriously re-consider your need to prevent the user from switching to another application while a dialog box is visible in your application. System-modal dialogs are a contradiction with today's modern multitasking environments, and there are only extremely limited circumstances where they make sense. Most of those circumstances are limited to the operating system (the shutdown dialog from your example, UAC prompts from Windows Vista/7), rather than individual applications. See if you can't settle for the expected and less user-hostile application modal dialog instead, which you can get easily in C# and VB.NET using the ShowDialog method.
I am attempting to write a specialized onscreen keyboard (OSK) for an application I'm writing in C#. To facilitate this, I've created a form which has several buttons on it representing keys, and clicking them calls SendKeys and sends out the appropriate keys.
This form is owned by the main window that is shown when the application first starts, using the Owner property. This way, the OSK pops up whenever the user focuses the application, and it stays on top of the main window if it said main window is dragged over it.
This all works great, but because I have modal dialogs that I also want to use with the OSK, I attempted to create it in a separate thread, complete with its own message loop (via Application.Run) so it could still be usable with any modal dialogs in the main thread.
The problem with this is that, obviously, being in a separate thread can cause InvalidOperationExceptions because of cross-threaded calls. One specific example of this is when calling Application.Run(osk) from the new thread, a cross thread error occurs because it is attempting to update the window's handle with the owner (the main window).
My question is, is it possible to have an owned form on a thread that is separate from the owner in a safe manner? And, if failing that, is it possible to emulate an owned form's characteristics (namely being Always On Top for only the main window, and popping up when the main window is focused)?
Thanks, and sorry if this is confusing.
I think this is actually a bug in Windows Forms. Somewhat inevitable due to the way it checks for access to the Handle property by the wrong thread. The SDK docs for SetParent are not explicit about it, it states the requirement is that both windows belong to the same application. No mention of having to belong to the same thread. I know for a fact that the 'same application' requirement is not hard, there's appcompat code in Windows that makes this work for windows from different processes. Adobe Acrobat ab/used this for a long time. Which definitely absolves the 'same thread' requirement.
Well, punt the problem and try it out. Set Control.CheckForIllegalCrossThreadCalls to false before you set the owner, back to true afterward. And test the heck out of it. If you have trouble then try pinvoking SetParent() directly instead of setting the Owner. Windows Forms actually uses SetWindowLongPtr which is not recommended by the SDK.
I'll take a stab at this. Try running the OSK as a separate process.
Try using ShowDialog for the OSK as well instead of Application.Run - ShowDialog creates a message loop and ends it when the window is closed, and perhaps will solve your problems.
new Thread(() => new OSK().ShowDialog());
Why not use Control.Invoke to make cross-threaded calls to avoid InvalidOperationException?