AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.98k stars 2.25k forks source link

Error when call OpenFileDialog on osx and linux #1838

Closed qcmiao1998 closed 6 years ago

qcmiao1998 commented 6 years ago

When I load OpenFileDialog on osx and Ubuntu, it has some problem. I use the following code to load OpenFileDialog:

var task = Task.Run(() =>
{
    OpenFileDialog openFileDialog = new OpenFileDialog
    {
        AllowMultiple = true
    };
    var outPathStrings = openFileDialog.ShowAsync();
    return outPathStrings;
});
var filelistStrings = task.GetAwaiter().GetResult();

It works normal on Windows. But when I run it on osx, it shows you are calling a method that can only be invoked from the UI thread. And on Ubuntu, it says GtkDialog mapped without a transient parent. This is discouraged.. And they all have no more response.

I think it may be because I did not load a MainWindow. I use the following code to start the application:

 var app = new Application();
AppBuilder.Configure(app)
    .UsePlatformDetect()
    .SetupWithoutStarting();

I want to know what is the actual reason for the problem and if I can use some way to avoid the problem.

wdcossey commented 6 years ago

OpenFileDialog.ShowAsync() is already a Task and you are wrapping it in another one.

What is in the startup (Program.cs?) code of your app, give us a small sample?

qcmiao1998 commented 6 years ago

The startup code is

var app = new Application();
AppBuilder.Configure(app)
    .UsePlatformDetect()
    .SetupWithoutStarting();

I use a new task because if I run it directly, it will meet some problem. I have described it in another issue:

1837

wdcossey commented 6 years ago
    public class App : Application
    {
        public override void Initialize()
        {
            AvaloniaXamlLoader.Load(this);

            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                AllowMultiple = true
            };
            var outPathStrings = openFileDialog.ShowAsync().Result;
        }
    }

image

qcmiao1998 commented 6 years ago

Now, it has no exception print out but still cannot see any dialog in both osx and Linux. Can you share me your startup code? And if it must have a MainWindow?

wdcossey commented 6 years ago

I just edited the code from one of the sample apps.

Give me a little bit and I'll test it out on Mac OS and get back to you.

wdcossey commented 6 years ago

I can confirm that it doesn't work in Linux (with Gtk3). It appears that you can't create a GtkDialog without a valid parent.

kekekeks commented 6 years ago

You can't call file dialogs (or anything related to Avalonia, basically) from non-UI threads. It works on Windows because Win32 API allows multithreaded usage. Other platforms don't. That's why you are getting an exception.

ShowAsync returns a Task. Task is meant to be awaited, not blocked with GetResult call. When you are using GetResult() a task, you are blocking the thread. If UI thread is blocked, there is nobody to complete the task. Again, that works on Windows because of Wind32 backend implementation details. It won't work on other platforms.

Basically, it's not an issue in Avalonia, but API misuse. The code doesn't work, because it can not work.

Regarding GtkDialog mapped without a transient parent. This is discouraged. You aren't specifying the parent window for the dialog. Window manager displays it somewhere, so you are probably just don't see it. Pass the parent window to ShowAsync, it should properly display the dialog.

kekekeks commented 6 years ago

@grokys I think we should make window parameter here mandatory. Right now it's too error-prone to use, since not passing the parent window is too forgiving on Win32 which is the most common developer platform.

qcmiao1998 commented 6 years ago

In some cases, a standalone file dialog is useful for a small application written for unprofessional users. They may not type the right path in the console, a file dialog is a good choice for them. Now, I found a useful C/CPP tool to call native file dialog on multiple platforms. I have tested it on win, ubuntu and osx. Maybe people has the same need can reference it. https://github.com/native-toolkit/tinyfiledialogs

kekekeks commented 6 years ago

It's fine to pass null as window, just don't expect the dialog to show at any meaningful location (like on top of your window).