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

时间:2010-08-09 09:40:43

标签: c# xaml silverlight windows-phone-7 textbox

我有一个非常简单的测试应用程序,只是为了使用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";
    }
}

{1}}事件在XAML中连接到TextBox.TextChanged

TextBoxChanged

但是,每当我在模拟器(屏幕键盘或物理键盘,按下Pause以启用后者)中按下某个键时,它会使计数器递增两次,在{{1中显示两行}}。我尝试的所有东西都表明这个事件真的发射了两次,我不知道为什么。我已经验证它只被订阅了一次 - 如果我在<TextBox Height="72" HorizontalAlignment="Left" Margin="6,37,0,0" Name="textBox1" Text="" VerticalAlignment="Top" Width="460" TextChanged="TextBoxChanged" /> 构造函数中取消订阅,当文本发生变化时,根本没有任何事情发生(对于文本块)。

我在常规的Silverlight应用程序中尝试过等效的代码,并没有在那里发生。目前我没有手机可以重现这一点。我还没有找到任何关于Windows Phone 7中已知问题的记录。

任何人都可以解释我做错了什么,或者我应该将其报告为错误?

编辑:为了减少使用两个文本控件的可能性,我尝试完全删除TextBlock,并将TextBoxChanged方法更改为只是增量{{1 }}。然后我在模拟器中运行,输入10个字母然后然后MainPage行放置一个断点(只是为了摆脱闯入调试器导致问题的任何可能性) - 它显示TextBlock为20。

编辑:我现在asked in the Windows Phone 7 forum ......我们会看到会发生什么。

10 个答案:

答案 0 :(得分:75)

{1}}事件在WP7中触发两次的原因是TextChanged如何模仿Metro外观的副作用。

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

如果您不需要这些状态,可以更改模板以删除额外的TextBox(及相关状态),或修改模板以在禁用/只读状态下实现不同的外观,使用辅助TextBox

这样,事件只会发射一次。

答案 1 :(得分:18)

我会去找错误,主要是因为如果你把KeyDownKeyUp事件放在那里,它会显示它们只被触发一次(每个都被触发)但{{{ 1}}事件被触发两次

答案 2 :(得分:8)

这对我来说听起来像个错误。作为解决方法,您始终可以使用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行。

答案 3 :(得分:2)

尼斯!我通过搜索相关问题找到了这个问题,并在我的代码中发现了这个烦人的事情。在我的情况下,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;
        ...
    }

}

答案 4 :(得分:1)

我认为这一直是Compact Framework中的一个错误。它必须被转移到WP7中。

答案 5 :(得分:0)

对我来说肯定看起来像个错误,如果你试图在每次文本更改时尝试引发一个事件,你可以尝试使用双向绑定,不幸的是,这不会引起按键更改事件(仅限当场失去焦点时)。如果您需要,可以使用以下解决方法:

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

答案 6 :(得分:0)

免责声明 - 我不熟悉xaml的细微差别,我知道这听起来不合逻辑......但无论如何 - 我的第一个想法是尝试传递只是简单的eventargs而不是textchangedeventargs。没有意义,但可能有帮助吗?看起来我之前看过这样的双击,或者是由于一个错误或由于某种方式在幕后发生了2次添加事件处理程序调用......我不确定哪个呢?

如果你需要快速和肮脏,再次,我没有经验xaml-我的下一步将只是跳过xaml为该文本框作为一个快速解决方法...现在完全在c#中执行该文本框直到你可以查明错误或棘手的代码......也就是说,如果你需要临时解决方案。

答案 7 :(得分:0)

我不认为这是一个错误.. 将值分配给textchanged事件内的text属性时,会更改文本框值,这将再次调用text changed事件。

在Windows窗体应用程序中尝试此操作,您可能会收到错误

“System.Windows.Forms.dll中发生了'System.StackOverflowException'类型的未处理异常”

答案 8 :(得分:0)

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>

答案 9 :(得分:0)

这是一个古老的主题,但不是更改模板(这对我不起作用,我看不到另一个带Blend的文本框)你可以添加布尔值来检查事件是否已经完成了该功能。

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

我知道这不是完美的方式,但我认为这是一种简单的方法。它有效。