c\#WPF 에서 System.Windows.Interactivity 사용

배경
 WPF 개발 애플 리 케 이 션 을 진행 할 때 이벤트 에 사용 할 수 밖 에 없습니다.MVVM 모드 에 따라 엄격하게 개발 하지 않 을 때 xaml 에서 이 벤트 를 직접 정의 한 다음 에 해당 하 는.cs 파일 에 이벤트 처리 과정 을 직접 작성 하 는 습관 이 있 습 니 다.이러한 처리 방식 은 매우 간단 하고 코드 간 에 규범 에 부합 되 는 지 를 고려 하지 않 아 도 된다.그러나 우리 가 코드 를 쓸 때 WPF 규범 의 MVVM 모델 에 따라 완전히 개발 할 때 해당 하 는 사건 처 리 를 ViewModel 층 에 써 야 전체 코드 가 더욱 규범 에 부합 되 고 차원 도 더욱 명확 하 며 MVVM 규범 에 더욱 부합된다.
일반적인 용법
1 네 임 스페이스 도입
코드 에 System.Windows.Interactivity.dll 을 도입 하면 이 dll 을 도입 한 후에 우 리 는 이 안의 방법 으로 이 벤트 를 ViewModel 층 에 투사 할 수 있 습 니 다.구체 적 인 사용 절 차 를 살 펴 보 겠 습 니 다.첫 번 째 단 계 는 이름 컨트롤 을 도입 하 는 것 입 니 다.

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
또 다른 방식 으로 네 임 스페이스 를 도입 할 수 있 는데 사실은 둘 다 대등 하 다.

xmlns:i=http://schemas.microsoft.com/expression/2010/interactivity
2 이벤트 에 대응 하 는 Command 추가
여기 서 TextBox 의 GetFocus 와 LostFocus 를 예 로 들 어 설명 을 해 드 리 도록 하 겠 습 니 다.

<TextBox Text="CommandBinding">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="LostFocus">
            <i:InvokeCommandAction Command="{Binding OnTextLostFocus}"
                                   CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="GotFocus">
            <i:InvokeCommandAction Command="{Binding OnTextGotFocus}"
                                   CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>
이 안에 서 는 이 Invoke CommandAction 의 코드 구 조 를 중점적으로 살 펴 보 겠 습 니 다.

namespace System.Windows.Interactivity
{
    public sealed class InvokeCommandAction : TriggerAction<DependencyObject>
    {
        public static readonly DependencyProperty CommandProperty;
        public static readonly DependencyProperty CommandParameterProperty;
 
        public InvokeCommandAction();
 
        public string CommandName { get; set; }
        public ICommand Command { get; set; }
        public object CommandParameter { get; set; }
 
        protected override void Invoke(object parameter);
    }
}
여기 서 우 리 는 Command 를 정의 하면 Command 에서 만 연 결 된 Command Parameter 라 는 인 자 를 얻 을 수 있 습 니 다.그러나 가끔 은 이 사건 을 촉발 하 는 Routed EventArgs 를 가 져 와 야 할 때 이런 방식 으로 는 얻 기 어렵 습 니 다.이 럴 때 우 리 는 스스로 Invoke CommandAction 을 확장 해 야 합 니 다.이 럴 때 우 리 는 어떻게 해 야 합 니까?전체 과정 은 세 단계 로 나 뉜 다.
2.1 자신 을 정의 하 는 CommandParameter

public class ExCommandParameter
{
    /// <summary> 
    ///       
    /// </summary> 
    public DependencyObject Sender { get; set; }
    /// <summary> 
    ///      
    /// </summary> 
    public EventArgs EventArgs { get; set; }
    /// <summary> 
    ///      
    /// </summary> 
    public object Parameter { get; set; }
}
이 대상 은 우리 의 일반적인 인 자 를 봉인 하 는 것 외 에 우리 가 필요 로 하 는 EventArgs 속성 도 봉인 합 니 다.이것 이 있 으 면 우 리 는 현재 이벤트 의 EventArgs 를 전달 할 수 있 습 니 다.
2.2 자신의 InvokeCommandAction 다시 쓰기

