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

나는 버그로 갈 것입니다. 주로 거기에 KeyDown및 KeyUp이벤트 를 넣으면 한 번만 실행된다는 것을 보여 주지만 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.