TextBox.TextChanged事件在Windows Phone 7模拟器上触发两次

我有一个非常简单的testing应用程序,只是为了玩弄Windows Phone 7.我刚刚添加了一个TextBox和一个TextBlock到标准的用户界面模板。 唯一的自定义代码如下:

 public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); } private int counter = 0; private void TextBoxChanged(object sender, TextChangedEventArgs e) { textBlock1.Text += "Text changed " + (counter++) + "\r\n"; } } 

TextBox.TextChanged事件连接到XAML中的TextBoxChanged

 <TextBox Height="72" HorizontalAlignment="Left" Margin="6,37,0,0" Name="textBox1" Text="" VerticalAlignment="Top" Width="460" TextChanged="TextBoxChanged" /> 

但是,每次在模拟器中运行时(按屏幕键盘或物理按键,按下暂停以启用后者)按键时,都会使计数器递增两次,在TextBlock显示两行。 我试过的所有事情都表明这个事件真的是两次发射,我不知道为什么。 我已经validation了它只被订阅了一次 – 如果我在MainPage构造函数中取消订阅,当文本更改时什么都不发生(到文本块)。

我已经在普通的Silverlight应用程序中尝试了等效的代码,并没有在那里发生。 目前我还没有物理电话来重现这一点。 我还没有发现这是Windows Phone 7中已知问题的任何logging。

任何人都可以解释我做错了什么,或者我应该报告这是一个错误?

编辑:为了减less这种可能性有两个文本控件,我已经尝试完全删除TextBlock ,并将TextBoxChanged方法更改为增加counter 。 然后我在模拟器中运行,input10个字母, 然后counter++;上放置一个断点counter++; (只是为了摆脱进入debugging器的任何可能性导致问题) – 它显示counter为20。

编辑:我现在问在Windows Phone 7论坛 …我们将看到会发生什么。

TextChanged事件在WP7中触发两次的原因是TextBox为Metro外观模板的副作用。

如果在Blend中编辑TextBox模板,则会看到它包含禁用/只读状态的辅助TextBox 。 这导致事件发生两次,作为副作用。

如果不需要这些状态,则可以更改模板以删除多余的TextBox (和关联的状态),或者修改模板以在禁用/只读状态下实现不同的外观,而不使用辅助TextBox

这样,这个事件只会触发一次。

我会去的错误,主要是因为如果你把KeyDownKeyUp事件在那里,它表明,他们只被解雇一次(他们每个人),但TextBoxChanged事件被激发两次

这听起来像是一个bug。 作为一个解决方法,你总是可以使用Rx的DistinctUntilChanged 。 有一个重载允许你指定不同的密钥。

此扩展方法返回可观察的TextChanged事件,但跳过连续的重复项:

 public static IObservable<IEvent<TextChangedEventArgs>> GetTextChanged( this TextBox tb) { return Observable.FromEvent<TextChangedEventArgs>( h => textBox1.TextChanged += h, h => textBox1.TextChanged -= h ) .DistinctUntilChanged(t => t.Text); } 

一旦错误被修复,您可以简单地删除DistinctUntilChanged行。

我相信这一直是Compact Framework中的一个错误。 它一定被结转到WP7。

太好了! 我通过search相关的问题发现了这个问题,并在我的代码中发现了这个恼人的事情。 双重事件在我的情况下吃更多的CPU资源。 所以,我解决了我的实时filter文本框:

 private string filterText = String.Empty; private void SearchBoxUpdated( object sender, TextChangedEventArgs e ) { if ( filterText != filterTextBox.Text ) { // one call per change filterText = filterTextBox.Text; ... } } 

当然,看起来像一个错误,如果你试图提出一个事件,每当文本更改,你可以尝试使用双向绑定,而不幸的是,这不会引发按键更改事件(只有当字段失去了重点)。 这是一个解决方法,如果你需要一个:

  this.textBox1.TextChanged -= this.TextBoxChanged; textBlock1.Text += "Text changed " + (counter++) + "\r\n"; this.textBox1.TextChanged += this.TextBoxChanged; 

免责声明 – 我不熟悉xaml的细微差别,我知道这听起来不合逻辑…但无论如何,我的第一个想法是尝试传递只是简单的eventargs而不是textchangedeventargs。 没有意义,但可能会有所帮助? 看起来好像当我在这之前看到过这样的双击时,它可能是由于一个错误,或者是由于某种原因在事件处理程序后面添加事件处理程序……我不确定是哪一个?

如果你需要快速和肮脏,再次,我没有经验的XAML,我的下一步将只是跳过该文本框的XAML作为一个快速的解决方法…在C#现在完全用文本框,直到你可以找出错误或棘手的代码…也就是说,如果你需要一个临时的解决scheme。

我不认为这是一个错误..当您将值赋给textchanged事件内的文本属性时,文本框的值将被更改,这将再次调用文本更改的事件..

在Windows窗体应用程序中试试这个,你可能会遇到一个错误

System.Windows.Forms.dll中发生“System.StackOverflowException”types的未处理exception“

StefanWick是对的,考虑使用这个模板

 <Application.Resources> <ControlTemplate x:Key="PhoneDisabledTextBoxTemplate" TargetType="TextBox"> <ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/> </ControlTemplate> <Style x:Key="TextBoxStyle1" TargetType="TextBox"> <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/> <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/> <Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}"/> <Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}"/> <Setter Property="BorderBrush" Value="{StaticResource PhoneTextBoxBrush}"/> <Setter Property="SelectionBackground" Value="{StaticResource PhoneAccentBrush}"/> <Setter Property="SelectionForeground" Value="{StaticResource PhoneTextBoxSelectionForegroundBrush}"/> <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/> <Setter Property="Padding" Value="2"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TextBox"> <Grid Background="Transparent"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates" ec:ExtendedVisualStateManager.UseFluidLayout="True"> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"/> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Collapsed</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="ReadOnly"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EnabledBorder"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Collapsed</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="EnabledBorder"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBackgroundBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="EnabledBorder"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneTextBoxEditBorderBrush}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Unfocused"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <VisualStateManager.CustomVisualStateManager> <ec:ExtendedVisualStateManager/> </VisualStateManager.CustomVisualStateManager> <Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}"> <ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </Application.Resources> 

这是一个老话题,但不是改变模板(这对我不起作用,我没有看到与Blend其他文本框),你可以添加布尔值来检查事件是否已经做了function。

 boolean already = false; private void Tweet_SizeChanged(object sender, EventArgs e) { if (!already) { already = true; ... } else { already = false; } } 

我意识到这不是完美的方式,但我认为这是做到这一点的简单方法。 它工作。