zoukankan      html  css  js  c++  java
  • 【C#】第3章补充(二)如何将图形作为对象

    分类:C#、VS2015

    创建日期:2016-06-23

    使用教材:(十二五国家级规划教材)《C#程序设计及应用教程》(第3版)

    一、要点

    该例子属于高级技术中的基本用法。对于初学者来说这是难点(难在还没有学习第13章WPF相关的绘图技术),因此,这里的关键是理解设计思路,而不是一开始就陷于细节的实现上。或者说,一旦你掌握了这些基本的设计思路,就会极大地提高你对面向对象编程的理解。

    用到的技术:封装、继承、多态。

    本补充示例的运行效果:

    image

    二、设计步骤

    1、新建项目

    项目名:WpfAdvanceDemo2

    模板:WPF应用程序项目。

    2、添加W0_DrawObject.cs文件

    鼠标右击解决方案资源管理器中的项目名,选择【添加】->【类】,输入文件名W0_DrawObject.cs,然后将代码改为下面的内容:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Input.StylusPlugIns;
    using System.Windows.Media;
    
    namespace WpfAdvanceDemo2
    {
        public abstract class W0_DrawObject : DynamicRenderer
        {
            protected Point previousPoint;
    
            public MyInkCanvas myInkCanvas { get; private set; }
    
            public DrawObjectStroke InkStroke { get; protected set; }
    
            public DrawingAttributes inkDA { get; set; }
    
            public abstract void CreateNewStroke(InkCanvasStrokeCollectedEventArgs e);
    
            public abstract Point Draw(Point first, DrawingContext dc, StylusPointCollection points);
    
            [ThreadStatic]
            protected Brush brush = Brushes.Gray;
    
            public W0_DrawObject(MyInkCanvas myInkCanvas)
            {
                this.myInkCanvas = myInkCanvas;
                this.inkDA = myInkCanvas.inkDA.Clone();
                this.DrawingAttributes = inkDA;
            }
    
            protected override void OnStylusDown(RawStylusInput rawStylusInput)
            {
                inkDA = myInkCanvas.inkDA.Clone();
                this.DrawingAttributes = inkDA;
                previousPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
                base.OnStylusDown(rawStylusInput);
            }
    
            protected override void OnStylusUp(RawStylusInput rawStylusInput)
            {
                base.OnStylusUp(rawStylusInput);
                this.InkStroke = null;
            }
        }
    
        public class DrawObjectStroke : Stroke
        {
            protected W0_DrawObject ink;
    
            public DrawObjectStroke(W0_DrawObject ink, StylusPointCollection stylusPoints)
                : base(stylusPoints)
            {
                this.ink = ink;
                this.DrawingAttributes = ink.inkDA.Clone();
                this.DrawingAttributes.Color = Colors.Transparent;
            }
    
            protected virtual void RemoveDirtyStylusPoints()
            {
                if (StylusPoints.Count > 2)
                {
                    for (int i = StylusPoints.Count - 2; i > 0; i--)
                    {
                        StylusPoints.RemoveAt(i);
                    }
                }
            }
        }
    }

    3、添加W1_DrawRectangle.cs文件

    鼠标右击解决方案资源管理器中的项目名,选择【添加】->【类】,输入文件名W1_DrawRectangle.cs,然后将代码改为下面的内容:

    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Input.StylusPlugIns;
    using System.Windows.Media;
    
    namespace WpfAdvanceDemo2
    {
        public class W1_DrawRectangle : W0_DrawObject
        {
            public W1_DrawRectangle(MyInkCanvas myInkCanvas) : base(myInkCanvas)
            {
            }
    
            public override void CreateNewStroke(InkCanvasStrokeCollectedEventArgs e)
            {
                InkStroke = new DrawRectangleStroke(this, e.Stroke.StylusPoints);
            }
    
            public override Point Draw(Point first, DrawingContext dc, StylusPointCollection points)
            {
                Point pt = (Point)points.Last();
                Vector v = Point.Subtract(pt, first);
                if (v.Length > 4)
                {
                    Rect rect = new Rect(first, v);
    
                    //填充
                    var b = new RadialGradientBrush(Colors.White, Colors.Red);
                    dc.DrawRectangle(b, null, rect);
    
                    //画轮廓
                    Pen pen = new Pen(Brushes.DarkRed, 1.0);
                    dc.DrawRectangle(null, pen, rect);
                }
                return first;
            }
            protected override void OnStylusDown(RawStylusInput rawStylusInput)
            {
                base.OnStylusDown(rawStylusInput);
                previousPoint = (Point)rawStylusInput.GetStylusPoints().First();
            }
    
            protected override void OnStylusMove(RawStylusInput rawStylusInput)
            {
                StylusPointCollection stylusPoints = rawStylusInput.GetStylusPoints();
                this.Reset(Stylus.CurrentStylusDevice, stylusPoints);
                base.OnStylusMove(rawStylusInput);
            }
    
            protected override void OnDraw(DrawingContext drawingContext, StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
            {
                Draw(previousPoint, drawingContext, stylusPoints);
                base.OnDraw(drawingContext, stylusPoints, geometry, brush);
            }
        }
    
        public class DrawRectangleStroke : DrawObjectStroke
        {
            public DrawRectangleStroke(W1_DrawRectangle ink, StylusPointCollection stylusPoints)
                : base(ink, stylusPoints)
            {
                this.RemoveDirtyStylusPoints();
            }
    
            protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes)
            {
                base.DrawCore(drawingContext, drawingAttributes);
                Point pt1 = (Point)StylusPoints.First();
                ink.Draw(pt1, drawingContext, StylusPoints);
            }
        }
    }

    4、添加W2_DrawEllipse.cs文件

    鼠标右击解决方案资源管理器中的项目名,选择【添加】->【类】,输入文件名W2_DrawEllipse.cs,然后将代码改为下面的内容:

    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Input.StylusPlugIns;
    using System.Windows.Media;
    
    namespace WpfAdvanceDemo2
    {
        public class W2_DrawEllipse : W0_DrawObject
        {
            public W2_DrawEllipse(MyInkCanvas myInkCanvas) : base(myInkCanvas)
            {
            }
    
            public override void CreateNewStroke(InkCanvasStrokeCollectedEventArgs e)
            {
                InkStroke = new DrawEllipseStroke(this, e.Stroke.StylusPoints);
            }
    
            public override Point Draw(Point first, DrawingContext dc, StylusPointCollection points)
            {
                Point pt = (Point)points.Last();
                Vector v = Point.Subtract(pt, first);
                double radiusX = (pt.X - first.X) / 2.0;
                double radiusY = (pt.Y - first.Y) / 2.0;
                Point center = new Point((pt.X + first.X) / 2.0, (pt.Y + first.Y) / 2.0);
    
                //填充
                var b = new RadialGradientBrush(Colors.White, Colors.Red);
                dc.DrawEllipse(b, null, center, radiusX, radiusY);
    
                //画轮廓
                Pen pen = new Pen(Brushes.DarkRed, 1.0);
                dc.DrawEllipse(null, pen, center, radiusX, radiusY);
    
                return first;
            }
    
            protected override void OnStylusDown(RawStylusInput rawStylusInput)
            {
                base.OnStylusDown(rawStylusInput);
                previousPoint = (Point)rawStylusInput.GetStylusPoints().First();
            }
    
            protected override void OnStylusMove(RawStylusInput rawStylusInput)
            {
                StylusPointCollection stylusPoints = rawStylusInput.GetStylusPoints();
                this.Reset(Stylus.CurrentStylusDevice, stylusPoints);
                base.OnStylusMove(rawStylusInput);
            }
    
            protected override void OnDraw(DrawingContext drawingContext, StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
            {
                Draw(previousPoint, drawingContext, stylusPoints);
                base.OnDraw(drawingContext, stylusPoints, geometry, brush);
            }
        }
    
        public class DrawEllipseStroke : DrawObjectStroke
        {
            public DrawEllipseStroke(W2_DrawEllipse ink, StylusPointCollection stylusPoints)
                : base(ink, stylusPoints)
            {
                this.RemoveDirtyStylusPoints();
            }
    
            protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes)
            {
                base.DrawCore(drawingContext, drawingAttributes);
                Point pt1 = (Point)StylusPoints.First();
                ink.Draw(pt1, drawingContext, StylusPoints);
            }
        }
    }

    5、添加W3_DrawCurve.cs文件

    鼠标右击解决方案资源管理器中的项目名,选择【添加】->【类】,输入文件名W3_DrawCurve.cs,然后将代码改为下面的内容:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfAdvanceDemo2
    {
        public class W3_DrawCurve : W0_DrawObject
        {
            public W3_DrawCurve(MyInkCanvas myInkCanvas)
                : base(myInkCanvas)
            {
            }
    
            public override void CreateNewStroke(InkCanvasStrokeCollectedEventArgs e)
            {
                InkStroke = new DrawCurveStroke(this, e.Stroke.StylusPoints);
            }
            public override Point Draw(Point first, DrawingContext dc, StylusPointCollection points)
            {
                return first;
            }
    
            protected override void OnDraw(DrawingContext drawingContext, StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
            {
                base.OnDraw(drawingContext, stylusPoints, geometry, Brushes.Black);
            }
        }
    
        public class DrawCurveStroke : DrawObjectStroke
        {
            public DrawCurveStroke(W0_DrawObject ink, StylusPointCollection stylusPoints)
                : base(ink, stylusPoints)
            {
                this.DrawingAttributes.FitToCurve = true;
            }
    
            protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes)
            {
                base.DrawCore(drawingContext, drawingAttributes);
                Geometry geometry = this.GetGeometry();
                drawingContext.DrawGeometry(Brushes.Black, null, geometry);
            }
        }
    }

    6、添加MyInkCanvas.cs文件

    鼠标右击解决方案资源管理器中的项目名,选择【添加】->【类】,输入文件名MyInkCanvas.cs,然后将代码改为下面的内容:

    using System.Windows.Controls;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfAdvanceDemo2
    {
        public class MyInkCanvas : InkCanvas
        {
            private W0_DrawObject ink;
    
            public DrawingAttributes inkDA { get; private set; }
    
            public MyInkCanvas()
            {
                inkDA = new DrawingAttributes()
                {
                    Color = Colors.Red,
                    Width = 15,
                    Height = 15,
                    StylusTip = StylusTip.Rectangle,
                    IgnorePressure = true,
                    FitToCurve = false
                };
                this.DefaultDrawingAttributes = inkDA;
                ink = new W1_DrawRectangle(this);
                UpdateInkParams();
            }
    
            /// <summary>当收集墨迹时,会自动调用此方法</summary>
            protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
            {
                this.Strokes.Remove(e.Stroke);
                ink.CreateNewStroke(e);
                this.Strokes.Add(ink.InkStroke);
                InkCanvasStrokeCollectedEventArgs args = new InkCanvasStrokeCollectedEventArgs(ink.InkStroke);
                base.OnStrokeCollected(args);
            }
    
            /// <summary>初始化墨迹参数</summary>
            public void SetInkAttributes(string name)
            {
                switch (name)
                {
                    //---------------墨迹类型---------------------
                    case "矩形":
                        ink = new W1_DrawRectangle(this);
                        inkDA.Width = inkDA.Height = 15;
                        inkDA.StylusTip = StylusTip.Rectangle;
                        this.UseCustomCursor = false;
                        break;
                    case "球形":
                        ink = new W2_DrawEllipse(this);
                        inkDA.Width = inkDA.Height = 15;
                        inkDA.StylusTip = StylusTip.Ellipse;
                        this.UseCustomCursor = false;
                        break;
                    case "毛笔":
                        ink = new W3_DrawCurve(this);
                        inkDA.Width = inkDA.Height = 10;
                        this.Cursor = Cursors.Pen;
                        this.UseCustomCursor = true;
                        break;
                }
                UpdateInkParams();
            }
    
            /// <summary>
            /// 根据墨迹类型和笔尖信息,设置MyInkCanvas中的相关参数
            /// </summary>
            private void UpdateInkParams()
            {
                this.DynamicRenderer = ink;
                this.EditingMode = InkCanvasEditingMode.Ink;
            }
        }
    }

    7、修改MainWindow.xaml文件

    将其改为下面的内容。

    <Window x:Class="WpfAdvanceDemo2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfAdvanceDemo2"
            mc:Ignorable="d"
            Title="将图形作为对象--简单示例(http://cnblogs.com/rainmj)" Height="400" Width="700" WindowStartupLocation="CenterScreen" Background="#FFE4EEDE">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="0">
                <TextBlock Text="提示:选择一种绘制类型,然后在绘图框区域内按住鼠标左键拖动绘制。" Margin="0 20" FontSize="16" Foreground="Blue" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                <Separator/>
                <WrapPanel ButtonBase.Click="RadioButton_Click" Margin="0 10 0 0">
                    <TextBlock Text="绘制类型:" VerticalAlignment="Center"/>
                    <RadioButton Content="矩形" IsChecked="True" Margin="5"/>
                    <RadioButton Content="球形" Margin="5"/>
                    <RadioButton Content="毛笔" Margin="5"/>
                </WrapPanel>
            </StackPanel>
            <Frame Name="frame1" Grid.Row="1" Margin="10" BorderThickness="1" BorderBrush="Blue"
                       NavigationUIVisibility="Hidden" />
            <TextBlock Grid.Row="2" Text="(完整例子在【网络应用编程】课程中还会介绍,该例子仅演示了最基本的用法)" Margin="0 0 0 5" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        </Grid>
    </Window>

    8、修改MainWindow.xaml.cs文件

    将其改为下面的内容。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    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;
    
    namespace WpfAdvanceDemo2
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            MyInkCanvas mycanvas;
            public MainWindow()
            {
                InitializeComponent();
                mycanvas = new MyInkCanvas();
                frame1.Content = mycanvas;
            }
    
            private void RadioButton_Click(object sender, RoutedEventArgs e)
            {
                string s = (e.Source as RadioButton).Content.ToString();
                mycanvas.SetInkAttributes(s);
            }
        }
    }

    9、运行

    按<F5>键调试运行。

    OK,这个例子虽然简单,但是却演示了封装、继承、多态在实际项目中的基本应用设计思路。请耐着性子仔细分析该例子的源代码,相信你掌握设计思路和技巧后一定会对C#面向对象编程的理解有一个大的飞跃。

    在此基础上,你就可以继续学习复杂的例子了。实际上,任何内容都可以通过拖放绘制出来,包括视频。

    三、高级用法

    下面的截图演示了高级用法示例的运行效果(选择某种绘图类型以及其他选项后,按住鼠标左键随意拖放即可):

    image

    该例子更接近于实际项目,虽然例子看起来好像很复杂,但是基本的设计思路还是这个简单例子的思路,只不过是在简单例子基础上多添加了一些类而已。

    这里顺便解释一下,类似Office的工具箱界面是如何实现的(用到了Ribbon控件):

    (1)鼠标右击【引用】->【添加引用】,然后按下图所示添加Ribbon引用。

    image

    (2)在项目中添加一个Windows窗体,然后就可以在该窗体中使用Ribbon控件设计工具箱的内容了。下面是高级例子对应的XAML代码(为了方便快速理解,这里去掉了重复的内容,仅列出了其中的一部分代码):

    <Window x:Class="WpfExamples.ch03.Ex02.WpfAdvanceDemo3.Demo3MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfExamples.ch03.Ex02.WpfAdvanceDemo3"
            mc:Ignorable="d"
            Title="将图形图像作为对象--高级功能" Height="460" Width="980" Background="#FFF0F9D8" WindowState="Maximized">
        <Grid x:Name="root">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Ribbon Name="ribbon" Grid.Row="0">
                <Ribbon.Resources>
                    <Style TargetType="RibbonRadioButton">
                        <Setter Property="LargeImageSource" Value="/Resources/Images/b1.png"/>
                        <Setter Property="SmallImageSource" Value="/Resources/Images/b1.gif"/>
                        <Setter Property="CornerRadius" Value="13"/>
                        <Setter Property="Margin" Value="5 0 0 0"/>
                        <EventSetter Event="Checked" Handler="RibbonRadioButton_Checked"/>
                    </Style>
                </Ribbon.Resources>
                <Ribbon.ApplicationMenu>
                    <RibbonApplicationMenu Name="appMenu1" ToolTip="主菜单">
                        <RibbonApplicationMenu.Resources>
                            <Style TargetType="RibbonApplicationMenuItem">
                                <Setter Property="ImageSource" Value="/Resources/Images/b1.gif"/>
                                <EventSetter Event="Click" Handler="RibbonApplicationMenuItem_Click"/>
                            </Style>
                        </RibbonApplicationMenu.Resources>
                        <RibbonApplicationMenuItem Header="打开"/>
                        <RibbonApplicationMenuItem Header="另存为"/>
                        <RibbonSeparator/>
                        <RibbonApplicationMenuItem Header="退出"/>
                    </RibbonApplicationMenu>
                </Ribbon.ApplicationMenu>
    
                <RibbonTab Name="rt1" Header="工具箱">
                    <RibbonGroup Header="墨迹类型">
                        <RibbonGroup.GroupSizeDefinitions>
                            <RibbonGroupSizeDefinition>
                                <RibbonControlSizeDefinition ImageSize="Small"/>
                                <RibbonControlSizeDefinition ImageSize="Small"/>
                                ......(略,内容都一样,个数与下面的RibbonRadioButton个数对应即可)
                            </RibbonGroupSizeDefinition>
                        </RibbonGroup.GroupSizeDefinitions>
                        <RibbonRadioButton x:Name="rrbEllipseType" Label="球形" IsChecked="True"/>
                        <RibbonRadioButton Label="矩形"/>
                        <RibbonRadioButton Label="图像"/>
                        <RibbonRadioButton Label="球形序列"/>
                        <RibbonRadioButton Label="矩形序列"/>
                        <RibbonRadioButton Label="图像序列"/>
                        <RibbonRadioButton Label="直线"/>
                        <RibbonRadioButton Label="曲线"/>
                        <RibbonRadioButton Label="文字"/>
                    </RibbonGroup>
                    <RibbonGroup Header="笔尖类型">
                        <RibbonRadioButton x:Name="rrbEllipseStylus" Label="圆笔" IsChecked="True" GroupName="edit" />
                        <RibbonRadioButton Label="竖笔" GroupName="edit"/>
                        <RibbonRadioButton Label="横笔" GroupName="edit"/>
                        <RibbonRadioButton Label="钢笔" GroupName="edit"/>
                    </RibbonGroup>
                    .....(后面的代码和前面类似,不再列出了)
                </RibbonTab>
            </Ribbon>
            <Grid x:Name="grid1" Margin="10" Grid.Row="1" Visibility="Visible">
                <Rectangle Grid.ColumnSpan="2" Fill="white"
                           RadiusX="14" RadiusY="14"
                           Stroke="Blue" StrokeDashArray="3" />
                <local:MyInkCanvas x:Name="ink1"/>
            </Grid>
        </Grid>
    </Window>

    注意:练习时要一行一行的敲,不要用复制粘贴的办法,否则系统不会自动在后台代码(代码隐藏类)中添加对应的事件处理程序。

    在后续的章节中,我们还会学习该高级例子涉及的更多概念(比如利用序列化和反序列化将绘图结果保存到文件中,并将序列化后的结果读取出来还原为截图中的各个绘图对象等)。这里暂不列出高级例子的设计步骤,准备等后续章节把相关的概念介绍完毕后,再学习高级例子的源代码也不晚。

    说明:这些例子全部都是本人原创的,转载请注明出处:http://cnblogs.com/rainmj

  • 相关阅读:
    简单了解enum
    PowerDesigner CDM 生成PDM时,外键的命名规则
    HADOOP docker(五):hadoop用户代理 Proxy user
    记一次云安全的安全事件应急响应
    docker学习笔记--重基础使用
    Elasticsearch学习随笔(二)-- Index 和 Doc 查询新建API总结
    Elasticsearch学习随笔(一)--原理理解与5.0核心插件部署过程
    ntopng-一款流量审计框架的安装以及应用
    Suricata规则编写——常用关键字
    浅谈运维中的安全问题-FTP篇
  • 原文地址:https://www.cnblogs.com/rainmj/p/5609202.html
Copyright © 2011-2022 走看看