TextBox.TextChanged 事件在 Windows Phone 7 模拟器上触发两次
Posted
技术标签:
【中文标题】TextBox.TextChanged 事件在 Windows Phone 7 模拟器上触发两次【英文标题】:TextBox.TextChanged event firing twice on Windows Phone 7 emulator 【发布时间】:2011-03-27 05:05:18 【问题描述】:我有一个非常简单的测试应用程序,用于玩 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" />
但是,每次我在模拟器中运行时按下一个键(屏幕键盘或物理键盘,按下暂停以启用后者)它都会将计数器增加两次,在 TextBlock
中显示两行.我尝试过的所有事情都表明该事件确实触发了两次,我不知道为什么。我已经验证它只被订阅一次 - 如果我在 MainPage
构造函数中取消订阅,那么当文本更改时(文本块)什么也不会发生。
我已经在常规 Silverlight 应用程序中尝试了等效代码,但它并没有出现在那里。我目前没有实体电话可以重现此内容。我没有发现任何记录表明这是 Windows Phone 7 中的一个已知问题。
谁能解释我做错了什么,或者我应该将此报告为错误吗?
编辑:为了减少这归结为拥有两个文本控件的可能性,我尝试完全删除 TextBlock
,并将 TextBoxChanged 方法更改为 just 增量 counter
。然后我在模拟器中运行,输入 10 个字母,然后 然后 在 counter++;
行上放置一个断点(只是为了消除任何闯入调试器导致问题的可能性) - 它将counter
显示为 20。
编辑:我现在 asked in the Windows Phone 7 forum... 我们会看看会发生什么。
【问题讨论】:
只是出于兴趣-如果您检查事件内部,两次事件触发时 TextBox 的内容是否相同?我真的不知道为什么会发生这种情况,因为我通常使用 MVVM 和数据绑定而不是事件处理来处理这些事情(Silverlight 和 WPF,对 WP7 没有太多经验)。 @Rune:是的,我看到两次“之后”文本。因此,如果我按“h”并显示textBox1.Text
作为 textBlock1 添加的一部分,它将在两行中显示“h”。
你提到了 2 个键盘,这可能是一个因素吗?你能禁用一个吗?也许您可以检查两个调用中 TextChangedEventArgs 的所有成员是否相等?
@Henk:大部分时间我都没有费心启用物理键盘......只是看看这是否会产生影响。 TextChangedEventArgs
并没有很多可用的 - 只有 OriginalSource
,它始终为空。
它看起来确实像一个错误,它与键盘无关,因为您只需为 Text 属性分配一个新值即可获得相同的结果,TextChanged 仍会触发两次。
【参考方案1】:
TextChanged
事件在 WP7 中触发两次的原因是 TextBox
如何为 Metro 外观模板化的副作用。
如果您在 Blend 中编辑 TextBox
模板,您会看到它包含一个用于禁用/只读状态的辅助 TextBox
。作为副作用,这会导致事件触发两次。
如果您不需要这些状态,您可以更改模板以删除额外的TextBox
(和相关状态),或者修改模板以在禁用/只读状态下实现不同的外观,而无需使用二级TextBox
。
这样,事件只会触发一次。
【讨论】:
【参考方案2】:我会去解决这个错误,主要是因为如果你把 KeyDown
和 KeyUp
事件放在那里,它表明它们只被触发一次(每个)但 TextBoxChanged
事件被触发两次
【讨论】:
@undertakeror:感谢您查看该位。我会在特定于 WP7 的论坛上问同样的问题,看看回复是什么...... TextInput 是做什么的?在 WP7 的单元测试中,这些似乎是一个相当大的错误,但它是 SL @Chris S:“TextInput
做什么?”是什么意思?我不熟悉TextInput
...
@Jon `OnTextInput(TextCompositionEventArgs e)` 是处理文本输入而不是 KeyDown 的 SL 方式,因为显然设备可能没有键盘:“当 UI 元素在设备中获取文本时发生-独立方式”msdn.microsoft.com/en-us/library/…
我只是好奇它是否也会触发两次【参考方案3】:
这对我来说听起来确实像一个错误。作为一种解决方法,您始终可以使用 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
行。
【讨论】:
【参考方案4】:不错!我通过搜索相关问题找到了这个问题,并且在我的代码中也发现了这个烦人的东西。就我而言,双重事件会占用更多的 CPU 资源。所以,我用这个解决方案修复了我的实时过滤器文本框:
private string filterText = String.Empty;
private void SearchBoxUpdated( object sender, TextChangedEventArgs e )
if ( filterText != filterTextBox.Text )
// one call per change
filterText = filterTextBox.Text;
...
【讨论】:
【参考方案5】:我相信这一直是 Compact Framework 中的一个错误。它必须已经被转移到 WP7 中。
【讨论】:
我认为它已在更新版本的 CF 中得到修复......尽管迁移到 Silverlight,但进入它会很奇怪。另一方面,无论如何,这是一个非常奇怪的错误...... 我同意这很奇怪。我昨天在一个 CF 2.0 应用程序中重新运行它。【参考方案6】:在我看来确实是一个错误,如果您尝试在每次文本更改时引发事件,您可以尝试使用双向绑定,不幸的是,这不会引发每次按键更改事件(仅当字段失去焦点时)。如果您需要,这里有一个解决方法:
this.textBox1.TextChanged -= this.TextBoxChanged;
textBlock1.Text += "Text changed " + (counter++) + "\r\n";
this.textBox1.TextChanged += this.TextBoxChanged;
【讨论】:
我不确定这是否会解决它 - 问题不是因为textBlock1.Text
更改而触发的事件处理程序 - 不过我会试一试。 (我要尝试的解决方法是让我的事件处理程序有状态,记住以前的文本。如果它实际上没有改变,忽略它:)【参考方案7】:
免责声明 - 我不熟悉 xaml 的细微差别,我知道这听起来不合逻辑......但无论如何 - 我的第一个想法是尝试将纯事件参数而不是 textchangedeventargs 传递。没有意义,但可能有帮助吗?似乎当我之前看到这样的双重触发时,它要么是由于错误,要么是由于在幕后发生了 2 个添加事件处理程序调用......我不确定是哪个?
如果您需要又快又脏,我对 xaml 没有经验-我的下一步是跳过该文本框的 xaml 作为快速解决方法...暂时完全在 c# 中执行该文本框,直到您可以查明错误或棘手的代码...也就是说,如果您需要临时解决方案。
【讨论】:
我不是传递任何事件参数的人 - 我正在实现一个事件处理程序。但我已经证实,纯粹在 C# 中添加事件处理程序没有任何区别......它仍然会被触发两次。 好的,嗯。是的,如果它是纯 C#,那么它听起来更像是一个错误。关于第一个建议-对不起,我的措辞很糟糕,我应该怎么说是-我会尝试[在您的实现/TextBoxChanged处理程序方法中]将args参数类型更改为纯eventargs。可能行不通...但是嘿...这只是我的第一个想法。 换句话说,它可能行不通,但我会尝试方法签名 = private void TextBoxChanged(object sender, EventArgs e) 只是说我尝试过 =) 对。恐怕这不会有任何效果。【参考方案8】:我不认为这是一个错误.. 当您将值分配给 textchanged 事件中的文本属性时,文本框的值将发生更改,这将再次调用文本更改事件..
在 Windows 窗体应用程序中试试这个,你可能会得到一个错误
“System.Windows.Forms.dll 中发生了“System.***Exception”类型的未处理异常”
【讨论】:
来自问题:“我刚刚在标准 UI 模板中添加了一个 TextBox 和一个 TextBlock”——它们不是一回事。我有一个用户可以输入的 TextBox 和一个显示计数的 TextBlock。【参考方案9】: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>
【讨论】:
【参考方案10】:这是一个老话题,但不是更改模板(这对我不起作用,我没有看到带有 Blend 的其他文本框),您可以添加布尔值来检查事件是否已经完成了该功能。
boolean already = false;
private void Tweet_SizeChanged(object sender, EventArgs e)
if (!already)
already = true;
...
else
already = false;
我知道这不是完美的方法,但我认为这是实现此目的的简单方法。它有效。
【讨论】:
以上是关于TextBox.TextChanged 事件在 Windows Phone 7 模拟器上触发两次的主要内容,如果未能解决你的问题,请参考以下文章
当Timer触发其tick时,如何使TextBox.TextChanged事件不触发?
TextBox.TextChanged & ICommandSource