【问题标题】:Why do I have to click twice to execute method in Label MouseDown event为什么我必须单击两次才能在 Label MouseDown 事件中执行方法
【发布时间】:2021-12-04 18:38:19
【问题描述】:

我正在为大学做作业,要求我必须使用多线程,现在每次登录时,我都必须单击两次才能更改主窗口的 UI。知道我可能做错了什么吗?

如果我使用 user_login 方法中的代码,它可以正常工作,UI 更新很快,但是当我使用多线程时,我必须单击两次标签才能更改我的 UI。

我使用按钮控件做了同样的事情,但对于上面给出的两个测试也有相同的结果。

        private void tbLogin_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //Assign Class Property Values
            login.Student_Email = txtstd_Email.Text;
            login.Student_Password = txtstd_Password.Password;

            Thread user_login_thread = new Thread(() => User_Login(login.Student_Email, 
                                                                   login.Student_Password));
            user_login_thread.Start();

            if (login.UserLoggedIn)
            {
                foreach (Window window in Application.Current.Windows)
                {
                    if (window.GetType() == typeof(MainWindow))
                    {
                        //Change page on login           
                        (window as MainWindow).frmView.Source = new Uri("Views/Dashboard.xaml", UriKind.Relative);
                    }
                }
            }

            user_login_thread.Join();

            if (chkRemember.IsChecked == true)
            {
                Properties.Settings.Default.Student_Email = login.Student_Email;
                Properties.Settings.Default.Student_Password = login.Student_Password;
                Properties.Settings.Default.Save();
            }

        }
private void User_Login(string email, string password)
{
    //Security object
    Secure security = new Secure();

    conn.Open();

    string sql = "SELECT Student_Number, Student_FullName, Student_Email, Student_Password FROM 
                 Student_Data WHERE Student_Email=@Email";

    using (SqlCommand cmd = new SqlCommand(sql, conn))
    {
        cmd.Parameters.Add("@Email", System.Data.SqlDbType.VarChar, 55).Value = email;
        cmd.Parameters.Add("@Pass", System.Data.SqlDbType.VarChar, 55).Value = password;

        SqlDataReader reader = cmd.ExecuteReader();

        if (reader.Read() && 
           login.Student_Password.Equals(security.Decrypt(reader["Student_Password"].ToString())))
        {

            login.UserLoggedIn = true;
        }
        else
        {
           _ = MessageBox.Show("Login Unsuccessful", "Student Login Unsuccessfull", 
                               MessageBoxButton.OKCancel, MessageBoxImage.Error);
        }
    }

    conn.Close();
}

【问题讨论】:

  • 我必须使用多线程 - 您能否在您的作业中发布造成此限制的确切措辞?
  • Regardless of database access technology, the application should use multi-threading to ensure that the user interface never becomes unresponsive while retrieving or storing information.
  • 多线程严格来说并不意味着显式地创建一个新线程。 Task.Run 将使用线程池中的线程。
  • 恕我直言,该要求的相关部分是“确保用户界面在检索或存储信息时永远不会无响应”。如果您通过显式的新线程或异步编程来实现这一点,应该是无关紧要的。除非这是一项明确要求创建新线程的作业,否则 Charlieface 的答案非常好。
  • 这学期成绩的一部分,我被扣分了,因为我的讲师不知道代表是如何工作的,所以小心点

标签: c# sql wpf multithreading


【解决方案1】:

主要问题是您在检查if (login.UserLoggedIn)之前没有等待查询完成。

我建议您为此使用 asyncawait 而不是线程。

您还有一些其他问题:

  • 连接和读取器对象需要using 块。
  • 缓存连接,在需要时创建一个新连接。
  • 对密码进行可逆加密是个坏主意,请改用散列法。将哈希传递给服务器进行验证,不要将其带回客户端应用程序。
  • 连接打开时不要用消息框阻塞线程。
  • 不要阅读过多的列。
  • 如果您只有一列和一行,请使用 ExecuteScalar
private async void tbLogin_MouseDown(object sender, MouseButtonEventArgs e)
{
    //Assign Class Property Values
    login.Student_Email = txtstd_Email.Text;
    login.Student_Password = txtstd_Password.Password;

    await User_Login(login.Student_Email, login.Student_Password));

    .....


private async Task User_Login(string email, string password)
{
    //Security object
    Secure security = new Secure();

    const string sql = @"
SELECT 1
FROM Student_Data
WHERE Student_Email = @Email
  AND Student_Password = @Pass;
";

    using (var conn = new SqlConnection(yourConnString))
    using (var cmd = new SqlCommand(sql, conn))
    {
        cmd.Parameters.Add("@Email", SqlDbType.VarChar, 55).Value = email;
        cmd.Parameters.Add("@Pass", SqlDbType.VarChar, 55).Value = security.Encrypt(password);
        await conn.OpenAsync();
        login.UserLoggedIn = await comm.ExecuteScalarAsync() != null;
    }
    if (!login.UserLoggedIn)
    {
       _ = MessageBox.Show("Login Unsuccessful", "Student Login Unsuccessfull", 
                           MessageBoxButton.OKCancel, MessageBoxImage.Error);
    }
}

【讨论】:

  • 需求状态多线程不是异步的
  • 鉴于你只是在你的线程上做Join,它实际上并没有太大的区别,但如果你真的想要你可以使用Task.Run然后等待它
  • @Charlieface 一个小提示:我会在await User_Login(...) 前后添加Enabled = false;Enabled = true; 以防止重入。
  • ExecuteScalarAsync()Operator cannot be applied to operands of type 'int' and 'object' 收到错误消息
  • 抱歉,您应该与null 进行比较,现在已修复
猜你喜欢
  • 2017-03-07
  • 1970-01-01
  • 2021-11-11
  • 2013-10-31
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 1970-01-01
  • 2021-12-30
相关资源
最近更新 更多