繁体   English   中英

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

[英]TextBox.TextChanged event firing twice on Windows Phone 7 emulator

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

但是,每当我在模拟器中运行时按一个键(屏幕键盘或物理键盘,按下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,主要是因为如果你把KeyDownKeyUp事件放在那里,它表明它们只被触发一次(每个都被触发)但是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM