在WPF中,假如我们需要给Button增加阴影效果,同时又需要给TextBlock增加阴影效果,或者又来了新的需求,连DataGrid也需要增加阴影效果。虽然我们已经学过如何给一个控件增加阴影效果,那就是在UIElement基类中去设置Effect属性,但是,每个控件都要单独去实现一次阴影效果,未免太繁琐。那又该如何偷懒呢?Behavior类(行为)就是专门用来实现这类需求的。
一句话,行为就是某些控件的共同特征的实现。如上所示,既然所有控件都有Effect属性,那干脆将这个特征写成一个行为,哪个控件有这个需求,直接调用这个行为即可。说是调用行为,专业点说,是将这个行业附加到某个控件上。
一、行为基类的定义
首先,我们来看一下Behavior基类的定义,了解大致组成后,我们才知道要怎么使用它。
namespace System.Windows.Interactivity
{
public abstract class Behavior : Animatable, IAttachedObject
{
protected Type AssociatedType { get; }
protected DependencyObject AssociatedObject { get; }
public void Attach(DependencyObject dependencyObject);
public void Detach();
protected override Freezable CreateInstanceCore();
protected virtual void OnAttached();
protected virtual void OnDetaching();
}
}
从定义上看,第一,它是一个抽象类,所以我们不能直接使用它,而是去继承它,在子类中实现我们自己的行业效果。第二,它继承于Animatable,说明行为是可以动画实现的。第三,它的命名空间为System.Windows.Interactivity,这个空间在哪里?
要使用行为,我们需要引入一个.dll动态库,或者您可以在nuget上找到它。请安装Microsoft.Xaml.Behaviors.Wpf这个组件包。

Behavior泛型基类
namespace System.Windows.Interactivity
{
public abstract class Behavior<T> : Behavior where T : DependencyObject
{
protected Behavior();
protected T AssociatedObject { get; }
}
}
其实,我们实际继承的是行为的泛型基类,因为WPF并不知道我们要给哪一种控件定义行为,于是,干脆提供一个泛型基类供我们使用。所以,我们将两个基类合并起来介绍。
AssociatedObject属性:这个属性通常表示一个控件,类型是DependencyObject(依赖对象),也就是说,我们写的行为要给某个控件使用的前提是,这个控件是一个DependencyObject(依赖对象)。
OnAttached():这是一个虚方法,将来在行为中被重写,表示附加一个行为时要执行的业务逻辑。
OnDetaching():这是一个虚方法,将来在行为中被重写,表示分离一个行为时要执行的业务逻辑。
二、行为的开发
接下来,我们假如要给一个TextBlock文字块增加一个阴影效果。那么,就可以编写一个自定义行为TextBehavior,相关代码如下:
public class TextBehavior : Behavior<TextBlock>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
}
private void AssociatedObject_MouseEnter(object sender,
System.Windows.Input.MouseEventArgs e)
{
var element = sender as TextBlock;
DropShadowEffect effect = new DropShadowEffect();
effect.Color = Colors.Gold;
effect.ShadowDepth = 0;
effect.BlurRadius = 15;
element.Effect = effect;
}
private void AssociatedObject_MouseLeave(object sender,
System.Windows.Input.MouseEventArgs e)
{
var element = sender as TextBlock;
DropShadowEffect effect = new DropShadowEffect();
effect.Color = Colors.Gold;
effect.ShadowDepth = 0;
effect.BlurRadius = 15;
element.Effect = null;
}
}
在上面的代码中, 我们给AssociatedObject的MouseEnter和MouseLeave分别订阅了两个事件回调函数,在回调函数中去设置DropShadowEffect 效果。注意,这里的AssociatedObject实际上就等于TextBlock控件哦。
在定义好一个行为后,接下来就是在XAML中使用它。
第一步,在XAML中导入命名空间 xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
第二步,编写前端XAML代码,引用TextBehavior行为,完整代码如下:
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:controls="clr-namespace:HelloWorld.Controls"
xmlns:helper="clr-namespace:HelloWorld.MVVM"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behavior="clr-namespace:HelloWorld.Behaviors"
mc:Ignorable="d" Background="DarkCyan"
Title="WPF中文网 - 行为 - www.wpfsoft.com" Height="350" Width="500">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Canvas>
<TextBlock Text="WPF中文网"
Foreground="White"
FontSize="60"
Canvas.Left="92"
Canvas.Top="75">
<i:Interaction.Behaviors>
<behavior:TextBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
<TextBlock Text="Behavior行为之阴影效果"
Foreground="White"
FontSize="40"
Canvas.Left="21"
Canvas.Top="182">
<i:Interaction.Behaviors>
<behavior:TextBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
</Canvas>
</Window>

