Windows Phone 7 에뮬레이터에서 TextBox.TextChanged 이벤트가 두 번 발생합니다.


91

Windows Phone 7을 사용하기위한 매우 간단한 테스트 앱이 있습니다 . 표준 UI 템플릿 에 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이벤트까지 연결되어 TextBoxChangedXAML에서 :

<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-20으로 표시 됩니다.

편집 : 이제 Windows Phone 7 포럼에서 질문했습니다 .


관심이 없습니다. 이벤트 내부를 확인하면 이벤트가 실행될 때 TextBox의 내용이 동일합니까? 나는 일반적으로 이러한 일을 처리하는 대신 MVVM 및 데이터 바인딩을 사용하기 때문에 왜 이런 일이 발생하는지 알지 못합니다 (Silverlight 및 WPF, WP7에 대한 경험이 많지 않음).
Rune Jacobsen

@Rune : 예, "이후"텍스트가 두 번 표시됩니다. 따라서 "h"를 누르고 textBox1.TexttextBlock1 추가의 일부로 표시 하면 두 줄에 "h"가 표시됩니다.
Jon Skeet

1
2 개의 키보드를 언급했는데 그게 요인이 될 수 있습니까? 하나를 비활성화 할 수 있습니까? 그리고 TextChangedEventArgs의 모든 멤버가 두 호출에서 동일한 지 확인할 수 있습니까?
Henk Holterman

@Henk : 대부분의 경우 물리적 키보드를 활성화하는 데 신경 쓰지 않았습니다 ... 그게 효과가 있는지 확인하기 위해서 만요. TextChangedEventArgs실제로 사용할 수있는 항목이 많지 않습니다 OriginalSource. 항상 null 인.
Jon Skeet

3
버그처럼 보이지만 Text 속성에 새 값을 할당하여 동일한 결과를 얻을 수 있기 때문에 키보드와 관련이 없습니다. TextChanged는 여전히 두 번 실행됩니다.
AnthonyWJones

답변:


75

TextChangedWP7에서 이벤트가 두 번 발생 하는 이유는 TextBoxMetro 모양에 대해 템플릿이 지정 되는 방식의 부작용 입니다.

TextBoxBlend 에서 템플릿 을 편집하면 TextBox비활성화 / 읽기 전용 상태에 대한 보조가 포함 된 것을 볼 수 있습니다 . 이로 인해 부작용으로 이벤트가 두 번 발생합니다.

TextBox이러한 상태가 필요하지 않은 경우 템플릿을 변경하여 추가 (및 관련 상태) 를 제거 하거나 보조 TextBox.

이를 통해 이벤트는 한 번만 실행됩니다.


18

나는 버그로 갈 것입니다. 주로 거기에 KeyDownKeyUp이벤트 를 넣으면 한 번만 실행된다는 것을 보여 주지만 TextBoxChanged이벤트는 두 번 발생합니다.


@undertakeror : 그 비트를 확인해 주셔서 감사합니다. 나는 WP7 관련 포럼에 같은 질문을하고 응답이 ... 무엇인지 볼 수 있습니다
존 소총

TextInput의 기능은 무엇입니까? 이것들은 WP7의 단위 테스트를 통과하기에는 꽤 큰 버그처럼 보이지만 SL
Chris S

@Chris S : "무엇을 TextInput합니까?" 란 무엇을 의미 합니까? 나는 익숙하지 않습니다 TextInput...
Jon Skeet

@Jon`OnTextInput (TextCompositionEventArgs e)`는 장치에 키보드가 없을 수 있으므로 KeyDown 대신 텍스트 입력을 처리하는 SL 방식입니다. "UI 요소가 장치 독립적 인 방식으로 텍스트를 가져올 때 발생합니다" msdn.microsoft. com / en-us / library /…
Chris S

나는 그것도 두 번 해고되는지 궁금했다
Chris S

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줄을 제거 할 수 있습니다 .


2

좋은! 나는 관련 문제를 검색 하여이 질문을 발견했으며 내 코드 에서이 성가신 것을 발견했습니다. 이중 이벤트는 내 경우 더 많은 CPU 리소스를 먹습니다. 그래서이 솔루션으로 실시간 필터 텍스트 상자를 수정했습니다.

private string filterText = String.Empty;

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

}

1

나는 이것이 항상 Compact Framework의 버그라고 생각합니다. WP7로 옮겨 졌을 것입니다.


