为啥我必须单击两次才能在 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)
之前没有等待查询完成。
我建议您为此使用 async
和 await
而不是线程。
您还有一些其他问题:
连接和读取器对象需要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 事件中执行方法的主要内容,如果未能解决你的问题,请参考以下文章