最后,运行调试,鼠标移上去,文字将增加阴影,鼠标移开,文字阴影将消失。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:106-《什么是行为》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年11月21日
在上一节中,我们学习了什么是行为,并给TextBlock文字块开发一个阴影行为,但是那个TextBehavior只能给TextBlock控件使用,我们还可以进一步优化这个行为,使之所有控件都可以直接使用。
这一节,我们来优化一下这个阴影效果。新建一个ShadowBehavior类,并继承Behavior基类,T泛型参数改为UIElement。如下所示:
public class ShadowBehavior : Behavior<UIElement>
{
private DropShadowEffect effect = new DropShadowEffect();
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
effect.ShadowDepth = 0;
effect.BlurRadius = 5;
effect.Color = Colors.Black;
AssociatedObject.Effect = effect;
}
//鼠标离开去掉阴影
private void AssociatedObject_MouseLeave(object sender,
System.Windows.Input.MouseEventArgs e)
{
effect.BlurRadius = 5;
}
//鼠标进入增加阴影
private void AssociatedObject_MouseEnter(object sender,
System.Windows.Input.MouseEventArgs e)
{
effect.BlurRadius = 15;
}
}
这次我们将代码做了一些优化,比如只实例化了一个DropShadowEffect 对象,更节省内存。同时,由于Effect 属性定义在UIElement基类中,所以T参数就是UIElement。这样做的好处是,所有控件都可以共享这个行为了。比如下面的Border、TextBlock和Button都可以使用这个阴影效果。
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:controls="clr-namespace:HelloWorld.Controls"
xmlns:helper="clr-namespace:HelloWorld.MVVM"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behavior="clr-namespace:HelloWorld.Behaviors"
mc:Ignorable="d" Background="DarkCyan"
Title="WPF中文网 - 行为 - www.wpfsoft.com" Height="350" Width="500">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Canvas>
<Border Width="50" Height="50" CornerRadius="50"
Background="LightGoldenrodYellow"
Canvas.Left="228" Canvas.Top="20">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
</i:Interaction.Behaviors>
</Border>
<TextBlock Text="WPF中文网"
Foreground="White"
FontSize="60"
Canvas.Left="92"
Canvas.Top="75">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
<TextBlock Text="Behavior行为之阴影效果"
Foreground="White"
FontSize="40"
Canvas.Left="21"
Canvas.Top="182">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
<Button Content="退出" Width="80" Height="30"
FontSize="14"
Canvas.Left="375" Canvas.Top="265">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
</i:Interaction.Behaviors>
</Button>
</Canvas>
</Window>

F5运行调试,鼠标放到任意一个控件上,都将出现阴影效果的变化。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:107-《行为之UIElement阴影效果》-源代码
——重庆教主 2023年11月21日
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
关于控件的平移,我们之前学习转换时,曾经对TranslateTransform进行了讲解和示例。在那个示例中,我们只需要设置TranslateTransform的X属性和Y属性,使可以移动一个控件的位置。在学习了行为之后,我们肯定不希望一遍又一遍地去实现控件的移动效果,如果将移动效果做成一个行为,那么,任何一个控件,只要想拥有移动效果,直接调用行为即可,岂不快哉。
实现思路:
1、TranslateTransform平移对象最终赋值给RenderTransform属性,而RenderTransform位于UIElement基类中,所以,行为的泛型T参数应设置为UIElement。
2、TranslateTransform的X和Y是当前控件平移的位置坐标,这个坐标相对于当前控件的父控件而言的,所以在实现行为时,要拿到这个父控件。
3、我们采用鼠标去拖拽一个控件进行平移,所以要获取鼠标按下时的位置和移动过程中的位置,从而才知道TranslateTransform要平移到什么位置。
4、如果当前被移动的控件没有TranslateTransform,我们需要先给它实例化一个TranslateTransform对象。
根据上面的思路,我们来实现这个移动行为。
public class DragBehavior : Behavior<FrameworkElement>
{
protected bool isDragging = false;
private Point startPosition;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseDown += AssociatedObject_MouseDown;
AssociatedObject.MouseMove += AssociatedObject_MouseMove;
AssociatedObject.MouseUp += AssociatedObject_MouseUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseDown -= AssociatedObject_MouseDown;
AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
AssociatedObject.MouseUp -= AssociatedObject_MouseUp;
}
private void AssociatedObject_MouseDown(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
isDragging = true;
var parent = AssociatedObject.Parent as FrameworkElement;
var clickPosition = e.GetPosition(parent);
//首次按下时,判断RenderTransform的类型是否为TranslateTransform
if (!(AssociatedObject.RenderTransform is TranslateTransform))
{
AssociatedObject.RenderTransform = new TranslateTransform();
}
var transform = AssociatedObject.RenderTransform as TranslateTransform;
startPosition.X = clickPosition.X - transform.X;
startPosition.Y = clickPosition.Y - transform.Y;
AssociatedObject.CaptureMouse();//强制鼠标捕捉
}
private void AssociatedObject_MouseMove(object sender,
System.Windows.Input.MouseEventArgs e)
{
if (isDragging)
{
var parent = AssociatedObject.Parent as FrameworkElement;
Point currentPosition = e.GetPosition(parent);
var transform = AssociatedObject.RenderTransform as TranslateTransform;
transform.X = currentPosition.X - startPosition.X;
transform.Y = currentPosition.Y - startPosition.Y;
}
}
private void AssociatedObject_MouseUp(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
isDragging = false;
AssociatedObject.ReleaseMouseCapture();//释放鼠标捕捉
}
}
下一步,我们在需要移动的控件上引用这个行为。
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:controls="clr-namespace:HelloWorld.Controls"
xmlns:helper="clr-namespace:HelloWorld.MVVM"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behavior="clr-namespace:HelloWorld.Behaviors"
mc:Ignorable="d" Background="DarkCyan"
Title="WPF中文网 - 行为 - www.wpfsoft.com" Height="350" Width="500">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Canvas>
<Border Width="50" Height="50" CornerRadius="50"
Background="LightGoldenrodYellow"
Canvas.Left="228" Canvas.Top="20">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:DragBehavior/>
</i:Interaction.Behaviors>
</Border>
<TextBlock Text="WPF中文网"
Foreground="White"
FontSize="60"
Canvas.Left="92"
Canvas.Top="75">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:DragBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
<TextBlock Text="Behavior行为之阴影效果"
Foreground="White"
FontSize="40"
Canvas.Left="21"
Canvas.Top="182">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:DragBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
<Button Content="退出" Width="80" Height="30"
FontSize="14"
Canvas.Left="375" Canvas.Top="265">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:DragBehavior/>
</i:Interaction.Behaviors>
</Button>
</Canvas>
</Window>

