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 模板中添加了 TextBoxTextBlock。唯一的自定义代码如下:

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】:

我会去解决这个错误,主要是因为如果你把 KeyDownKeyUp 事件放在那里,它表明它们只被触发一次(每个)但 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

wpf 自定义控件 的问题 点了按钮以后 反应非常慢 请大神帮助

关于点击事件只执行一次的问题

onkeyup事件

概率论