WPF中的图形绘制

WPF中一般可采用三种方法进行图形绘制,

1、采用shape进行图形绘制

SHAPE继承至FrameworkElement,具有可视化控件的一般属性,分别有Ellipse(圆或者椭圆)、Line(直线)、Path(绘制复杂图形的路径)、Polygon(多边形)、Polyline(相互连接的多条直线)、Rectangle(矩形),因为继承至FrameworkElement对象,上述形状对象可直接在XAML中进行设置,如需要在表格中绘制弧线时,代码如下:

myPolyline = new Polyline();
myPolyline.Stroke = System.Windows.Media.Brushes.SlateGray;
myPolyline.StrokeThickness = 2;
myPolyline.FillRule = FillRule.EvenOdd;
System.Windows.Point Point4 = new System.Windows.Point(1, 50);
System.Windows.Point Point5 = new System.Windows.Point(10, 80);
System.Windows.Point Point6 = new System.Windows.Point(20, 40);
PointCollection myPointCollection2 = new PointCollection();
myPointCollection2.Add(Point4);
myPointCollection2.Add(Point5);
myPointCollection2.Add(Point6);
myPolyline.Points = myPointCollection2;
myGrid.Children.Add(myPolyline);

2、采用Geometry进行图形绘制

Geometry是抽象类,继承至System.Windows.Media.Animation.Animatable,非界面可视化元素,需要和Path对象合用,实现二维图形数据剪辑、呈现。分别有CombinedGeometryEllipseGeometryGeometryGroupLineGeometryPathGeometryRectangleGeometryStreamGeometry继承至少该对象;CombinedGeometry表示组合定义两个Geometry表示的图形;

<Window x:Class="WPF.SimpleGraph.Combine"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Combine" Height="300" Width="300">
    <Canvas>
        <Path Stroke="Red" StrokeThickness="2" Fill="Yellow">
            <Path.Data>
                <CombinedGeometry GeometryCombineMode="Exclude">
                    <CombinedGeometry.Geometry1>
                        <EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
                    </CombinedGeometry.Geometry1>
                    <CombinedGeometry.Geometry2>
                        <EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
                    </CombinedGeometry.Geometry2>
                </CombinedGeometry>
            </Path.Data>
        </Path>
    </Canvas>
</Window>

上述代码,实现两个圆的互斥组合,当两个以上的形状需要组合时,可以将两两组合得到的CombinedGeometry继续和其它图形组合呈现。简单介绍一下其它继承至Geometry的对象:EllipseGeometry绘制圆或者椭圆形状;GeometryGroup实现多个Geometry对象的绘制;LineGeometry绘制直线,PathGeometry类似GeometryGroup实现多个Geometry对象的绘制,不但可以绘制图形形状(如:圆、长方形等),还可以绘制图形轮廓,如绘制直线、贝塞尔曲线等等,他提供了AddGeometry方法实现多个图形形状的组合,还提供了Figures属性,实复杂轮廓的绘制;PathGeometry的示例代码如下示意:

<Path Stroke="Black" StrokeThickness="1">
            <Path.Data>
                <!--指定Path.Data的填充是PathGeometry-->
                <PathGeometry>
                    <!--定义PathGeometry的Figuress-->
                    <PathGeometry.Figures>
                        <PathFigureCollection>
                            <!--PathFigure表示几何图形的一个子部分、一系列单独连接的二维几何线段。
                            IsClosed:获取或设置一个值,该值指示是否连接该图形的第一条线段和最后一条线段。 -->
                            <PathFigure IsClosed="True" StartPoint="50,100">
                                <PathFigure.Segments>
                                    <BezierSegment Point1="100,0" Point2="200,200" Point3="300,100"/>
                                    <LineSegment Point="400,100" />
                                    <ArcSegment Size="50,50" RotationAngle="45" IsLargeArc="False"  SweepDirection="Clockwise" Point="200,100"/>
                                </PathFigure.Segments>
                            </PathFigure>
                        </PathFigureCollection>
                    </PathGeometry.Figures>
                </PathGeometry>
            </Path.Data>
        </Path>

非页面代码,即.cs编写代码如下示意:

            ArcSegment arc = new ArcSegment();
            PathFigure figure = new PathFigure();
            figure.Segments.Add(new ArcSegment());
            pLines.Figures.Add(figure);