这是鼠标移动之前的效果。

这是鼠标随意移动之后的效果。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:108-《行为之控件移动效果》-源代码
——重庆教主 2023年11月21日
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
这一节,我们来利用Behavior行为实现控件的缩放效果。要实现此效果,需要在鼠标的滚轮事件下,通过获取Delta值去设置ScaleTransform缩放对象的ScaleX和ScaleY,从而将控件进行放大或缩小。
下面是缩放行为的定义代码:
public class ScaleBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseWheel += AssociatedObject_MouseWheel;
}
private void AssociatedObject_MouseWheel(object sender,
System.Windows.Input.MouseWheelEventArgs e)
{
//首次滚动时,判断RenderTransform的类型是否为ScaleTransform
if (!(AssociatedObject.RenderTransform is ScaleTransform))
{
AssociatedObject.RenderTransform = new ScaleTransform()
{
CenterX = AssociatedObject.ActualWidth / 2,
CenterY = AssociatedObject.ActualHeight / 2,
};
}
var transform = AssociatedObject.RenderTransform as ScaleTransform;
double scale = e.Delta * 0.001;
transform.ScaleX += scale;
transform.ScaleY += scale;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseWheel -= AssociatedObject_MouseWheel;
}
}
然后,我们在上一节的代码中稍作修改,引用这个行为。
<Window x:Class="HelloWorld.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:HelloWorld"
xmlns:controls="clr-namespace:HelloWorld.Controls"
xmlns:helper="clr-namespace:HelloWorld.MVVM"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behavior="clr-namespace:HelloWorld.Behaviors"
mc:Ignorable="d" Background="DarkCyan"
Title="WPF中文网 - 行为 - www.wpfsoft.com" Height="350" Width="500">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Canvas>
<Border Width="50" Height="50" CornerRadius="50"
Background="LightGoldenrodYellow"
Canvas.Left="228" Canvas.Top="20">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:ScaleBehavior/>
</i:Interaction.Behaviors>
</Border>
<TextBlock Text="WPF中文网"
Foreground="White"
FontSize="60"
Canvas.Left="92"
Canvas.Top="75">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:ScaleBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
<TextBlock Text="Behavior行为之缩放效果"
Foreground="White"
FontSize="40"
Canvas.Left="21"
Canvas.Top="182">
<i:Interaction.Behaviors>
<behavior:ShadowBehavior/>
<behavior:ScaleBehavior/>
</i:Interaction.Behaviors>
</TextBlock>
</Canvas>
</Window>

缩放之前的效果

缩放之后的效果
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:109-《行为之控件缩放效果》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年11月21日