![](/img/trans.png)
[英]WebForms Textbox.TextChanged event is firing twice on button click
[英]TextBox.TextChanged event firing twice on Windows Phone 7 emulator
我有一个非常简单的测试应用程序,只是为了使用Windows Phone 7.我刚刚在标准UI模板中添加了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" />
但是,每当我在模拟器中运行时按一个键(屏幕键盘或物理键盘,按下Pause以启用后者),它会使计数器递增两次,在TextBlock
显示两行。 我尝试的所有东西都表明这个事件真的发射了两次,我不知道为什么。 我已经验证它只被订阅了一次 - 如果我在MainPage
构造函数中取消订阅,当文本更改时,根本没有任何事情发生(到文本块)。
我在常规的Silverlight应用程序中尝试过等效的代码,并没有在那里发生。 目前我没有手机可以重现这一点。 我还没有找到任何关于Windows Phone 7中已知问题的记录。
任何人都可以解释我做错了什么,或者我应该将此报告为错误?
编辑:为了减少有两个文本控件的可能性,我已经尝试完全删除TextBlock
,并将TextBoxChanged方法更改为只增加counter
。 然后我在模拟器中运行,键入10个字母, 然后在counter++;
上放置一个断点counter++;
line(只是为了摆脱闯入调试器导致问题的任何可能性) - 它将counter
显示为20。
编辑:我现在已经在Windows Phone 7论坛中询问过 ......我们会看到会发生什么。
TextChanged
事件在WP7中触发两次的原因是TextBox
如何TextBox
Metro外观的副作用。
如果在Blend中编辑TextBox
模板,您将看到它包含用于禁用/只读状态的辅助TextBox
。 作为副作用,这会导致事件发生两次。
如果不需要这些状态,可以更改模板以删除额外的TextBox
(和关联的状态),或者修改模板以在禁用/只读状态下实现不同的外观,而不使用辅助TextBox
。
有了这个,该事件将只发射一次。
我会去找bug,主要是因为如果你把KeyDown
和KeyUp
事件放在那里,它表明它们只被触发一次(每个都被触发)但是TextBoxChanged
事件被触发了两次
这对我来说听起来像个错误。 作为解决方法,您可以始终使用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
行即可。
太好了! 我通过搜索相关问题找到了这个问题,并在我的代码中发现了这个烦人的事情。 在我的情况下,Double事件会占用更多的CPU资源。 所以,我用这个解决方案修复了我的实时过滤器文本框:
private string filterText = String.Empty;
private void SearchBoxUpdated( object sender, TextChangedEventArgs e )
{
if ( filterText != filterTextBox.Text )
{
// one call per change
filterText = filterTextBox.Text;
...
}
}
我相信这一直是Compact Framework中的一个错误。 它必须被转移到WP7中。
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的文本框)你可以添加布尔值来检查事件是否已经完成了该功能。
boolean already = false;
private void Tweet_SizeChanged(object sender, EventArgs e)
{
if (!already)
{
already = true;
...
}
else
{
already = false;
}
}
我知道这不是完美的方式,但我认为这是一种简单的方法。 它有效。
当然看起来像是一个bug,如果你试图在每次文本更改时尝试引发一个事件,你可以尝试使用双向绑定,不幸的是,这不会引起按键更改事件(仅当字段时)失去焦点)。 如果您需要,可以使用以下解决方法:
this.textBox1.TextChanged -= this.TextBoxChanged;
textBlock1.Text += "Text changed " + (counter++) + "\r\n";
this.textBox1.TextChanged += this.TextBoxChanged;
免责声明 - 我不熟悉xaml的细微差别,我知道这听起来不合逻辑......但无论如何 - 我的第一个想法是尝试传递只是简单的eventargs而不是textchangedeventargs。 没有意义,但可能有帮助吗? 看起来我之前看过这样的双击,或者是由于一个错误或由于某种方式在幕后发生了2次添加事件处理程序调用......我不确定哪个?
如果你需要快速和肮脏,再次,我没有经验xaml-我的下一步将只是跳过xaml作为一个快速解决方案的文本框...现在完全在c#中的文本框,直到你可以查明错误或棘手的代码......也就是说,如果您需要临时解决方案。
我不认为这是一个错误..当您将值分配给textchanged事件内的文本属性时,文本框值将更改,这将再次调用文本已更改事件..
在Windows窗体应用程序中尝试此操作,您可能会收到错误
“System.Windows.Forms.dll中发生了'System.StackOverflowException'类型的未处理异常”
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.