최신 버전의 CF에서 수정되었다고 생각했는데 ... Silverlight로 이동 했음에도 불구하고 들어가는 것이 이상 할 것입니다. 반면에, 어쨌든보기에는 꽤 이상한 버그입니다 ...
Jon Skeet

이상하다는 것에 동의합니다. 어제 CF 2.0 응용 프로그램에서 다시 실행했습니다.
Jerod Houghtelling

0

물론 버그처럼 보입니다. 텍스트가 변경 될 때마다 이벤트를 발생시키려는 경우 양방향 바인딩을 대신 사용해 볼 수 있습니다. 안타깝게도 키당 변경 이벤트가 발생하지 않습니다 (필드 초점을 잃는다). 필요한 경우 해결 방법은 다음과 같습니다.

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

나는 그것이 그것을 해결할 것인지 확신하지 못합니다. 문제는 textBlock1.Text변경으로 인해 발생하는 이벤트 핸들러가 아닙니다 . 그래도 시도해 보겠습니다. ( 내가 시도한 해결 방법 은 이전 텍스트를 기억하면서 이벤트 핸들러를 상태 저장으로 만드는 것이 었습니다. 실제로 변경되지 않은 경우 무시하십시오.)
Jon Skeet 2010-08-09

0

면책 조항-나는 xaml 뉘앙스에 익숙하지 않으며 이것이 비논리적으로 들린다는 것을 알고 있습니다 ... 어쨌든-내 첫 번째 생각은 textchangedeventargs가 아닌 평범한 eventargs로 전달하는 것입니다. 말이되지 않지만 도움이 될 수 있습니까? 버그 때문이거나 2 개의 이벤트 처리기 호출이 뒤에서 일어나기 때문에 이전에 이와 같은 이중 발사를 본 적이있는 것 같습니다.

빠르고 더러운 것이 필요하다면 xaml에 대한 경험이 없습니다. 다음 단계는 빠른 해결 방법으로 해당 텍스트 상자에 대한 xaml을 건너 뛰는 것입니다 ... 버그를 정확히 찾을 수있을 때까지 해당 텍스트 상자를 C #으로 완전히 수행하거나 까다로운 코드 ... 즉, 임시 솔루션이 필요한 경우.


이벤트 인수를 전달하는 사람이 아닙니다. 이벤트 처리기를 구현하고 있습니다. 하지만 순전히 C #에서 이벤트 처리기를 추가해도 아무런 차이가 없음을 확인했습니다. 여전히 두 번 실행됩니다.
Jon Skeet

좋아, 흠. 예, 순수한 C #이면 버그처럼 들립니다. 첫 번째 제안에 대해-내 말이 끔찍한 것이 유감입니다. 어떻게 말 했어야했는지-[구현 / TextBoxChanged 처리기 메서드에서] args 매개 변수 유형을 일반 eventargs로 변경하려고합니다. 아마 작동하지 않을 것입니다 ...하지만 헤이 ... 그것은 단지 내 첫 생각이었습니다.
Pimp Juice McJones 2010 년

즉, 아마 작동하지 않습니다하지만 난 방법 서명 = 개인 무효 TextBoxChanged (개체 보낸 사람, EventArgs입니다 전자) 단지 = 나는 그것을 시도한다는 말을) 시도 할 것
포주 주스 McJones

권리. 나는 그것이 효과가 없을 것이라고 생각합니다.
Jon Skeet

0

나는 그것이 버그라고 생각하지 않는다 .. textchanged 이벤트 내의 텍스트 속성에 값을 할당하면 텍스트 변경 이벤트를 다시 호출하는 텍스트 상자 값이 변경됩니다 ..

Windows Forms 응용 프로그램에서 이것을 시도하면 오류가 발생할 수 있습니다.

"System.Windows.Forms.dll에서 'System.StackOverflowException'유형의 처리되지 않은 예외가 발생했습니다."


질문에서 : "표준 UI 템플릿에 TextBox와 TextBlock을 추가했습니다."-같은 것이 아닙니다. 사용자가 입력 할 수있는 하나의 TextBox와 개수를 표시하는 하나의 TextBlock이 있습니다.
Jon Skeet 2011 년

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>

0

그것은 오래된 주제이지만, 템플릿을 변경하는 대신 (블렌드로 다른 텍스트 상자가 보이지 않습니다) 이벤트가 이미 기능을 수행했는지 여부를 확인하기 위해 부울을 추가 할 수 있습니다.

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

이것이 완벽한 방법이 아니라는 것을 알고 있지만 그렇게하는 간단한 방법이라고 생각합니다. 그리고 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.