最近在用WPF做一个项目,其中有一个数据库的查询,这个查询触发没有采用输入后点击Button的形式,而是采用TextChanged触发。
查询结果用一个ListBox来接收。
为了增加用户体验,我在TextChanged内部使用了线程,但正是这一做法,让我遇到了这个问题。
先给出一个测试的代码:
1 <Window x:Class="奇怪的TextChanged.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="奇怪的TextChanged" Height="350" Width="525" WindowStartupLocation="CenterScreen" ResizeMode="NoResize"> 5 <Grid> 6 <TextBox Name="txt" TextChanged="txt_TextChanged" VerticalAlignment="Center" Width="200"/> 7 </Grid> 8 </Window>
1 private void txt_TextChanged(object sender, TextChangedEventArgs e) 2 { 3 Thread t = new Thread(new ParameterizedThreadStart(delegate 4 { 5 this.Dispatcher.Invoke(new Action(() => 6 { 7 MessageBox.Show("Hello,I'm here!"); 8 }), new object[] { }); 9 })); 10 t.Start(); 11 }
直接CTRL+F5运行,在文本框中输入任意一个英文字符,程序弹出一个MessageBox。
注意了,这时候再输入一个汉字,按我所想,应该仍是弹出一个MessageBox,可它偏偏不是这样,它居然弹出了两个MessageBox!
与汉字输入顺序位置无关,与汉字内容无关。
如果这个测试的代码换成给ListBox Add ListBoxItem,那么ListBox中的数据会被绑定两遍(并且每次绑定前都Clear了ListBox的Items),当然,直接用ItemsSource不会有这个问题。
设断点,发现在输入汉字后,这个TextChanged事件被执行了两次!
难道逼我换回主线程?这得损失多少用户体验啊!
最终我的做法是这样的:
1 string txt_temp = ""; 2 private void txt_TextChanged(object sender, TextChangedEventArgs e) 3 { 4 if (txt_temp==txt.Text) 5 { 6 return; 7 } 8 txt_temp = txt.Text; 9 Thread t = new Thread(new ParameterizedThreadStart(delegate 10 { 11 this.Dispatcher.Invoke(new Action(() => 12 { 13 MessageBox.Show("Hello,I'm here!"); 14 }), new object[] { }); 15 })); 16 t.Start(); 17 }
这样,就把程序的第二次执行跳过去了,但至于程序为什么会执行两次,我还是不明白。