为啥我必须单击两次才能在 Label MouseDown 事件中执行方法

Posted

技术标签:

【中文标题】为啥我必须单击两次才能在 Label MouseDown 事件中执行方法【英文标题】: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 的答案非常好。 这学期成绩的一部分,我被扣分了,因为我的讲师不知道代表是如何工作的,所以小心点 【参考方案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 进行比较,现在已修复

以上是关于为啥我必须单击两次才能在 Label MouseDown 事件中执行方法的主要内容,如果未能解决你的问题,请参考以下文章

为啥我需要在提交按钮上单击两次才能提交我的表单?

按钮需要单击两次才能工作 - 为啥?

为啥我的word excel 一次打不开,必须两次才能打开,在“打开”中选择文件可以打开。

为啥/何时我必须点击两次才能触发 iOS 上的点击

我必须单击两次才能让日期选择器显示

我必须单击两次搜索按钮才能从api获取数据