在我们的常见的软件界面设计中我们经常会设置软件的背景为SolidColorBrush或者LinerColorBrush、RadialGradientBrush 等一系列的颜色画刷为背景,有时我们也会使用ImageBrush添加图片来作为界面的背景,另外常用的还有DrawingBrush以及今天需要进行总结的VisualBrush,这些我们都是比较容易实现的,那么我们如果想将软件的界面设计成一个动画或者干脆播放一段视频作为背景,这个对于整个软件的效果又是一个巨大的提升。
首先我们来看看background属性,MSDN上的解释是:获取或设置用于填充控件Control的边框之间的区域的 Brush。它的类型是:Type: System.Windows.Media.Brush,所以我们能够使用具有Brush属性的控件或者属性来作为背景来填充它。
我们首先来看看前台样式的设计:
<Window x:Class="TestBackGroundWorker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:X.UI;assembly=X.UI" xmlns:local="clr-namespace:TestBackGroundWorker" Title="MainWindow" Height="681" Width="1000"> <Window.Resources> <Style TargetType="local:BackgroundPlayer"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:BackgroundPlayer"> <MediaElement x:Name="Media" Stretch="Fill"></MediaElement> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <Grid.Background> <VisualBrush> <VisualBrush.Visual> <local:BackgroundPlayer Source="~/Images/bg.avi"></local:BackgroundPlayer> </VisualBrush.Visual> </VisualBrush> </Grid.Background> </Grid> </Window>
在这里我们用到了VisualBrush这个画刷,然后再在VisualBrush的Visual中添加我们自定义的Style,这个是一个非常重要的画刷,如果我们对WPF的继承关系清楚的话,我们会发现几乎所有的控件都是从Visual这个*的基类继承过来的,所以几乎所有的Control都能够作为VisualBrush的Visual,所以Grid的Background属性是十分丰富的,这里我们定义了一个BackgroundPlayer的自定义控件,并且更改了其控件模板。
那么我们再重点看一下这个自定义控件的后台是如何进行定义的:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace TestBackGroundWorker { public class BackgroundPlayer : System.Windows.Controls.Control { static BackgroundPlayer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BackgroundPlayer), new FrameworkPropertyMetadata(typeof(BackgroundPlayer))); } public string Source { get { return (string)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(BackgroundPlayer), new FrameworkPropertyMetadata("", (a, b) => { BackgroundPlayer bp = a as BackgroundPlayer; if (bp.Player != null) { bp.Player.Source = new Uri(b.NewValue.ToString().Replace("~", AppDomain.CurrentDomain.BaseDirectory), UriKind.RelativeOrAbsolute); } })); private MediaElement _player; public MediaElement Player { get { return _player; } set { _player = value; } } public override void OnApplyTemplate() { Player = GetTemplateChild("Media") as MediaElement; if (null == Player) throw new ArgumentNullException("Media"); Player.LoadedBehavior = MediaState.Manual; Player.MediaEnded += Player_MediaEnded; Player.MediaOpened += Player_MediaOpened; Player.MediaFailed += Player_MediaFailed; Player.Loaded += Player_Loaded; if (!string.IsNullOrEmpty(Source)) { Player.Source = new Uri(Source.Replace("~", AppDomain.CurrentDomain.BaseDirectory), UriKind.RelativeOrAbsolute); Player.Play(); } base.OnApplyTemplate(); } void Player_Loaded(object sender, RoutedEventArgs e) { } void Player_MediaFailed(object sender, ExceptionRoutedEventArgs e) { } void Player_MediaOpened(object sender, RoutedEventArgs e) { //Player.Play(); } void Player_MediaEnded(object sender, RoutedEventArgs e) { Player.Position = TimeSpan.FromMilliseconds(1); Player.Play(); } } }
这里我们的自定义控件是从System.Windows.Controls.Control这里继承过来的,我们看一下相关的代码,然后做进一步的分析。首先我们必须为当前的类添加一个默认的静态构造函数,这个是非常重要的,它会更改默认的Control的样式。
static BackgroundPlayer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BackgroundPlayer), new FrameworkPropertyMetadata(typeof(BackgroundPlayer))); }
接下来我们需要为自定义控件定义一些默认的依赖项属性,首先第一个是:Source属性,既然我们将Control的属性定义为MediaElement,那么我们一定要为这个MediaElement设置相关的属性,这里我们为这个Source属性定义了一个当属性变化时的回调函数,这个在xaml中第一次为Source属性赋值时就会触发该回调函数,所以我们经常可以利用该回调函数来做一些操作,这个回调函数是非常有用的。
另外我们还需要为MediaElement设置一些常见的属性,比如说当前片源放完成后会后续进行什么操作等。
这里我们是通过重载基类的OnApplyTemplate方法来进行相关的操作的,我们在了解WPF时必须懂得,去重载这些常见的虚方法来完成我们的操作。当然这也需要我们不断去积累。
我们来看看OnApplyTemplate这个虚方法是为了完成什么样的操作,通过重写OnApplyTemplate()后就可以通过GetTemplateChild方法获取模板里的子控件了,但是OnApplyTemplate的执行顺序有问题,当类的构造函数执行后,并不是立即执行OnApplyTemplate方法,而是延迟了一定的时间,而且如果这个自定义的控件在放到应用的项目中时如果Visibility为隐藏的话,更是不会执行OnApplyTemplate方法了,这点需要我们去留心,在我们的程序中也是通过重写OnApplyTemplate方法来获取MediaElement对象,从而为其添加事件,这是一种非常有效果的方式。
最后贴出相应的截图看看动画的效果(星空风格图)
完整的视频是一副移动的画面,这里只是截出了很少的照片,正常播放时整个都面都动,上面的文章只是简约去分析,仅供参考。