public class ExInvokeCommandAction : TriggerAction<DependencyObject>
    {
 
        private string commandName;
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExInvokeCommandAction), null);
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExInvokeCommandAction), null);
        /// <summary> 
        ///                  。 
        /// </summary> 
        /// <value>            。</value> 
        /// <remarks>          Command   ,           。</remarks> 
        public string CommandName
        {
            get
            {
                base.ReadPreamble();
                return this.commandName;
            }
            set
            {
                if (this.CommandName != value)
                {
                    base.WritePreamble();
                    this.commandName = value;
                    base.WritePostscript();
                }
            }
        }
        /// <summary> 
        ///               。      。 
        /// </summary> 
        /// <value>      。</value> 
        /// <remarks>          CommandName   ,          。</remarks> 
        public ICommand Command
        {
            get
            {
                return (ICommand)base.GetValue(ExInvokeCommandAction.CommandProperty);
            }
            set
            {
                base.SetValue(ExInvokeCommandAction.CommandProperty, value);
            }
        }
        /// <summary> 
        ///          。      。 
        /// </summary> 
        /// <value>    。</value> 
        /// <remarks>      ICommand.CanExecute   ICommand.Execute   。</remarks> 
        public object CommandParameter
        {
            get
            {
                return base.GetValue(ExInvokeCommandAction.CommandParameterProperty);
            }
            set
            {
                base.SetValue(ExInvokeCommandAction.CommandParameterProperty, value);
            }
        }
        /// <summary> 
        ///     。 
        /// </summary> 
        /// <param name="parameter">     。         ,            。</param> 
        protected override void Invoke(object parameter)
        {
            if (base.AssociatedObject != null)
            {
                ICommand command = this.ResolveCommand();
                /*
                 * ★★★★★★★★★★★★★★★★★★★★★★★★
                 *                  
                 * ★★★★★★★★★★★★★★★★★★★★★★★★
                 */
                ExCommandParameter exParameter = new ExCommandParameter
                {
                    Sender = base.AssociatedObject,
                    Parameter = GetValue(CommandParameterProperty),
                    EventArgs = parameter as EventArgs
                };
                if (command != null && command.CanExecute(exParameter))
                {
                    /*
                     * ★★★★★★★★★★★★★★★★★★★★★★★★
                     *            Execute   
                     * ★★★★★★★★★★★★★★★★★★★★★★★★
                     */
                    command.Execute(exParameter);
                }
            }
        }
        private ICommand ResolveCommand()
        {
            ICommand result = null;
            if (this.Command != null)
            {
                result = this.Command;
            }
            else
            {
                if (base.AssociatedObject != null)
                {
                    Type type = base.AssociatedObject.GetType();
                    PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
                    PropertyInfo[] array = properties;
                    for (int i = 0; i < array.Length; i++)
                    {
                        PropertyInfo propertyInfo = array[i];
                        if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal))
                        {
                            result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);
                        }
                    }
                }
            }
            return result;
        }
 
    } 
이 안의 중점 은 기본 클래스 의 Invoke 방법 을 다시 쓰 는 것 입 니 다.현재 명령 을 반사 적 으로 가 져 온 다음 command.Execute 방법 을 실행 할 때 사용자 정의 ExCommand Parameter 를 전달 하 는 것 입 니 다.그러면 최종 바 인 딩 된 명령 에서 특정한 EventArgs 대상 을 얻 을 수 있 습 니 다.
2.3 코드 에 사용자 정의 InvokeCommandAction 적용

  <ListBox x:Name="lb_selecthistorymembers"                         
           SnapsToDevicePixels="true"
           ItemsSource="{Binding DataContext.SpecificHistoryMembers,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
           HorizontalAlignment="Stretch"
           ScrollViewer.HorizontalScrollBarVisibility="Disabled"
           Background="#fff"
           BorderThickness="1">
           <i:Interaction.Triggers>
              <i:EventTrigger EventName="SelectionChanged">
                 <interactive:ExInvokeCommandAction Command="{Binding DataContext.OnSelectHistoryMembersListBoxSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
                              CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}">
                 </interactive:ExInvokeCommandAction>
              </i:EventTrigger>
          </i:Interaction.Triggers>       
