Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programmatically closing a dialog #2633

Open
UCIS opened this issue Mar 10, 2024 · 2 comments
Open

Programmatically closing a dialog #2633

UCIS opened this issue Mar 10, 2024 · 2 comments

Comments

@UCIS
Copy link

UCIS commented Mar 10, 2024

I used a Dialog as a progress/busy indicator for some long-running operations in an application. The Dialog is displayed using ShowModalAsync, locking the parent Form/Dialog, while allowing the application code to start the long running operation and eventually Close the dialog and wait for the ShowModalAsync task to complete and Dispose the form. This seems to work fine sometimes, but breaks down when the operation completes synchronously.

I have tried to wait for the OnShown event to be raised before calling the Close/Dispose methods, and while this seems to work for Wpf it does not seem like a reliable solution, causing different effects on Gtk and possibly not working at all on Mac (#2254).

Is the use of a Dialog as a programmatically controlled progress/busy indicator acceptable? Would there be a better approach to achieve the desired behavior?

Expected Behavior

The dialog opens, blocking the main UI, and closes immediately after, or does not show at all.

  • A Dialog that is asynchronously initialized after Close or Dispose has been called, should just abort the initialization or immediately close after initialization

Actual Behavior

  • On Wpf: I have seen a NullReferenceException and an InvalidOperationException stating that the window was already closed. This error happens on the next UI iteration (RunIteration/MessageBox/main UI loop).
  • On Gtk: If Dispose is used, a NullReferenceException occurs on the next UI iteration. If Close is used, the Dialog Shown event is raised before the dialog is actually visible. A subsequent MessageBox appears first, and then the Dialog appears, raising another Shown event. The dialog does not close.
  • On XamMac/Mac64: Using Dispose results in a NullReferenceException on the UI thread; using Close leaves the Dialog open and manually closing the Dialog results in the main window still being blocked, using Close via AsyncInvoke has no effect.

Steps to Reproduce the Problem

  1. Run the test code below and click the button in the form that appears.
  2. Enabling the form.Close call and running on the Wpf platform produce slightly different results.
  3. (Scheduling the form.Close call on the UI thread seems to work for now)
using Eto.Forms;
using System;

namespace Test {
	class Program {
		[STAThread]
		public static void Main(string[] args) {
			new Application(Eto.Platforms.Gtk).Run(new TestForm()); return;
		}
		class TestForm : Form {
			public TestForm() {
				Content = new Button() { Command = new Command(ButtonClick) };
			}
			async void ButtonClick(object state, EventArgs e) {
				var form = new Dialog();
				_ = form.ShowModalAsync(this);
				//Application.Instance.AsyncInvoke(form.Close);
				//form.Close();
				form.Dispose();
				MessageBox.Show(this, "hi");
			}
		}
	}
}

Specifications

  • Version: 2.8.2
  • Platform(s): WPF, Gtk, XamMac2, Mac64
  • Operating System(s): Windows 10, Mac OS 13.1
@bow-meow
Copy link

bow-meow commented Dec 19, 2024

why would you not await the ShowModalAsync? Why not just use ShowModal?
not awaiting a UI element and then disposing of it while its still trying to build that UI element would of course cause issues.

@UCIS
Copy link
Author

UCIS commented Dec 19, 2024

why would you not await the ShowModalAsync? Why not just use ShowModal?
I am using the modal as a busy/progress indicator, so I need to start other processing once the modal is visible. Awaiting ShowModalAsync or calling ShowModal would block the thread until the user closes the window, and not allow for other work to be done.

not awaiting a UI element and then disposing of it while its still trying to build that UI element would of course cause issues.
Partially due to #2254 there does not seem to be a reliable way to find out when the UI element is ready and can be disposed of. I have tried to effectively run the work in/after the OnShown callback, but that did not yield reliable results either.

For comparison, Windows Forms allows to attach a form to a parent, show the form in a way that blocks only while the UI is being built and allowing the form to be disposed of immediately after.

Largely due to there being no reliable callback/event it seems to be impossible to close a modal immediately after its creation, regardless of whether the call to ShowModalAsync is awaited or not. Eto Forms could also handle this attempt better by either aborting the UI construction or scheduling its destruction if Dispose/Close is called before the UI element is fully constructed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants