0

Here is what I have for my login method:

    #region LoginMethod
    bool login = false;
    public async Task GetAccounts()
    {
        MainWin w = new MainWin();

        await Task.Run(() =>
        {
            this.Dispatcher.Invoke(() =>
            {
                using (SqlConnection connection = new SqlConnection(PublicVar.ConnectionString))
                {
                    gymEntities2 database = new gymEntities2();
                    SqlConnection con1 = new SqlConnection(PublicVar.ConnectionString);
                    PublicVar.TodayTime = String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(TimeNow.Text));
                    con1.Open();

                    SqlCommand Actives = new SqlCommand("Select DISTINCT (LockEndDate) from LockTable Where Username = '" + txt_username.Text + "' and Password = '" + txt_password.Password + "'", con1);
                    object Active = Actives.ExecuteScalar();
                    string SystemActive = Convert.ToString(Active);

                    //   SqlCommand Commandcmds = new SqlCommand("update VW_TimeOut set UserActive = 2 where UserEndDate < '" + String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(TimeNow.Text)) + "'", con1);
                    //   Commandcmds.ExecuteScalar();

                    SqlCommand Commandcmd = new SqlCommand("SELECT COUNT(*) FROM LockTable Where Username = '" + txt_username.Text + "' and Password = '" + txt_password.Password + "' and LockEndDate between '" + String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(Lock.Text)) + "' And '" + SystemActive + "'", con1);
                    int userCount = (int)Commandcmd.ExecuteScalar();

                    //Find Gym ID -> To Set Public Value Strings
                    SqlCommand FindGymID = new SqlCommand("Select DISTINCT (LockID) from LockTable Where Username = '" + txt_username.Text + "' and Password = '" + txt_password.Password + "'", con1);
                    object ObGymID = FindGymID.ExecuteScalar();

                    if (userCount > 0)
                    {
                        try
                        {
                            RegistryKey UsernameKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\GYM");

                            if (CheakRem.IsChecked == true)
                                if ((string)UsernameKey.GetValue("UserNameRegister") != "")
                                {
                                    UsernameKey.SetValue("UserNameRegister", txt_username.Text.Trim());
                                    UsernameKey.SetValue("PasswordRegister", Module.Decode.EncryptTextUsingUTF8(txt_password.Password.Trim()));
                                }

                            PublicVar.GymID = Convert.ToString(ObGymID);
                            login = true;
                        }
                        catch
                        {

                            w.Username = null;
                            w.Password = null;
                        }
                    }
                    else
                    {
                        ErrorPage pageerror = new ErrorPage();

                        con1.Close();
                        w.Username = null;
                        w.Password = null;
                    }
                    con1.Close();
                }
            });
        });

        if (login == true)
        {
            w.Username = txt_username.Text;
            w.Password = txt_password.Password;
            w.Show();
            this.Close();
        }
    }
    #endregion

But it doesn't work - my form is going to hang whenever I press the button.

private async void btn_join_Click(object sender, RoutedEventArgs e)
{
    await GetAccounts();
}

It doesn't work and my program is getting hung when I press the async button. What part of my method is wrong? What i actually want is the button press opening a new page, but I don't want it to open with delay... I was told to use an await method, but it still opens with delay.

V0ldek
  • 9,623
  • 1
  • 26
  • 57
Reza Pak
  • 129
  • 2
  • 9

3 Answers3

1

You hung your program by using Dispatcher.Invoke()inside your task method.

Dispatcher.Invoke() causes the code to be executed synchronously on the UI thread of WPF and doesn't return until this code is complete. At the same time, within your "async button" the code waits for the task to complete, and here is your deadlock.

You don't need Task.Run with Dispatcher.Invoke.

You should do something like this:

  1. Create and show your form.
  2. await Task.Run - get the data from the db, but don't push anything in the UI. You can return the values into a simple class and use to create a typed task.
  3. Populate the UI with the values returned from the db.

If you show more code, I can be more precise.

Nick
  • 4,787
  • 2
  • 18
  • 24
  • when i clean the Invoke i got this error The calling thread cannot access this object because a different thread owns it when i press the butten. – Reza Pak Nov 03 '18 at 09:07
  • You need to see what part of my codes? cuz its all method i have for login then i use that on async butten – Reza Pak Nov 03 '18 at 09:08
  • if You can , make that class and re-post the class method and edit this code . – Reza Pak Nov 03 '18 at 09:10
  • Make sure you _do not_ access form controls from your Task.Run code. Instead, create a class with two properties: user name and password. Return a new instance of this class from the Task with the results you need. Then, after you await this task, populate the form controls and show the form on screen. – Nick Nov 03 '18 at 09:52
  • im new , can u make that class and change the my main method? – Reza Pak Nov 03 '18 at 10:08
1

Take a look at this line this.Dispatcher.Invoke(() =>

Dispatcher.Invoke is a synchronous call, it will block your thread from running until it’s complete. Control will not return to the calling object until after the callback returns, therefore it is causing unresponsive GUI.

You may want to use Dispatcher.BeginInvoke which is asynchronous operation instead of Dispatcher.Invoke (example). Or use Dispatcher.Invoke only if you're really need to modify or updating UI contents. Example,

Dispatcher.Invoke(() =>
{
    w.Username = null;
    w.Password = null;
});
Luthfi
  • 478
  • 1
  • 3
  • 16
  • i got this error : convert lambda expression to type 'Delegate' because it is not a delegate type MYProject1 – Reza Pak Nov 03 '18 at 09:32
1

Both answers above are correct. But maybe fixing your code will make things more clear. You should separate the UI from Tasks as suggested above and as shown below. Hopefully, I don't have syntax errors since I just modified it without IDE. Basically, I changed GetAccounts to only deal with the database and let PopulateMethodAsync to deal with the UI. This means GetAccounts will be working in the background and when it is done, it will give the result to the UI section (PopulateMethodAsync).

    #region LoginMethod
    bool login = false;
    public async Task PopulateMethodAsync()
    {
        var isLoginSuccess = await GetAccounts(txt_username.Text.Trim(), txt_password.password.Text.Trim(), Lock.Text.Trim(), TimeNow.Text.Trim());

        MainWin w = new MainWin();

        if (login == true)
        {
            w.Username = txt_username.Text;
            w.Password = txt_password.Password;
            w.Show();
            this.Close();
        }
        else
        {
            w.Username = null;
            w.Password = null;
        }
    }

    public async Task<bool> GetAccounts(string txt_username, string txt_password, string Lock, string TimeNow)
    {
        await Task.Run(() =>
        {
            using (SqlConnection connection = new SqlConnection(PublicVar.ConnectionString))
            {
                gymEntities2 database = new gymEntities2();
                SqlConnection con1 = new SqlConnection(PublicVar.ConnectionString);
                PublicVar.TodayTime = String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(TimeNow));
                con1.Open();

                SqlCommand Actives = new SqlCommand("Select DISTINCT (LockEndDate) from LockTable Where Username = '" + txt_username + "' and Password = '" + txt_password + "'", con1);
                object Active = Actives.ExecuteScalar();
                string SystemActive = Convert.ToString(Active);

                //   SqlCommand Commandcmds = new SqlCommand("update VW_TimeOut set UserActive = 2 where UserEndDate < '" + String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(TimeNow.Text)) + "'", con1);
                //   Commandcmds.ExecuteScalar();

                SqlCommand Commandcmd = new SqlCommand("SELECT COUNT(*) FROM LockTable Where Username = '" + txt_username + "' and Password = '" + txt_password + "' and LockEndDate between '" + String.Format("{0:yyyy/MM/dd}", Convert.ToDateTime(Lock)) + "' And '" + SystemActive + "'", con1);
                int userCount = (int)Commandcmd.ExecuteScalar();

                //Find Gym ID -> To Set Public Value Strings
                SqlCommand FindGymID = new SqlCommand("Select DISTINCT (LockID) from LockTable Where Username = '" + txt_username + "' and Password = '" + txt_password + "'", con1);
                object ObGymID = FindGymID.ExecuteScalar();

                if (userCount > 0)
                {
                    try
                    {
                        RegistryKey UsernameKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\GYM");

                        if (CheakRem.IsChecked == true)
                            if ((string)UsernameKey.GetValue("UserNameRegister") != "")
                            {
                                UsernameKey.SetValue("UserNameRegister", txt_username);
                                UsernameKey.SetValue("PasswordRegister", Module.Decode.EncryptTextUsingUTF8(txt_password));
                            }

                        PublicVar.GymID = Convert.ToString(ObGymID);

                        con1.Close();

                        return true;
                    }
                    catch
                    {

                    }
                }

                con1.Close();
            }
        });

        return false;
    }
    #endregion

    private async void btn_join_Click(object sender, RoutedEventArgs e)
    {
        await PopulateMethodAsync();
    }

Hopefully this answers your question