WPF中的Behavior及Behavior在MVVM模式下的应用 在WPF中,Behaviors(行为)是一种可重用的组件,可以附加到任何UI元素上,以添加特定的交互行为或功能。Behaviors可以通过附加属性或附加行为的方式来实现。
Behavior并不是WPF组件中的内容,需要安装nuget包,使用行为需要nuget安装Microsoft.Xaml.Behaviors.Wpf
,FrameWork版本安装System.Windows.Interactivity.WPF
,同时添加命名空间xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
。
使用Interaction.Behaviors 1 2 3 4 5 6 7 8 9 <Border Width ="100" Height ="100" Background ="Red" > <i:Interaction.Behaviors > <i:MouseDragElementBehavior /> </i:Interaction.Behaviors > </Border >
官方内置了以下几种Behavior,可以根据需要使用。
自定义Behavior 可以根据需要,自定义Behavior,下面是一个鼠标触发MouseEnter事件改变背景色的Behavior
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class MyBehavior :Behavior <Border >{ protected override void OnAttached () { AssociatedObject.MouseEnter += (_, _) => AssociatedObject.Background = Brushes.Orange; } protected override void OnDetaching () { base .OnDetaching(); } }
1 2 3 4 5 6 7 8 9 10 <Border Width ="100" Height ="100" Background ="Red" > <i:Interaction.Behaviors > <i:MouseDragElementBehavior /> <local:MyBehavior /> </i:Interaction.Behaviors > </Border >
案例:实现自动清空文本框内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class ClearTextBehavior : Behavior <Button >{ public TextBox Target { get { return (TextBox)GetValue(TargetProperty); } set { SetValue(TargetProperty, value ); } } public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target" , typeof (TextBox), typeof (ClearTextBehavior), new PropertyMetadata(null )); protected override void OnAttached () { AssociatedObject.Click += AssociatedObject_Click; } private void AssociatedObject_Click (object sender, RoutedEventArgs e ) { Target?.Clear(); } protected override void OnDetaching () { AssociatedObject.Click -= AssociatedObject_Click; } }
1 2 3 4 5 6 <TextBox x:Name ="tbx" /> <Button Width ="30" Content ="清空" > <i:Interaction.Behaviors > <local:ClearTextBehavior Target ="{Binding ElementName=tbx}" /> </i:Interaction.Behaviors > </Button >
使用Interaction.Triggers 使用EnentTrigger实现点击button关闭整个窗口
1 2 3 4 5 6 7 8 <Button Content ="Close" > <i:Interaction.Triggers > <i:EventTrigger EventName ="Click" > <i:CallMethodAction MethodName ="Close" TargetObject ="{Binding RelativeSource={RelativeSource AncestorType=Window}}" /> </i:EventTrigger > </i:Interaction.Triggers > </Button >
同样,可以应用多个Trigger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <Button Content ="Close" > <i:Interaction.Triggers > <i:EventTrigger EventName ="Click" > <i:CallMethodAction MethodName ="Close" TargetObject ="{Binding RelativeSource={RelativeSource AncestorType=Window}}" /> </i:EventTrigger > <i:DataTrigger Binding ="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsMouseOver}" Value ="True" > <i:ChangePropertyAction PropertyName ="Background" TargetObject ="{Binding RelativeSource={RelativeSource AncestorType=Button}}" Value ="Red" /> </i:DataTrigger > </i:Interaction.Triggers > </Button >
还可以使用Command与VM绑定
1 2 3 4 5 6 7 <Button Width ="150" Height ="50" Content ="Interaction.Triggers" > <i:Interaction.Triggers > <i:EventTrigger EventName ="Click" > <i:InvokeCommandAction Command ="{Binding myCommand}" /> </i:EventTrigger > </i:Interaction.Triggers > </Button >
官方内置了以下几种Trigger,可以根据需要使用。
Style中的Trigger和控件中的Trigger Style中的各种Trigger参见WPF的Style
继承自FrameworkElement
类中控件也可以直接使用trigger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <Button Width ="150" Height ="100" Content ="Button.Triggers" > <Button.Triggers > <EventTrigger RoutedEvent ="MouseEnter" > <BeginStoryboard > <Storyboard > <ColorAnimation Storyboard.TargetProperty ="(Button.Background).(SolidColorBrush.Color)" To ="Red" Duration ="0:0:0:5" /> </Storyboard > </BeginStoryboard > </EventTrigger > </Button.Triggers > </Button >
可以看出无论是Style中的Trigger还是控件下面的Trigger,都是使用的RoutedEvent
属性来设置事件,但是有些事件是不属于RoutedEvent的,比如listbox的选择某个条目事件。而且这两种Trigger都是对故事板进行操作。
MVVM模式来实现获得鼠标的坐标 当鼠标在窗口上移动时,将坐标信息传到VM中,并在V中显示。那有人会问,这样还不如直接不经过VM直接在V中进行显示,关键在于,某些时候VM中需要坐标信息,而且要遵循MVVM模式。
自定义Behavior 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class PositionBehavior : Behavior <Window >{ public double X { get { return (double )GetValue(XProperty); } set { SetValue(XProperty, value ); } } public static readonly DependencyProperty XProperty = DependencyProperty.Register("X" , typeof (double ), typeof (PositionBehavior), new FrameworkPropertyMetadata(0.0 ,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public double Y { get { return (double )GetValue(YProperty); } set { SetValue(YProperty, value ); } } public static readonly DependencyProperty YProperty = DependencyProperty.Register("Y" , typeof (double ), typeof (PositionBehavior), new FrameworkPropertyMetadata(0.0 , FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); protected override void OnAttached () { AssociatedObject.MouseMove += AssociatedObject_MouseMove; } private void AssociatedObject_MouseMove (object sender, MouseEventArgs e ) { var pos = e.GetPosition(this .AssociatedObject); X = pos.X; Y = pos.Y; } protected override void OnDetaching () { AssociatedObject.MouseMove -= AssociatedObject_MouseMove; } }
VM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class ViewModel1 : INotifyPropertyChanged { private double x; private double y; public double X { get { return x; } set { x = value ; PropertyChanged.Invoke(this , new PropertyChangedEventArgs("X" )); PropertyChanged.Invoke(this , new PropertyChangedEventArgs("Position" )); } } public double Y { get { return y; } set { y = value ; PropertyChanged.Invoke(this , new PropertyChangedEventArgs("Y" )); PropertyChanged.Invoke(this , new PropertyChangedEventArgs("Position" )); } } public string Position { get { return $"坐标为:{X:F2} ,{Y:F2} " ; } } public event PropertyChangedEventHandler? PropertyChanged; }
View 1 2 3 4 5 6 7 8 9 10 11 <Window ...... xmlns:i="http://schemas.microsoft.com/xaml/behaviors" > <!--双向模式绑定VM中的X,Y--> <i:Interaction.Behaviors> <local:PositionBehavior X="{Binding X}" Y="{Binding Y}" /> </i:Interaction.Behaviors> <StackPanel Orientation="Vertical" > <TextBox x:Name="tbx" FontSize="30" Text="{Binding Position,Mode=OneWay}" /> </StackPanel> </Window>