1. 什么是XamlCompositionBrushBase
我早就想试试自定义XamlCompositionBrushBase,但一直没机会。上一篇文章介绍到使用Win2D的BorderEffect实现图片的平铺功能,原理很简单,但每次都要写这些代码很繁琐,正好就用这个作为例子试试XamlCompositionBrushBase。
CompositionBrush灵活多变,它的基本用法如下:
- 通过Compositor创建CompositionBrush;
- 配置CompositionBrush;
- 创建SpriteVisual并将它的Brush设置为CompositionBrush;
- 使用ElementCompositionPreview.SetElementChildVisual 将SpriteVisual设置到某个UIElement的可视化层里。
这些步骤很繁琐,而且不能用在XAML中。XamlCompositionBrushBase提供了将CompositionBrush用在XAML中一个桥梁,他继承自Brush
类,可以直接像普通的XAML 画笔(如SolidColorBrush)那样直接用在XAML中。
如上图所示,Windows Community Toolkit中已经提了很不少XamlCompositionBrushBase的实现,它们的使用方式已经有很多文章介绍,这里不一一列举。
2. 自定义XamlCompositionBrushBase
这篇文章将介绍一个自定义的画笔:TiledImageBrush
,它的主要目标是实现ImageBrush没有的图片平铺功能,并且它可以在XAML中使用,使用方式如下:
<Rectangle IsHitTestVisible="False">
<Rectangle.Fill>
<controls:TiledImageBrush Source="ms-appx:///Assets/flutter.png"/>
</Rectangle.Fill>
</Rectangle>
顺便复习下普通的ImageBrush的用法:
<Rectangle >
<Rectangle.Fill>
<ImageBrush ImageSource="ms-appx:///Assets/flutter.png"/>
</Rectangle.Fill>
</Rectangle>
看起来TiledImageBrush的用法是不是和ImageBrush很像?接下来讲解TiledImageBrush
的实现步骤。TiledImageBrush
继承自XamlCompositionBrushBase
,而实现XamlCompositionBrushBase
的一般步骤如下:
protected override void OnConnected()
{
// Delay creating composition resources until they're required.
if (CompositionBrush == null)
{
CompositionBrush = CreateCompositionBrush();//Create A CompositionBrush.
}
}
protected override void OnDisconnected()
{
// Dispose of composition resources when no longer in use.
if (CompositionBrush != null)
{
CompositionBrush.Dispose();
CompositionBrush = null;
}
}
首先重写OnConnected,当画笔在屏幕上首次用于绘制元素时会调用这个函数。在这个函数里创建CompositionBrush并赋值给XamlCompositionBrushBase.CompositionBrush。
然后重写OnDisconnected,它在画笔不再用于绘制任何元素时被调用。在这个函数里尽可能地释放各种资源,例如CompositionBrush
。这两步就是实现XamlCompositionBrushBase
的基本步骤。
创建CompositionBrush有很多种玩法,我之前写过两篇文章分别介绍 CompositionBrush入门及 在CompositionBrush上使用Effect。这里使用使用Win2D的BorderEffect实现图片的平铺功能这篇文章里介绍到的代码,首先使用LoadedImageSurface.StartLoadFromUri
创建CompositionSurfaceBrush
,然后加入到BorderEffect里实现图片平铺,然后把产生的CompositionEffectBrush赋值给XamlCompositionBrushBase.CompositionBrush
。
TiledImageBrush中添加了Source
属性用于设置图片Uri(实际上是个ImageSource类型),模仿ImageBrush,这里的Source也是一个ImageSource类型的属性,虽然实际上使用的是它的UriSource。详细代码如下:
public ImageSource Source
{
get => (ImageSource)GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
private void UpdateSurface()
{
if (Source != null && _surfaceBrush != null)
{
var uri = (Source as BitmapImage)?.UriSource ?? new Uri("ms-appx:///");
_surface = LoadedImageSurface.StartLoadFromUri(uri);
_surfaceBrush.Surface = _surface;
}
}
OnConnected
的详细代码如下:
protected override void OnConnected()
{
base.OnConnected();
if (CompositionBrush == null)
{
_surfaceBrush = Compositor.CreateSurfaceBrush();
_surfaceBrush.Stretch = CompositionStretch.None;
UpdateSurface();
_borderEffect = new BorderEffect()
{
Source = new CompositionEffectSourceParameter("source"),
ExtendX = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap,
ExtendY = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap
};
_borderEffectFactory = Compositor.CreateEffectFactory(_borderEffect);
_borderEffectBrush = _borderEffectFactory.CreateBrush();
_borderEffectBrush.SetSourceParameter("source", _surfaceBrush);
CompositionBrush = _borderEffectBrush;
}
}
这样一个基本的XamlCompositionBrush就完成了,完整的代码可以在这里查看:
OnePomodoro_TiledImageBrush.cs at master
3. 参考
XamlCompositionBrushBase Class (Windows.UI.Xaml.Media) - Windows UWP applications _ Microsoft Docs
WindowsCommunityToolkit_Microsoft.Toolkit.Uwp.UI.Media_Brushes at master
Working with Brushes and Content – XAML and Visual Layer Interop, Part One - Windows Developer Blog