WPF EventTrigger 질문드립니다.

안녕하세요. 입력박스가 비어있거나 디폴트 값이면 버튼이 좌우로 움직이는 기능을 구현하려고 합니다.

xaml 에서 버튼 이벤트 발생시 해당 텍스트 박스의 값을 체크하면 될 것은데 EventTrigger내에서 다른

컨트롤의 상태를 가져올 수는 없는건가요?? 이 기능은 비하인드코드에서 적용을 해야만 하는건지

도움부탁드립니다.



<Button x:Name="button2" Content="IndividualPrint" HorizontalAlignment="Left" Height="55" Margin="1036,558,0,0" VerticalAlignment="Top" Width="250" FontFamily="Arial Rounded MT Bold" FontSize="24" Background="#7FFF9771" Click="onePrintButton_Click">
     <Button.Style>
         <Style TargetType="{x:Type Button}">
             <Setter Property="Template">
                 <Setter.Value>
                     <ControlTemplate TargetType="{x:Type Button}">
                         <Border Background="{TemplateBinding Background}" BorderBrush="SkyBlue" BorderThickness="0.5" CornerRadius="5,5,5,5">
                             <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                         </Border>
                     </ControlTemplate>
                 </Setter.Value>
             </Setter>
             <Style.Triggers>
                 <EventTrigger RoutedEvent="Click">
                      <!-- 
                        이곳에 왼쪽텍스트(textBox1) 값을 참고해서 아래 애니메이션 효과가 일어나도록 조건을 
                         추가했으면 합니다. 
                          -->
                     <EventTrigger.Actions>
                         <BeginStoryboard>
                             <Storyboard>
                                 <StaticResource ResourceKey="dataError"/>
                             </Storyboard>
                         </BeginStoryboard>
                     </EventTrigger.Actions>
                 </EventTrigger>
2 Likes

기본적으로 EventTrigger에서는 로지컬 처리가 불가능 합니다.

대신 DataTrigger로 대체 하여 처리 가능합니다.

다음은 간단한 샘플의 코드 입니다.
[xaml]

<Window.Resources>
    <Storyboard x:Key="BtnStartShake">
        <DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
                                       Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
            <EasingDoubleKeyFrame KeyTime="0:0:0"
                                  Value="0" />
            <EasingDoubleKeyFrame KeyTime="0:0:0.1"
                                  Value="-5" />
            <EasingDoubleKeyFrame KeyTime="0:0:0.2"
                                  Value="5" />
            <EasingDoubleKeyFrame KeyTime="0:0:0.3"
                                  Value="-5" />
            <EasingDoubleKeyFrame KeyTime="0:0:0.4"
                                  Value="5" />
            <EasingDoubleKeyFrame KeyTime="0:0:0.5"
                                  Value="0" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="BtnEndShake">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
            <EasingDoubleKeyFrame KeyTime="0:0:0"
                          Value="0" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

    <TextBox x:Name="xInputTextBox"
             Grid.Column="0"
             Height="35"
             Text="텍스트를 입력하세요!"
             Margin="10"/>
    <Button Grid.Column="1"
            Height="35"
            Width="100"
            Content="Click"
            Margin="10">
        <Button.RenderTransform>
            <TranslateTransform/>
        </Button.RenderTransform>
        <Button.Style>
            <Style TargetType="Button">
                <Style.Triggers>
                    <!--텍스트 박스가 기본 문구 이거나-->
                    <DataTrigger Binding="{Binding ElementName=xInputTextBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
                                 Value="텍스트를 입력하세요!">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource BtnStartShake}" />
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <BeginStoryboard Storyboard="{StaticResource BtnEndShake}" />
                        </DataTrigger.ExitActions>
                    </DataTrigger>

                    <!--텍스트 박스가 비어 있거나-->
                    <DataTrigger Binding="{Binding ElementName=xInputTextBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
                                 Value="">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource BtnStartShake}" />
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <BeginStoryboard Storyboard="{StaticResource BtnEndShake}" />
                        </DataTrigger.ExitActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>
</Grid>

텍스트 박스의 Text 속성 값 기준으로
텍스트 박스에 값이 비 어 있거나,
텍스트 박스에 값이 기본 문구 라면,

버튼이 좌.우로 흔들리도록 처리 된 예제 입니다.

버튼_쉐이크


다만, 위 예는 버튼 클릭시 동작이 아닌 텍스트 박스의 값이 변경 될때 트리거가 발생 합니다.

작성하신 이 조건과도 같이 부합 되어야 한다면
버튼에 Command를 처리해서 텍스트 박스의 값을 체크하여
원하는 조건인지 판단하는 flag 속성을 바인딩 해서 처리 하셔야 합니다.

위 예시 코드에서 ElementName=xInputTextBox 부분을
조건 판단 유무의 flag속성으로 바인딩을 대체 하면 될 것 같습니다.

7 Likes

감사합니다. 알려주신 힌트로 클릭이벤트 TESt를 해보았습니다.

근데 이게 DataTrigger 이용시 기존값을 저장하고 있나요??

아래 코드의 CheckData 가 최초 실행시 True , 버튼을 클릭하면 False로 변경되어 정상적으로 버튼이

흔들리는데 이상태에서 재차 버튼을 클릭하면 DataTrigger에서는 값 변경이 없어서 인지 반응을 하지

않습니다. 그래서 차선책으로 일단 False로 변경 후 다시 True로 변경하는 로직을 비하인드에

넣어두면 정삭적으로 여러번 버튼을 클릭해도 버튼이 흔들립니다.

