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

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

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

 public class DrawingCanvas:Canvas
    {
        private List visuals = new List();
        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 visuals = new List();
        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不能被触发的解决方法”。

 
        
        
 

 

 

你可能感兴趣的:(wpf开发)