Creating a blocking drop in replacement popup like DisplayAlert() for Maui - c#

I'm updating a very large Windows Forms app to .Net Maui to make it into a cross platform app. Ideally, I would have the time to rewrite everything, but I don't. The client wants it yesterday. I'm already using a ton of the allotted time replacing the Windows Forms style interface with the XAML version. I've tried not to tinker too much with the code since the app was behaving flawlessly. Still, I've had to make a lot of changes because the Maui controls aren't always accessed the same ways as Windows Form's controls. Almost all of the methods and functions in the original app are synchronous. But some of the .Net Maui functions that I use to replace them are asynchronous. One function that is giving me a lot of trouble is the awaitable DisplayAlert(). Execution moves right onto the next statement in the code without waiting for the alert to be displayed or the user to respond. The function can be made awaitable, but to do this, the entire method within which it appears must be marked Async, which creates its own problems. There are literally hundreds of calls to The Windows Forms version of DisplayAlert(). Any solution that isn't basically a drop in replacement is going to cost tons of time.
I've been trying to create a blocking popup function that waits for the user's response before executing the next line of code within a synchronous block of code. In the XAML markup I've defined a popup "panel" with a frame, two labels and 3 buttons. Ideally, the code behind should be a function with the same parameters and behaviors as DisplayAlert() except that it blocks. It should display the panel, wait for a user response (button click/press), hide the panel, and finally return the text string of the button that was clicked...all within the same function.
Using simple method's like while loops to determine if a button was pressed block the thread from detecting that a button was press. Moving the panel display to a different thread than the button press, does nothing since the panel can only be displayed on the main thread. Invoking on the main thread has not been successful...at least not the way I wrote the code. I'm hoping someone can provide a very simple example of a blocking DisplayAlert() function that can be used within a non-async method.

Related

How to keep UI unlocked while loading child screens in winform

I have very generic question. I tried google it but couldn't find anything good.
So the question is: I have winform application where based on option selected from menu bar, child form/view is loaded and these child forms are used to display reports.
in order to generate report, these child form contains report selection criteria like date, name etc and these value comes from DB. based on values available in DB, we add controls in the view.
Problem comes when screen takes lots of time to load, it freeze UI/application during loading that I don't want.
I also tried run tasks async but since it is using view during the processing, this option failed.
ThreadPool.QueueUserWorkItem(delegate { method.Invoke(presenter, results); });
presenter is my viewpresenter and results is parameters to invoke it. application concluding presenter name at run time and calling its default method using methodinfo.invoke().
Is there any other way to achive the same.
Point: I cannot redesign the whole application to separate out complete DB logic from UI logic as it is legacy application and this might break the application.
If "freezing" is the problem, you could use doevents, see here: Use of Application.DoEvents()
Or, you could go the really old fashioned, vb6 way: run the code that freezes your UI synchronously, but from a modal window shown above your ui. You can event present the user with the progress indicator.

How do I know when all controls have loaded and displayed?