PathGeometryGeometryGroup均可实现复杂图形的绘制,差别在于PathGeometry更灵活,而GeometryGroup可实现多个图形的组合绘制

StreamGeometryPathGeometry 的轻量级替代:不支持数据绑定、动画或修改,在需要绘制大量图形的场合,使用StreamGeometry能取得更高的绘制效率

3、使用 画刷(Brush)进行图形绘制

继承至Brush的画刷分为两大类,其中 SolidColorBrush、GradientBrush实现颜色绘制,SolidColorBrush是单色绘制,而GradientBrush采用渐变颜色进行绘制;另外一大类则是TileBrush,ImageBrush、VisualBrush以及 DrawingBrush均继承至TileBrush,利用这三类画刷,能够实现控制复杂背景图形的绘制。

VisualBrush 绘制按钮背景:

<Window x:Class="WpfApplication1.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow" Height="350" Width="525">
 5     <Window.Resources>
 6         <VisualBrush x:Key="test" TileMode="Tile" Opacity="0.8">
 7             <VisualBrush.Visual>
 8                 <StackPanel>
 9                     <TextBlock Foreground="Gold">
10                         唧唧复唧唧
11                     </TextBlock>
12                     <TextBlock Foreground="LightBlue">
13                        木兰开飞机
14                     </TextBlock>
15                     <TextBlock Foreground="LightGray">
16                        开的什么机
17                     </TextBlock>
18                     <TextBlock Foreground="Pink">
19                        波音747
20                     </TextBlock>
21                 </StackPanel>
22             </VisualBrush.Visual>
23         </VisualBrush>
24     </Window.Resources>
25     <Grid>
26         <Button Content="我是超大按钮" Height="213" HorizontalAlignment="Left" Margin="32,34,0,0" Name="button1" 
27                 VerticalAlignment="Top" Width="414" Background="{StaticResource ResourceKey=test}"/>
28     </Grid>
29 </Window>

DrawingBrush绘制背景:

 <Window x:Class="WpfApplication4.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow" Height="350" Width="525">
 5     <Window.Resources>
 6         <DrawingBrush x:Key="test">
 7             <DrawingBrush.Drawing>
 8                 <DrawingGroup>
 9                     <DrawingGroup.Children>
10                         <GeometryDrawing>
11                             <!-- 绘制矩形 -->
12                             <GeometryDrawing.Geometry>
13                                 <RectangleGeometry RadiusX="0.2" RadiusY="0.5"
14                                                        Rect="0.02,0.02,0.96,0.96" />
15                             </GeometryDrawing.Geometry>
16                             <!-- 矩形填充色 -->
17                             <GeometryDrawing.Brush>
18                                 <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
19                                     <GradientStop Color="Green" Offset="0" />
20                                     <GradientStop Color="Red" Offset="1" />
21                                 </LinearGradientBrush>
22                             </GeometryDrawing.Brush>
23                             <!-- 矩形边框 -->
24                             <GeometryDrawing.Pen>
25                                 <Pen Thickness="0.02">
26                                     <Pen.Brush>
27                                         <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
28                                             <GradientStop Color="AliceBlue" Offset="0" />
29                                             <GradientStop Color="Black" Offset="1" />
30                                         </LinearGradientBrush>
31                                     </Pen.Brush>
32                                 </Pen>
33                             </GeometryDrawing.Pen>
34                         </GeometryDrawing>
35                     </DrawingGroup.Children>
36                 </DrawingGroup>
37             </DrawingBrush.Drawing>
38         </DrawingBrush>
39     </Window.Resources>
40     <Grid>
41         <Button Background="{StaticResource ResourceKey=test}" FontSize="40" Content="Button" Height="113" HorizontalAlignment="Left" Margin="89,80,0,0" Name="button1" VerticalAlignment="Top" Width="292" />
42     </Grid>
43 </Window>

3、使用 DrawVisual进行图形绘制

WPF进行大数量的绘制时,采用DrawVisual结合StreamGeometry能够比较显著的提供绘图效率,绘制节点数在十万以内时,基本可以在秒级范围内完成绘制(感觉会有2-3秒的卡顿)。

DrawVisual是一种轻型绘制用于呈现形状、 图像或文本的类,它不提供布局或事件处理,从而性能得以提升。

下面的的示例演示了DrawVisual的使用:

    public class EnCanvas:Canvas
    {
        DrawingVisual visual1 = null, visual2 = null, visual3 = null, lineVisual = null;

        private List<Visual> visuals = new List<Visual>();


        static EnCanvas()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(EnCanvas), new FrameworkPropertyMetadata(typeof(EnCanvas)));


        }
        public EnCanvas()
        {
              this.Background = new SolidColorBrush(Colors.Black);
        }

        //===============绘制背景线条=========================
        protected void AddLineToVisual(DrawingVisual visual)
        {
            DrawingContext dcc = visual.RenderOpen();
            double width = this.ActualWidth;
            double height = this.ActualHeight;

            int interval = 3;
            int lines = (int)width / interval;

            if (((int)width % interval) != 0)
                lines += 1;

            PathGeometry pLines = new PathGeometry();
            pLines.FillRule = FillRule.EvenOdd;
            Pen pen = new Pen();
            pen.Thickness = 0.5;
            pen.Brush = new SolidColorBrush(Colors.Red);
            pen.Freeze();  //冻结可加快绘制速度

            for (int i = 0; i <= lines; i++)
            {
                Point tmp;
                tmp = new Point((i) * interval, 0);
                if (tmp.X > this.ActualWidth)
                    tmp.X = this.ActualWidth;
                Point StartPoint = this.TranslatePoint(tmp, this);

                tmp = new Point((i) * interval, height);
                if (tmp.X > this.ActualWidth)
                    tmp.X = this.ActualWidth;
                Point EndPoint = this.TranslatePoint(tmp, this);

                dcc.DrawLine(pen, StartPoint, EndPoint);
            }
            lines = (int)height / interval;

            for (int i = 0; i <= lines; i++)
            {
                Point tmp = new Point(0, (i) * interval);
                if (tmp.Y > this.ActualHeight)
                    tmp.Y = this.ActualHeight;
                Point StartPoint = this.TranslatePoint(tmp, this);

                tmp = new Point(width, (i) * interval);
                Point EndPoint = this.TranslatePoint(tmp, this);
                dcc.DrawLine(pen, StartPoint, EndPoint);
            }

            dcc.Close();
        }

//==============绘制图形=================================
        protected void AddDrawToVisual(DrawingVisual visual,int StartX,int StartY)
        {
            DrawingContext dcc= visual.RenderOpen();
  

            PathGeometry pLines = new PathGeometry();
            pLines.FillRule = FillRule.EvenOdd;
            Pen pen = new Pen();
            pen.Thickness = 0.5;
            pen.Brush = new SolidColorBrush(Colors.Red);
            pen.Freeze();  //冻结可加快绘制速度
            Brush brush = new SolidColorBrush(Colors.Red);

            //============经过试验验证,使用StreamGeometry较之直接的DrawLine效率会有大幅提高========================
            pLines.AddGeometry(UseStreamGeometry(StartX, StartY));          


            dcc.DrawGeometry(new SolidColorBrush(Colors.Red), pen, pLines);
            dcc.Close();
        }

//===================重写Onrender方法,实现在Canvas中绘制图像========================
        protected override void OnRender(DrawingContext dc)
        {

            if (visual1 != null)
                this.RemoveVisual(visual1);
            if (visual2 != null)
                this.RemoveVisual(visual2);
            if (visual3 != null)
                this.RemoveVisual(visual3);
            if (lineVisual != null)
                this.RemoveVisual(lineVisual);


            visual1 = new DrawingVisual();
            AddDrawToVisual(visual1, 100, 100);
            this.AddVisual(visual1);

            lineVisual = new DrawingVisual();
            AddLineToVisual(lineVisual);
            this.AddVisual(lineVisual);

            base.OnRender(dc);
        }

//==============采用StreamGeometry绘制图形======================
        private StreamGeometry UseStreamGeometry(int X, int Y)
        {         
            StreamGeometry streamGeometry = new StreamGeometry();
            StreamGeometryContext sContext = streamGeometry.Open();

            for (int i = 0; i <100000; i++)
            {
                int x = X + i * 2;
                int y = Y + i * 2;
                sContext.BeginFigure(new Point(x, y), true, true);
                List<Point> points = new List<Point>();
                points.Add(new Point(x + 20, y));
                points.Add(new Point(x, y + 20));

                sContext.PolyLineTo(points, true, true);
            }

            sContext.Close();


            return streamGeometry;
        }

