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
。
这样,这个事件只会触发一次。
我会去的错误,主要是因为如果你把KeyDown
和KeyUp
事件在那里,它表明,他们只被解雇一次(他们每个人),但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; } }
我意识到这不是完美的方式,但我认为这是做到这一点的简单方法。 它工作。