What is the real order of events in a Windows Forms application?
What I mean is, when I put code in the Form_Shown event, I expect that code only to run after the form has been Shown:
verb (used with object), showed, shown or showed, showing. 1. to cause
or allow to be seen... - http://dictionary.reference.com/browse/shown
But the Form_Shown event is a little misleading. If I do some heavy stuff inside that event, it seems as though the code gets executed before the Form has finished been shown. Let's say that I have a MainMenu, a small Toolbar and a TextBox on a form.
I want to do some heavy stuff (nevermind threads and workers for now...), so the last event I can use I would think would be Form_Shown. So I put my heavy code in there, but when the Form begins to display, I end up waiting ~ 5 - 6 seconds for the Toolbar and stuff to display (which ends up happening after my heavy code does its thing.
Which leads me to believe that I'm subscribing to the wrong event. I don't want the Form_Shown event at all. What I really need is:
Form_WhenALLTheThingsHaveShownEventHandler event.
So, how can I know _when all the things (controls) have been fully loaded and displayed?
The Shown event is in fact the last event related to initialization that is raised. However, note that the actual rendering (drawing on-screen) of UI objects in Windows (and on other platforms) is deferred. The creation of a UI object merely allocates all the necessary resources and "invalidates" the visual area of the object. The platform then later schedules the rendering event (in unmanaged Windows, this is WM_PAINT, in the Winforms API this would be the Paint event for a Control instance).
The rendering event cannot be dispatched until the UI object's thread is available, and if you have long-running code in the Shown event, that will keep the UI object's thread unavailable for the duration of your code. I.e. nothing gets drawn until your code completes.
There are other events that you could use to more reliably detect when things have "settled down". For example, the Application.Idle event tells you when the main application thread is about to enter the idle state. Alternatively, you could just subscribe to the form's Paint event. In either case, you would want to use BeginInvoke() to dispatch your long-running code, so that you don't block the handling of those events.
Now, all that said: you really should not be performing any long-running work in the UI thread, period. Using either of the above events doesn't solve the underlying problem; it simply delays the problem until after the initial rendering of your UI. The UI will still remain blocked while your long-running work is executing, and frankly the user may actually find it preferable for there to be no UI at all, than for there to be something that looks like they can interact with but which they can't (i.e. is unresponsive to their input).
In the latest version of .NET, there are some very nice mechanisms available for shifting long-running work to background threads, so that the UI can remain responsive. See Task and the async and await keywords in C#. You could instead use the older BackgroundWorker object to accomplish the same, if you prefer.

GUI does not redraw while stepping in Debug Mode

In any .NET 2.0 Winforms application, if you run the code in Debug Mode, and you either hit a breakpoint or otherwise Step Into the code line by line, and you want to see the GUI of the application being debugged, then .NET does not draw the application screen.
For example, I have an application which writes some messages to a TextBox on a Form. When I debug the code step by step, or when a breakpoint is hit, I want to see what all messages are logged in the TextBox, but if I press Alt-Tab to switch from VS2005 window to the WinForms application window, all I see is white color. The form is not redrawn, until you press F5 in the debug mode in VS2005.
What is the reason for this, and is there a way to overcome this, without introducing any threads in the code?
What is the reason for this
While you're debugging, you're effectively blocking the UI thread - you're manually stepping through its execution flow. The UI thread can't draw the UI while you're stopping it from executing.
and is there a way to overcome this
You could try calling Application.DoEvents() manually, but I'd generally recommend against it.
It would be better to just wait until you got to the end of the method and let the UI redraw itself normally. If your method is very long (in terms of time) then bear in mind that when not debugging, the UI still wouldn't be able to update itself while that method is executing. This may cause you to change your design (it's hard to tell just from the information we've got at the moment).
The reason for this is because you can only have one UI thread, and when you enter your method that updates that code that code begins blocking the UI thread. It will not update until your method exits.
Here is a good SO on message pumps, which are what drive the UI updates
You should be able to use Add Watch/Quick Watch to look at any values at the time of debugging. This sounds like what you are really looking for, anyway.
Like everyone else has said in answers and comments, the UI thread is blocked so it cannot be redrawn.
However, if all you want to do is see the GUI, and not interact with it, and you are running Windows 7/8 (which sounds unlikely since you're using VS2005) and haven't disabled aero peek, you can mouse over your application in the task bar and Windows will show the preview thumbnail. When you mouse over the thumbnail, you can "peek" at the application even when the breakpoint is blocking the UI thread.

1 second white screen while a winForm is loading with a picture in PictureBox

There is a form. On the form there is a pictureBox docked on all form's surface.
When app starts, for a second a form with white surface is displayed, then
the picture is shown.
how can i get rid of that 1s white form?
Sounds like you are doing something time consuming in form_Shown event. Call Form's Refresh() method as the first thing in form shown -event and it will first draw the form, then do the time consuming things
You have not stated when you are loading the picturebox with your image. But I would try making your picturebox visible at the end of your Form_Load event or in your Form_Shown event.
Sounds more like a threading problem to me. I guess that your UI thread is doing too much work and cannot update the UI often enough.
Do all of the following:
Make sure loading and processing any data (including the images) is NOT located in the constructors.
Move that code into the appropriate FormLoad() event handler methods.
Implement loading of the images so that it runs a separate thread.
You can find some advice in this MSDN article: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads
If you are working in a .NET 4.x version, you can also use the Task Parallel Library to make working with multiple threads easier.
The upcoming .NET 4.5 also offers the even more comfortable await and asyc keywords: Asynchronous Programming with Async and Await.

Creating a Form and using Form.ShowDialog on a background thread

Using Winforms,
If I'm on a thread that is not the "main" ui thread can I safetly
Create a Form,
Call ShowDialog on that form
It seems like I would be obeying the rule of:
Winforms controls must be modified on the thread they were created in.
From some of the reading I've done it seems like ShowDialog will create its own message pump so that I don't need to worry about the Form being created on a thread that already has a message pump.
The background of the question is that I get a callback where I'd like to display some Winforms UI, but I don't have access to any other UI that I could use to Invoke to the main thread.
That's roughly correct, albeit that it is pretty important that you call the thread's SetApartmentState() method to switch the thread to STA. Important for many UI operations, including the clipboard, drag and drop and the shell dialogs. And that you usually have a crummy Z-order problem when the form that you create on the thread is not in the foreground and hides behind another window. Or has the nasty habit of actually do move in the foreground when the user doesn't expect it and grab a mouse click or keystroke unexpectedly.
These are hard problems to fix, they do make your app flaky. There's no good reason to not have a reference to invoke to, you can also pass it to the class some way some how. You've always got Application.OpenForms[0] to fall back on, if really necessary.
Yes, you can do that, but if you want the dialog to actually act like a modal dialog (i.e., block the parent Window, which I assume you want since you are calling ShowDialog) then be prepared to be disappointed.
What problem are you actually trying to solve here. It sounds like you want a modal dialog that doesn't block, which is a bit strange. If you explain the problem at hand there may exist a solution you have not yet considered.

Categories

Resources