//========将图形增加到逻辑树和可视化树=====================
        public void AddVisual(Visual visual)
        {
            visuals.Add(visual);

            base.AddVisualChild(visual);
            base.AddLogicalChild(visual);
        }

        //删除Visual
        public void RemoveVisual(Visual visual)
        {
            visuals.Remove(visual);

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

        protected override int VisualChildrenCount
        {
            get { return visuals.Count; }
        }



        //获取Visual
        protected override Visual GetVisualChild(int index)
        {
            return visuals[index];
        }

    }

关于大数据量图形绘制chu出现卡顿问题的解决:

1、图形绘制速度慢主要因为读取数据,并创建图形数据组成较慢造成,比如绘制一条由数十万个点组成的曲线时,由于需要将点数据添加到Geometry中,tian添加Geometry的过程将消耗比较多的时间,可cai采用异步的方式先形成图点数据然后在绘制图形将有效优化图形的绘制速度,对te'别复杂的数据,还可以将图形分解为几个组成部分,并行的绘制的组成,也能够比较ming明显的提供绘图效率。代码示例:

private void EnCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (isChanging)
            {
                e.Handled = true;
                return;
            }

            isChanging = true;
            if (visual1 != null)
                this.RemoveVisual(visual1);
            if (visual2 != null)
                this.RemoveVisual(visual2);
            if (visual3 != null)
                this.RemoveVisual(visual3);

            visual1 = new DrawingVisual();
            visual2 = new DrawingVisual();
            visual3 = new DrawingVisual();


            Func<List<PathGeometry>> func=new Func<List<PathGeometry>>(() =>
            {
                System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();//实例化类
                st.Start();//开始计时

                //===================需要在线程间共享的Freezable类继承对象,必须设置为冻结后才能在线程间共享===================
                PathGeometry pLines1 = AddDraws(100, 100);
                PathGeometry pLines2 = AddDraws(120, 120);
                PathGeometry pLines3 = AddDraws(140, 140);
                pLines1.Freeze();
                pLines2.Freeze();
                pLines3.Freeze();
                st.Stop();

                System.Diagnostics.Debug.WriteLine(String.Format("执行时间为:{0}",st.ElapsedMilliseconds));
                return new List<PathGeometry>(new PathGeometry[] { pLines1,pLines2,pLines3});
            });

            IAsyncResult ar= func.BeginInvoke(new AsyncCallback(funcCB), null);                      
            //aVisual1.BeginInvoke( null, null);

        }
        protected void funcCB(System.IAsyncResult ar)
        {
            System.Runtime.Remoting.Messaging.AsyncResult aes = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
            List<PathGeometry> figures=((Func<List<PathGeometry>>)aes.AsyncDelegate).EndInvoke(ar);

            if (ar.IsCompleted)
            {
                this.Dispatcher.BeginInvoke(new Action<PathGeometry[]>(p =>
                {
                    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                    sw.Start();

                    Pen pen = new Pen();
                    pen.Thickness = 0.5;
                    pen.Brush = new SolidColorBrush(Colors.Red);
                    pen.Freeze();  //冻结可加快绘制速度
                    DrawingContext dcc = null;
                    try
                    {
                        dcc = visual1.RenderOpen();
                        dcc.DrawGeometry(Brushes.Red, pen, p[0]);
                        dcc.Close();
                        this.AddVisual(visual1);
                    }
                    catch (Exception ee)
                    {
                        System.Diagnostics.Debug.Write(ee.Message);
                    }


                    dcc = visual2.RenderOpen();
                    dcc.DrawGeometry(new SolidColorBrush(Colors.Red), pen, p[1]);
                    dcc.Close();
                    this.AddVisual(visual2);

                    dcc = visual3.RenderOpen();
                    dcc.DrawGeometry(new SolidColorBrush(Colors.Red), pen, p[2]);
                    dcc.Close();
                    this.AddVisual(visual3);

                   
                    this.InvalidateVisual();
                    sw.Stop();
                    System.Diagnostics.Debug.WriteLine(String.Format("the drawline time is {0}", sw.ElapsedMilliseconds));

                }), new Object[] { new PathGeometry[] { figures[0], figures[1], figures[2] } });
            }

            isChanging = false;
            

        }

猜你喜欢

转载自blog.csdn.net/u012846041/article/details/81180803
今日推荐