简体   繁体   English

调用时 UI 冻结。在新的 Window 上显示

[英]UI Freezes when calling .Show on new Window

In my application, I open the MainWindow from LoginWindow.在我的应用程序中,我从 LoginWindow 打开 MainWindow。 When I call MainWindow.Show(), LoginWindow UI freezes.当我调用 MainWindow.Show() 时,LoginWindow UI 冻结。 I've tried:我试过了:

  • await Task.Run() but this won't work because I need to open the window on the UI thread await Task.Run() 但这不起作用,因为我需要在 UI 线程上打开 window

  • await Application.Current.Dispatcher.BeginInvoke but this doesn't seem to work asynchronously等待 Application.Current.Dispatcher.BeginInvoke 但这似乎不能异步工作

I must be misunderstanding something fundamental about how async programming works.我一定误解了关于异步编程如何工作的一些基本知识。

public async void LoginAsync(object pbx)
    {
        VisLoginLoading = Visibility.Visible;
        var passwordBox = pbx as PasswordBox;
        string enteredPassword = passwordBox.Password;

        string cmdQuery = $"SELECT USERNAME, PASSWORD, AccessLevel, ALERTS, STORES FROM Tbl_UserAccounts WHERE USERNAME = @Username";

        try
        {
            string errorMsg = await Task.Run(() =>
            {
                using (SqlConnection con = new SqlConnection(PosConString))
                using (SqlCommand cmd = new SqlCommand(cmdQuery, con))
                {
                    cmd.Parameters.AddWithValue("@Username", SqlDbType.NVarChar).Value = Username;
                    cmd.CommandTimeout = 30;
                    con.Open();
                    SqlDataReader reader = cmd.ExecuteReader();

                    if (reader.HasRows)
                    {
                        while (reader.Read())
                        {
                            string password = reader.GetString(1);

                            if (DoesPasswordMatch(password, enteredPassword))
                            {
                                UserAccount.Username = (reader[0] == DBNull.Value) ? null : reader.GetString(0);
                                UserAccount.Access = Enum.IsDefined(typeof(UserAccess), reader.GetInt32(2)) ? (UserAccess)reader.GetInt32(2) : UserAccess.Unknown;
                                UserAccount.Alerts = (reader[3] == DBNull.Value) ? false : reader.GetBoolean(3);
                                UserAccount.Stores = (reader[4] == DBNull.Value) ? null : HelperMethods.ReturnWordList(reader.GetString(4));
                                return String.Empty;
                            }
                            else
                            {
                                return "Password is incorrect";
                            }
                        }
                    }
                    else
                    {
                        return "User does not exist";
                    }
                }

                return "Failed to login";
            });

            if (String.IsNullOrEmpty(errorMsg))
            {
                LoadApp();
            }
            else
            {
                MessageBox.Show(errorMsg);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show($"Failed to login\n\n{ex}");
        }

        VisLoginLoading = Visibility.Hidden;
    }

    public async void LoadApp()
    {
        VisLoginLoading = Visibility.Visible;

        await Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                MainWindow mainWindow = new MainWindow();
                mainWindow.Show();
            }));

        foreach (Window window in Application.Current.Windows)
        {
            if (window.Title == "Login") window.Close();
        }
        VisLoginLoading = Visibility.Hidden;
    }

It's logically, that GUI thread freezes.从逻辑上讲,GUI 线程冻结了。
Look, you start a long running operation on GUI thread with Application.Current.Dispatcher.BeginInvoke .看,您使用Application.Current.Dispatcher.BeginInvoke在 GUI 线程上开始长时间运行的操作。 So all other "user" of this thread have to wait until it's available, to handle their(LoginWindow) operations on it.所以这个线程的所有其他“用户”必须等到它可用,才能处理他们的(LoginWindow)操作。 What you can do in this situation?在这种情况下你能做什么?

You can start another GUI thread, which will take care about new Window:您可以启动另一个 GUI 线程,它将处理新的 Window:

public void LoadApp()
{
    VisLoginLoading = Visibility.Visible;

    var thr = new Thread(() =>
    {
        var mw = new MainWindow();
        mw.Show();
        mw.Closed += (sender, e) => mw.Dispatcher.InvokeShutdown();
        System.Windows.Threading.Dispatcher.Run();
    });
    thr.SetApartmentState(ApartmentState.STA);
    thr.Start();

    //Move this logic to the MainWindow, after initialization is done.
    //foreach (Window window in Application.Current.Windows)
    //{
    //    if (window.Title == "Login") window.Close();
    //}
    VisLoginLoading = Visibility.Hidden;
}

Now you can start a MainWindow , but you have to solve another challenge (I think) - notify VisLoginLoading the initialization is finished.现在您可以启动MainWindow ,但您必须解决另一个挑战(我认为) - 通知VisLoginLoading初始化完成。 But it's another question.但这是另一个问题。 So your UI will not freez.所以你的用户界面不会冻结。
See also Threading Model.另见线程 Model。

Update:更新:

In MainWindow use:在 MainWindow 中使用:

Application.Current.Dispatcher.BeginInvoke((Action)(()=>
{
    foreach (Window window in Application.Current.Windows)
    {
        if (window.Title == "Login") window.Close();
    }
}));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM