У меня есть очень простое тестовое приложение, чтобы поиграть с Windows Phone 7. Я только что добавил a TextBox
и a 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" />
Однако каждый раз, когда я нажимаю клавишу во время работы в эмуляторе (на экранной клавиатуре или на физической, нажав Pause для включения последней), он увеличивает счетчик дважды, отображая две строки в файле TextBlock
. Все, что я пробовал, показывает, что мероприятие действительно запускается дважды, и я не знаю почему. Я подтвердил, что он подписывается только один раз - если я откажусь от подписки в MainPage
конструкторе, вообще ничего не произойдет (с текстовым блоком) при изменении текста.
Я пробовал эквивалентный код в обычном приложении Silverlight, но там этого не произошло. На данный момент у меня нет физического телефона, чтобы воспроизвести это. Я не нашел никаких свидетельств того, что это известная проблема в Windows Phone 7.
Может ли кто-нибудь объяснить, что я делаю не так, или мне следует сообщить об этом как об ошибке?
EDIT: чтобы уменьшить вероятность того, что это TextBlock
связано с двумя текстовыми элементами управления, я попытался полностью удалить и изменить метод TextBoxChanged на просто увеличение counter
. Затем я запустил эмулятор, набрал 10 букв, а затем поставил точку останова на counter++;
строке (просто чтобы избавиться от любой возможности того, что взлом отладчика вызывает проблемы) - и отображается counter
как 20.
EDIT: я спросил на форуме Windows Phone 7 ... посмотрим, что произойдет.
источник
textBox1.Text
нажму «h» и отобразлю как часть добавления textBlock1, в обеих строках будет отображаться «h».TextChangedEventArgs
на самом деле не так много доступно - простоOriginalSource
, который всегда имеет значение null.Ответы:
Причина, по которой
TextChanged
событие запускается дважды в WP7, является побочным эффектом того, какTextBox
шаблон был создан для внешнего вида Metro.Если вы отредактируете
TextBox
шаблон в Blend, вы увидите, что он содержит вторичный объектTextBox
для состояния отключено / только для чтения. Это приводит к тому, что событие запускается дважды.Вы можете изменить шаблон, чтобы удалить лишние
TextBox
(и связанные состояния), если вам не нужны эти состояния, или изменить шаблон, чтобы добиться другого вида в отключенном состоянии / состоянии только для чтения, без использования вторичногоTextBox
.При этом событие сработает только один раз.
источник
я бы пойти на ошибку, в основном потому , что если вы положили
KeyDown
иKeyUp
события там, это показывает , что они обжигают только один раз (каждый из них) , ноTextBoxChanged
событие вызывается дваждыисточник
TextInput
?" Я не знаком сTextInput
...Для меня это звучит как ошибка. В качестве обходного пути вы всегда можете использовать 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
строку.источник
Ницца! Я нашел этот вопрос, выполнив поиск связанной проблемы, а также нашел эту раздражающую вещь в своем коде. В моем случае двойное событие съедает больше ресурсов процессора. Итак, я исправил текстовое поле фильтра в реальном времени с помощью этого решения:
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.
источник
Конечно, мне кажется, что это ошибка, если вы пытаетесь инициировать событие каждый раз, когда изменяется текст, вы можете попробовать вместо этого использовать двустороннюю привязку, к сожалению, это не вызовет событий изменения нажатия каждой клавиши (только когда поле теряет фокус). Вот обходной путь, если он вам нужен:
this.textBox1.TextChanged -= this.TextBoxChanged; textBlock1.Text += "Text changed " + (counter++) + "\r\n"; this.textBox1.TextChanged += this.TextBoxChanged;
источник
textBlock1.Text
изменений - я все же попробую. (Обходной путь, который я собирался попробовать, состоял в том, чтобы сделать мой обработчик событий отслеживающим состояние, запомнив предыдущий текст. Если он на самом деле не изменился, игнорируйте его :)Отказ от ответственности - я не знаком с нюансами xaml и знаю, что это звучит нелогично ... но в любом случае - моя первая мысль - попытаться передать как простые аргументы событий, а не textchangedeventargs. Не имеет смысла, но, может быть, это поможет? Похоже, что когда я раньше видел подобные двойные срабатывания, это либо из-за ошибки, либо из-за каких-то двух вызовов обработчиков событий добавления, происходящих за кулисами ... Я не уверен, что именно?
Если вам нужно быстро и грязно, опять же, у меня нет опыта работы с xaml - моим следующим шагом было бы просто пропустить xaml для этого текстового поля в качестве быстрого обходного пути ... сделайте это текстовое поле полностью на C #, пока вы не сможете точно определить ошибку или хитрый код ... то есть, если вам нужно временное решение.
источник
Я не думаю, что это ошибка .. Когда вы присваиваете значение свойству text внутри события textchanged, значение текстового поля изменяется, что снова вызывает событие изменения текста ..
попробуйте это в приложении Windows Forms, вы можете получить сообщение об ошибке
«Необработанное исключение типа 'System.StackOverflowException' произошло в System.Windows.Forms.dll»
источник
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; } }
Я понимаю, что это НЕ идеальный способ, но я думаю, что это самый простой способ сделать это. И это работает.
источник