快速 BitmapEffects 解决方案
Identitymine 的 ElementSnapshot 的无耻(肯定有 bug)克隆

引言
如果你用过BitmapEffects
,你可能很快就停止使用了,因为它们会占用大量的CPU资源。但是,你的救星来了:SnapshotPanel
!
它只在包含的元素调整大小时才渲染它们,从而使BitmapEffects
变得非常快。
Using the Code
废话不多说,代码如下:
(抱歉缺少注释。也许我以后会修复这个问题。Arrange
和Measure
的代码大部分是从FishEyePanel
项目复制粘贴过来的。)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfTriangelo
{
class SnapshotPanel: Panel
{
RenderTargetBitmap bmp;
public SnapshotPanel()
{
this.Background = Brushes.Transparent;
}
bool aSizeChange = true, mSizeChange = true;
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
UpdateSnapshot();
base.OnRenderSizeChanged(sizeInfo);
}
void ChildChangedUpdate(Panel p)
{
foreach (UIElement c in p.Children)
{
var cinp = c as INotifyPropertyChanged;
if (cinp != null)
{
cinp.PropertyChanged -=
new PropertyChangedEventHandler(cinp_PropertyChanged);
cinp.PropertyChanged +=
new PropertyChangedEventHandler(cinp_PropertyChanged);
}
var cp = c as Panel;
if (cp != null)
ChildChangedUpdate(cp);
}
}
void cinp_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
UpdateSnapshot();
}
protected override void OnVisualChildrenChanged
(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
ChildChangedUpdate(this);
}
private void UpdateSnapshot()
{
aSizeChange = mSizeChange = true;
int w = (int)this.ActualWidth;
int h = (int)this.ActualHeight;
w = w > 0 ? w : 1;
h = h > 0 ? h : 1;
bmp = new RenderTargetBitmap(w, h, 96, 96, PixelFormats.Pbgra32);
this.InvalidateVisual();
var b = BitmapFrame.Create(bmp);
bmp.Render(this);
this.Background = new ImageBrush(b);
}
protected override Size ArrangeOverride(Size finalSize)
{
if (this.Children == null || this.Children.Count == 0 || aSizeChange != true)
return finalSize;
aSizeChange = false;
foreach (UIElement child in this.Children)
{
child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
}
return finalSize;
}
protected override Size MeasureOverride(Size availableSize)
{
if (!mSizeChange)
return availableSize;
mSizeChange = false;
Size idealSize = new Size(0, 0);
// Allow children as much room as they want - then scale them
//Size size = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
foreach (UIElement child in Children)
{
child.Measure(availableSize);
idealSize.Width += child.DesiredSize.Width;
idealSize.Height += child.DesiredSize.Height;
}
// EID calls us with infinity, but framework
// doesn't like us to return infinity
if (double.IsInfinity(availableSize.Height) ||
double.IsInfinity(availableSize.Width))
return idealSize;
else
return availableSize;
}
protected override Visual GetVisualChild(int index)
{
return base.GetVisualChild(index);
}
protected override System.Collections.IEnumerator LogicalChildren
{
get
{
return base.LogicalChildren;
}
}
protected override int VisualChildrenCount
{
get
{
if (aSizeChange)
return base.VisualChildrenCount;
else
return 0;
}
}
}
}
它只在一个子元素上进行了测试,并且该子元素应该设置了height
和width
。否则,屏幕上将不会显示任何内容。
关注点
隐藏子元素的真正魔力在于VisualChildrenCount
。我想这不言而喻。
我附上了一个小型的测试项目。只需删除DataTemplate
中的SnapshotPanel
,你就会看到它的运行速度有多慢。(我知道它加载得更快,但你必须做出权衡。)
PS:演示项目也是DataBinding
和Converters
的一个很好的例子,因为石头上的点是数据绑定的……
谢谢
感谢Josh Smith和Sacha Barber:你们的文章帮助我理解了WPF!
历史
- 更新 1:修复了不必要的
Grid
,从而减少了绑定元素的渲染。