DataTrigger 부분에 뭔가 이전의 바인딩 값을 저장하고 있어서 바뀐 값이 동일하면 트리거가 작동을

안하는 느낌인데, 원래 그런가요??

<XAML>
<DataTrigger Binding="{Binding CheckData}" Value="False">
    
    <DataTrigger.EnterActions>
        <BeginStoryboard>
            <Storyboard>
                <StaticResource ResourceKey="dataError"/>
            </Storyboard>
        </BeginStoryboard>
    </DataTrigger.EnterActions>
 public Boolean CheckData 
 {
     get { return checkDataP; }
     set { checkDataP = value; OnPropertyChanged("CheckData");
         //MessageBox.Show(value.ToString());
     }
 }

 public void oneprintLabel() 
 {

     if (string.IsNullOrEmpty(printTxtP) 
         || printTxtP.Equals("출력하고자 하는 바코드를 텍스트로 입력하세요."))
     {         
         CheckData = false;
     }
     else 
     {
         //생략 
     }
      //OnpropertyChaged는 계속 발생되나 
      //DataTrigger가 동일한 값을 인지하지 못해 True로 변경, 뭔가 이상함
     //false -> false : Trigger 작동 X , false -> true -> false : Trigger 작동
     CheckData = true; 
  
 }
1 Like

재현할 수 있는 간단한 전체 샘플 소스코드가 있으면 좋을 것 같습니다!

1 Like

이전의 바인딩 값을 저장하고 있는게 아니고

CheckData 속성이 최초 초기값 true에서 → false로 바뀌었을때

트리거가 발동 되면서 스토리보드가 작동 되는 것 입니다.

스토리보드가 끝났다고 다시 CheckData 속성 값이 초기화 되진 않죠

여전히 계속 true이기에 한번 스토리보드는 작동 되었기에 다시 재생 하진 않습니다.


그냥 작성 하신 것 처럼 그냥 다시 true로 설정 하셔도 딱히 이상할건 없습니다.


저 방식이 마음에 안드신다 하면…
비해이비어를 별도로 구성하셔서 버튼 클릭시 조건을 체크해서 스토리보드를 작동해도 됩니다.

다음은 대충 비해이비어를 구현해 보았습니다.

public class WhenStoryboardBehavior : Behavior<ButtonBase>
{
    private const string Default_InputText = "텍스트를 입력하세요!";

    protected override void OnAttached()
    {
        AssociatedObject.Click += (_, __) =>
        {
            if (Storyboard is not null)
            {
                if (string.IsNullOrWhiteSpace(InputText) ||
                    InputText is Default_InputText)
                {
                    AssociatedObject.BeginStoryboard(Storyboard);
                }
            }
        };
    }

    public static readonly DependencyProperty InputTextProperty =
        DependencyProperty.Register("InputText",
            typeof(string),
            typeof(WhenStoryboardBehavior));

    public string? InputText
    {
        get { return base.GetValue(InputTextProperty) as string; }
        set { base.SetValue(InputTextProperty, value); }
    }

    public static readonly DependencyProperty StoryboardProperty =
        DependencyProperty.Register("Storyboard",
            typeof(Storyboard),
            typeof(WhenStoryboardBehavior));

    public Storyboard? Storyboard
    {
        get { return base.GetValue(StoryboardProperty) as Storyboard; }
        set { base.SetValue(StoryboardProperty, value); }
    }

}
<TextBox x:Name="xInputTextBox"
         Grid.Column="0"
         Height="35"
         Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}"
         Margin="10"/>
<Button Grid.Column="1"
        Height="35"
        Width="100"
        Content="Click"
        Command="{Binding ClickCommand}"
        Margin="10">
    <Button.RenderTransform>
        <TranslateTransform/>
    </Button.RenderTransform>

    <bi:Interaction.Behaviors>
        <local:WhenStoryboardBehavior InputText="{Binding InputText}"
                                      Storyboard="{StaticResource BtnStartShake}"/>
    </bi:Interaction.Behaviors>
</Button>
4 Likes

아니고 맘에 안들다니요;; 너무 감사하죠~^^;; 다만 제가 아직 초보이다 보니 알려주신 방법으로 구현을 하던중 생각했던 동작이 나오질 않아 이유가 궁금해서 추가질문을 드렸던 겁니다~~^^ 다른 구현방법까지 알려주시고 너무 감사합니다~!

2 Likes

우선 궁금한게 텍스트가 변경 되었을때 버튼이 움직이는 건지
아니면 버튼 클릭 했을때 버튼이 움직이는 건지가 내용으로는 판단이 안되네요.

일반적으로 잘못된 값을 넣는 중간에는 특별히 에러 표시를 안하고
액션( 엔터나 버튼 클릭 ) 했을때
에러를 노티 하는 방식을 많이 쓰는 걸로 알고 있습니다.

텍스트가 변경 되었을때는
위에서 말씀해 주신 거처럼 데이터트리거로 처리 하는게 맞고
액션 했을때는 이벤트 트리거로 해주시면 될거 같습니다.

2 Likes

안녕하세요. 원 질문이 버튼 클릭 이벤트 트리거에서 해당 조건을 확인해서 버튼이 흔들리는 걸 문의드렸습니다. 버튼이 흔들리는것 까지는 구현했었는데 해당 버튼의 이벤트 트리거에서 textBox의 Text를 어찌가져올 수가 있는지가 핵심 질문내용이었습니다.
aroooong 님께서 eventTrigger에서는 로지컬 처리가 불가하다고 하여 바인딩으로 처리했습니다~!

2 Likes