Событие TextBox.TextChanged запускается дважды в эмуляторе Windows Phone 7 - PullRequest
90 голосов
/ 09 августа 2010

У меня очень простое тестовое приложение, чтобы поиграться с Windows Phone 7. Я только что добавил 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 подключено до TextBoxChanged в XAML:

<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 на просто приращение counter.Затем я запустил эмулятор, набрал 10 букв и , а затем установил точку останова в строке counter++; (просто чтобы избавиться от любой возможности, которая может вызвать проблемы при взломе отладчика) - и он показываетcounter как 20.

РЕДАКТИРОВАТЬ: Я сейчас спросил на форуме Windows Phone 7 ... посмотрим, что произойдет.

Ответы [ 10 ]

75 голосов
/ 02 апреля 2011

Причина, по которой событие TextChanged запускается дважды в WP7, является побочным эффектом того, как TextBox был спроектирован для внешнего вида Metro.

Если вы отредактируете шаблон TextBox в Blend, вы увидите, что он содержит дополнительный TextBox для отключенного / доступного только для чтения состояния. В качестве побочного эффекта это событие вызывает срабатывание дважды.

Вы можете изменить шаблон, чтобы удалить лишние TextBox (и связанные с ним состояния), если вам не нужны эти состояния, или изменить шаблон, чтобы получить другой вид в состоянии отключено / только для чтения, без использования среднее TextBox.

При этом событие сработает только один раз.

18 голосов
/ 09 августа 2010

Я бы пошел за ошибкой, в основном потому, что если вы поместите туда события KeyDown и KeyUp, это показывает, что они запускаются только один раз (каждое из них), но событие TextBoxChanged срабатывает дважды

8 голосов
/ 07 сентября 2010

Это звучит как ошибка для меня. В качестве обходного пути вы всегда можете использовать Rx's 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.

2 голосов
/ 24 ноября 2013

Nice!Я нашел этот вопрос путем поиска связанной проблемы, а также нашел эту раздражающую вещь в моем коде.Двойное событие съедает больше ресурсов процессора в моем случае.Итак, я исправил текстовое поле фильтра в реальном времени с помощью этого решения:

private string filterText = String.Empty;

private void SearchBoxUpdated( object sender, TextChangedEventArgs e )
{
    if ( filterText != filterTextBox.Text )
    {
        // one call per change
        filterText = filterTextBox.Text;
        ...
    }

}
1 голос
/ 11 августа 2010

Я считаю, что это всегда было ошибкой в ​​Compact Framework. Должно быть, оно было перенесено в WP7.

0 голосов
/ 27 февраля 2013

Это старая тема, но вместо изменения шаблона (который не работает для меня, я не вижу другого текстового поля с Blend), вы можете добавить логическое значение, чтобы проверить, выполняло ли событие функцию или нет.

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

Я знаю, что это НЕ идеальный способ, но я думаю, что это простой способ сделать это. И это работает.

0 голосов
/ 06 ноября 2012

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>
0 голосов
/ 18 января 2011

Не думаю, что это ошибка. Когда вы присваиваете значение текстовому свойству внутри события textaged, значение текстового поля изменяется, что снова вызывает событие text change ..

попробуйте это вВ приложении Windows Forms может появиться ошибка

"Необработанное исключение типа 'System.StackOverflowException' произошло в System.Windows.Forms.dll"

0 голосов
/ 09 августа 2010

Отказ от ответственности - я не знаком с нюансами xaml, и я знаю, что это звучит нелогично ... но в любом случае - моя первая мысль - попытаться передать просто обычные события, а не textchange deventargs. Не имеет смысла, но может быть, это поможет? Похоже, что когда я видел подобные двойные срабатывания, это происходило либо из-за ошибки, либо из-за того, что 2 добавили вызовы обработчика событий, происходящие за кулисами ... Хотя я не уверен, какие именно?

Если вам нужно быстро и грязно, опять же, у меня нет опыта работы с xaml - мой следующий шаг - просто пропустить xaml для этого текстового поля в качестве быстрого обходного пути ... сделайте это текстовое поле полностью в c #, пока вы не сможете точно определить ошибка или хитрый код ... то есть, если вам нужно временное решение.

0 голосов
/ 09 августа 2010

Конечно, для меня это ошибка, если вы пытаетесь вызывать событие каждый раз, когда текст меняется, вы можете вместо этого использовать двустороннюю привязку, к сожалению, это не вызовет события изменения нажатия на клавишукогда поле теряет фокус).Вот обходной путь, если он вам нужен:

        this.textBox1.TextChanged -= this.TextBoxChanged;
        textBlock1.Text += "Text changed " + (counter++) + "\r\n";
        this.textBox1.TextChanged += this.TextBoxChanged;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...