WPF高性能绘图之DrawingCanvas、DrawingVisual、Button的同时使用

当有大量图形需要绘制的时候,WPF推荐的是采用DrawingVisual结合DrawingCanvas进行绘制。

WPF中给出的DrawingCanvas代码如下,它从Canvas继承,并改写了VisualChildrenCount只读属性、GetVisualChild方法、AddVisual、DeleteVisual和ClearVisuals方法。然而,该代码不能显示Button之类的元素。

 public class DrawingCanvas:Canvas
    {
        private List<Visual> visuals = new List<Visual>();
        protected override int VisualChildrenCount
        {
            get
            { return visuals.Count; }
        }
        protected override Visual GetVisualChild(int index)
        {
            return visuals[index];
        }
        public void AddVisual(Visual visual)
        {
            visuals.Add(visual);
                base.AddVisualChild(visual);
                base.AddLogicalChild(visual);


        }
        public void DeleteVisual(Visual visual)
        {
            visuals.Remove(visual);


                base.RemoveVisualChild(visual);
                base.RemoveLogicalChild(visual);

        }
        public void ClearVisual()
        {
            foreach (var visual in visuals)
            {
 
                  base.RemoveLogicalChild(visual);
                    base.RemoveVisualChild(visual);
                
                
            }
            visuals.Clear();
        }

        public DrawingVisual GetVisual(Point point)
        {
            HitTestResult hitResult = VisualTreeHelper.HitTest(this, point);
            return hitResult.VisualHit as DrawingVisual;

        }

    }

为了改变上述状况,有两种方法。

方法一:

在DrawingCanvas中同时使用visuals列表和Children属性来存储DrawingVisuals对象和Button对象,代码如下所示。即在AddVisual方法中,先添加到visuals中,然后判断是否为button,如果是button则添加到Children中去,如果不是button则认为是DrawingVisual,则分别添加到可视化子对象集和逻辑子对象集中去。本来以为这种方法是可行的,用鼠标操作Button确实也可以,没什么问题。但是如果是采用触摸屏操作,则Button对象的TouchUp事件、TouchMove事件和Click事件无法被程序(用户)捕获,即无法触发该事件,只有TouchDown事件可以被触发。那怎么办呢?后来发现,当给Button的TouchDown事件绑定事件处理程序,并在事件处理程序中写出e.Handled=true,这时TouchUp事件、TouchMove事件和Click事件就都可以被触发了。 此方法即可以正常使用。

??注意:如果把DrawingCanvas放在ContentControl的ContentTemplate模板中使用的时候,可能出现问题(child与父级元素断开之类的异常),不知道如何解决该问题,所以暂时没有把DrawingCancas放在ContentTemplate中使用。

 public class DrawingCanvas:Canvas
    {
        private List<Visual> visuals = new List<Visual>();
        protected override int VisualChildrenCount
        {
            get
            { return visuals.Count; }
        }
        protected override Visual GetVisualChild(int index)
        {
            return visuals[index];
        }
        public void AddVisual(Visual visual)
        {
            visuals.Add(visual);


            if(visual is Button btn)
            {
                Children.Add(btn);       //在Canvas的Children中添加子元素
            }
            else
            {
                base.AddVisualChild(visual);
                base.AddLogicalChild(visual);
            }

        }
        public void DeleteVisual(Visual visual)
        {
            visuals.Remove(visual);

            if (visual is Button btn)
            {
                Children.Remove(btn);
            }
            else
            {
                base.RemoveVisualChild(visual);
                base.RemoveLogicalChild(visual);
            }
        }
        public void ClearVisual()
        {
            foreach (var visual in visuals)
            {
                if (visual is Button btn)
                {
                    Children.Remove(btn);
                }
                else
                {   base.RemoveLogicalChild(visual);
                    base.RemoveVisualChild(visual);
                
                }
            }
            visuals.Clear();
        }

        public DrawingVisual GetVisual(Point point)
        {
            HitTestResult hitResult = VisualTreeHelper.HitTest(this, point);
            return hitResult.VisualHit as DrawingVisual;

        }

    }

方法二:

Bird鸟人在 “WPF DrawingVisual详解”一文中建议的:在Grid中放置DrawingCanvas用于绘制DrawingVisual,然后在放置Canvas来内置各种控件。DrawingCanvas和Canvas重叠起来。 (说明:原文描述是这样的:"在Grid中放置Panel用于绘制DrawingVisual,然后在放置Canvas来内置各种控件。Panel和Canvas重叠起来。"         本质是一样的,只是Panel和DrawingVisual的起名不同而已。)

此种方法的总体代码如下所示,我经过测试,这种方法是可行的,这确实是很好的思路,感谢Bird鸟人提供的思路。

不过需要注意的是:如果在Canvas元素或者外围包装的Grid元素中使用IsManipulationEnabled=true,那么Canvas中添加的所有Button的TouchMove、TouchUp和Click事件都无法被捕获到,即这些事件不能使用了。为了解决这个问题,这时需要给每个Button对象绑定一个TouchDown处理程序,并将TouchDown处理程序中添加e.Handled=true,然后TouchMove、TouchUp和Click事件就可以正常使用了,参见文章“WPF组件中当设置IsManipulationEnabled="True"时导致TouchUp不能被触发的解决方法”。

 <Grid  Name="TouchPadWrapper">
        <local:DrawingCanvas Name="DrawingVisualCanvas"></local:DrawingCanvas>
        <Canvas Name="ButtonCanvas"></Canvas>
 </Grid>

 

 

WPF是一种基于向量图形的绘图方式,它能够让用户以较小的开销生成高质量的图形。为了实现WPF高性能绘图,我们需要采取以下几个方法: 1. 使用硬件加速:在WPF中,硬件加速是默认开启的。利用现代GPU进行绘制,可以大幅提高绘制速度。因此,我们可以使用硬件加速来优化WPF绘图性能。 2. 双缓冲:WPF的UI元素在绘制时,会自动进行双缓冲,即先将图像绘制到一个后台缓冲区,然后再将其绘制到前台缓冲区。双缓冲可以防止图像闪烁,也可以提高绘图速度。 3. 利用可视化子系统:WPF的可视化子系统支持多种绘图API,包括DrawingContext、RenderTargetBitmap和WriteableBitmap等。我们可以根据需要选择合适的API来进行绘图,以达到最优的绘图效果。 4. 缓存可重用的图形:WPF的可视化元素支持缓存,我们可以将可重复使用的UI元素进行缓存,以便将来直接调用缓存元素进行渲染,避免重复绘制,提升绘图效率。 5. 优化绘图算法:在一些复杂的绘图场景下,使用优化的绘图算法可以大幅提高绘图速度。例如,在绘制大量点时,我们可以将点集合并为一个多边形进行绘制,而不是一个个点逐个绘制。 总的来说,WPF高性能绘图需要综合考虑多个方面。我们需要充分利用硬件加速、双缓冲、可视化子系统、元素缓存等技术手段,同时优化渲染算法,才能在实际应用中获得最优的绘图效果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值