65.9K
CodeProject 正在变化。 阅读更多。
Home

快速 BitmapEffects 解决方案

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.27/5 (7投票s)

2008年4月27日

CPOL

1分钟阅读

viewsIcon

19835

downloadIcon

183

Identitymine 的 ElementSnapshot 的无耻(肯定有 bug)克隆

引言

如果你用过BitmapEffects,你可能很快就停止使用了,因为它们会占用大量的CPU资源。但是,你的救星来了:SnapshotPanel

它只在包含的元素调整大小时才渲染它们,从而使BitmapEffects变得非常快。

Using the Code

废话不多说,代码如下:
(抱歉缺少注释。也许我以后会修复这个问题。ArrangeMeasure的代码大部分是从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;
            }
        }
    }
}

它只在一个子元素上进行了测试,并且该子元素应该设置了heightwidth。否则,屏幕上将不会显示任何内容。

关注点

隐藏子元素的真正魔力在于VisualChildrenCount。我想这不言而喻。

我附上了一个小型的测试项目。只需删除DataTemplate中的SnapshotPanel,你就会看到它的运行速度有多慢。(我知道它加载得更快,但你必须做出权衡。)

PS:演示项目也是DataBindingConverters的一个很好的例子,因为石头上的点是数据绑定的……

谢谢

感谢Josh Smith和Sacha Barber:你们的文章帮助我理解了WPF!

历史

  • 更新 1:修复了不必要的Grid,从而减少了绑定元素的渲染。
© . All rights reserved.