</ListBox>
사용자 정의 inteactive 네 임 스페이스 를 먼저 도입 해 야 합 니 다.이것 은 사용 할 때 주의해 야 합 니 다.또한 최종 Command 구독 에서 EventArgs 는 이벤트 에 따라 다양한 표현 형식 이 있 습 니 다.예 를 들 어 Loaded 이벤트,최종 적 으로 얻 은 EventArgs 는 Routed EventArgs 대상 입 니 다.TableControl 의 Selection Changed 이벤트 라면,그러면 최종 적 으로 얻 은 것 은 Selection Changed EventArgs 대상 입 니 다.이것 은 사용 할 때 구분 해 야 합 니 다.
3  현재 프로그램 집합 을 사용 하여 Behavior 확장 추가
System.Windows.Interactivity.dll 의 중요 한 확장 은 Behavior 에 대한 확장 입 니 다.이 Behavior 는 도대체 어떻게 사용 해 야 합 니까?우 리 는 다음 의 예 를 살 펴 보 자.우 리 는 TextBlock 과 Button 에 통 일 된 DropShadow Effect 를 추가 해 야 한다.우 리 는 먼저 최종 효 과 를 본 다음 에 구체 적 인 코드 를 분석 해 야 한다.

코드 분석
1 EffectBehavior 1 개 추가

public class EffectBehavior : Behavior<FrameworkElement>
 {
 protected override void OnAttached()
 {
 base.OnAttached();

 AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
 AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
 }

 private void AssociatedObject_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
 {
 var element = sender as FrameworkElement;
 element.Effect = new DropShadowEffect() { Color = Colors.Transparent, ShadowDepth = 2 }; ;
 }

 private void AssociatedObject_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
 {
 var element = sender as FrameworkElement;
 element.Effect = new DropShadowEffect() { Color = Colors.Red, ShadowDepth = 2 };
 }

 protected override void OnDetaching()
 {
 base.OnDetaching();
 AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;
 AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;

 }
 }
여기 서 우 리 는 System.Windows.Interactivity 의 Behavior라 는 범 형 류 를 계승 합 니 다.여기 서 우리 의 범 형 매개 변 수 는 Framework Element 를 사용 합 니 다.대부분의 컨트롤 은 이 대상 을 계승 하기 때문에 우 리 는 통일 적 으로 효 과 를 추가 할 수 있 습 니 다.이 기본 클래스 를 통합 한 후에 우 리 는 기본 클래스 의 OnAttached 와 OnDetaching 방법 을 다시 써 야 합 니 다.이 안에 Associated Object 는 우리 가 구체 적 으로 Effect 를 추가 하 는 요소 입 니 다.우리 의 예제 에서 이것 은 각각 TextBlock 과 Button 대상 입 니 다.
2.구체 적 인 컨트롤 에 이 효 과 를 추가 합 니 다.

<Window x:Class="WpfBehavior.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:local="clr-namespace:WpfBehavior"
 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
 mc:Ignorable="d"
 Title="MainWindow" Height="450" Width="800">
 <Grid>
 <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
 <TextBlock Text="    " Margin="2" Height="30">
 <i:Interaction.Behaviors>
 <local:EffectBehavior></local:EffectBehavior>
 </i:Interaction.Behaviors>
 </TextBlock>
 <Button Content="  " Width="80" Height="30" Margin="2">
 <i:Interaction.Behaviors>
 <local:EffectBehavior></local:EffectBehavior>
 </i:Interaction.Behaviors>
 </Button>
 </StackPanel>
 </Grid>
</Window>
이상 은 c\#WPF 에서 System.Windows.Interactivity 를 사용 하 는 상세 한 내용 입 니 다.WPF 에서 System.Windows.Interactivity 에 관 한 자 료 는 다른 관련 글 을 주목 하 세 요!

좋은 웹페이지 즐겨찾기