C#用户控件之流动管道

如何绘制一个动态的流动管道(FlowPipe)?

分两步绘制

  1. 定义属性;
  2. 画布重绘;

主要技能:

  • 管道的绘制(渐变色矩形)
  /// <summary>
/// 画渐变色矩形的方法
/// </summary>
/// <param name="g">画布</param>
/// <param name="brush">画刷</param>
/// <param name="pen">笔</param>
/// <param name="rectangle">矩形</param>
private void PaintRectangle(Graphics g, Brush brush, Pen pen, Rectangle rectangle)
{
//填充矩形
g.FillRectangle(brush, rectangle); switch (this.pipeStyle)
{
case PipeStyle.Horizontal:
g.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X + rectangle.Width, rectangle.Y);
g.DrawLine(pen, rectangle.X, rectangle.Y + rectangle.Height - 1, rectangle.X + rectangle.Width, rectangle.Height);
break;
case PipeStyle.Vertical:
g.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Y + rectangle.Height);
g.DrawLine(pen, rectangle.X + rectangle.Width - 1, rectangle.Y, rectangle.X + rectangle.Width - 1, rectangle.Height);
break;
default:
break;
}
}
  • 管道的绘制(渐变色半圆)
/// <summary>
/// 画渐变色半圆的方法
/// </summary>
/// <param name="g">画布</param>
/// <param name="colorBlend"></param>
/// <param name="p"></param>
/// <param name="rect"></param>
/// <param name="startAngle"></param>
/// <param name="sweepAngle"></param>
private void PaintEllipse(Graphics g, ColorBlend colorBlend, Pen p, Rectangle rect, float startAngle, float sweepAngle)
{
//第一步:创建GPI路径
GraphicsPath path = new GraphicsPath();
path.AddEllipse(rect); //第二步:渐变色填充
PathGradientBrush brush = new PathGradientBrush(path);
brush.CenterPoint = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
brush.InterpolationColors = colorBlend; //第三步:绘制管道
g.FillPie(brush, rect, startAngle, sweepAngle); //第四步:绘制边线
g.DrawArc(p, rect, startAngle, sweepAngle);
}
  • 流动条的绘制(用笔的虚线)
//画虚线,关键用笔和路径来画
Pen pen = new Pen(this.flowColor, this.flowWidth);
pen.DashStyle = DashStyle.Custom;
pen.DashPattern = new float[]
{
flowLength,flowLengthGap
};
pen.DashOffset = this.startOffset;
g.DrawPath(pen, path); //流动条路径
GraphicsPath path = new GraphicsPath(); //虚线路径—左边、中间、右边
switch (this.pipeTurnLeft)
{
case PipeTurn.Up:
path.AddArc(new Rectangle(this.Height / 2, this.Height / 2 * (-1) -1, this.Height, this.Height), 181.0f, -91.0f);
break;
case PipeTurn.Down:
path.AddArc(new Rectangle(this.Height / 2, this.Height / 2, this.Height, this.Height), 180.0f, 90.0f);
break;
default:
path.AddLine(-1, this.Height / 2, this.Height+1, this.Height / 2);
break;
}
  • 关键理解:绘制的椭圆、线(Rectangle)<x,y【圆切矩形相对于控件原点<左上角>的坐标】,宽,高,开始角度,扫描角度>理解了就好画了

    path.AddArc(new Rectangle(this.Height / 2, this.Height / 2, this.Height, this.Height), 180.0f, 90.0f);

    path.AddLine(-1, this.Height / 2, this.Height+1, this.Height / 2);

  • 可以流动的关键要素

     //流动条流动速度(刷新速度)
this.myTimer = new Timer();
myTimer.Interval = 50;
this.myTimer.Tick += MyTimer_Tick; ; } #region 定时循环
private void MyTimer_Tick(object sender, EventArgs e)
{
this.startOffset = this.startOffset - this.moveSpeed; if (this.startOffset > this.flowLength + this.flowLengthGap || this.startOffset < (this.flowLength + this.flowLengthGap) * (-1))
{ this.startOffset = 0; }
this.Invalidate();
} #endregion

1.定义属性

  • 管道的(两端转向、样式、边沿颜色、中间颜色、激活)
  • 流动条的(速度、长度、宽度、间隙、颜色)
//属性示例:按照示例添加以上各种属性
private float moveSpeed = 0.3f;
[Browsable(true)]
[Category("布局_G")]
[Description("流动条速度,负数为反向")] //属性说明
public float MoveSpeed
{
get { return moveSpeed; }
set
{
this.moveSpeed = value;
this.Invalidate(); //重绘
}
}

2.画布重绘

【管道分为左、中、右三部分。先画管道(矩形):左、中、右;再画流动条(虚线):左、中、右】
//矩形画刷
LinearGradientBrush linearGradientBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, this.Height), pipeColorEdge, pipeColorEdge);
linearGradientBrush.InterpolationColors = colorBlend; //绘制左部分
switch (this.pipeTurnLeft)
{
case PipeTurn.Up:
this.PaintEllipse(g, colorBlend, p, new Rectangle(0, this.Height * (-1)-1, this.Height * 2, this.Height * 2), 90.0f, 90.0f);
break;
case PipeTurn.Down:
this.PaintEllipse(g, colorBlend, p, new Rectangle(0, 0, this.Height * 2, this.Height * 2), 180.0f, 90.0f);
break;
default:
this.PaintRectangle(g, linearGradientBrush, p, new Rectangle(-1, 0, this.Height+1, this.Height));
break;
} //绘制右部分
switch (this.pipeTurnRight)
{
case PipeTurn.Up:
this.PaintEllipse(g, colorBlend, p, new Rectangle(this.Width - this.Height * 2, this.Height * (-1)-1, this.Height * 2, this.Height * 2), 0.0f, 90.0f);
break;
case PipeTurn.Down:
this.PaintEllipse(g, colorBlend, p, new Rectangle(this.Width - this.Height * 2, 0, this.Height * 2, this.Height * 2), 270.0f, 90.0f);
break;
default:
this.PaintRectangle(g, linearGradientBrush, p, new Rectangle(this.Width - this.Height, 0, this.Height, this.Height));
break;
} //绘制中间
if (this.Width > this.Height * 2)
{
this.PaintRectangle(g, linearGradientBrush, p, new Rectangle(this.Height - 1, 0, this.Width - this.Height * 2 + 2, this.Height));
}
//流动条路径
GraphicsPath path = new GraphicsPath(); //虚线路径—左边
switch (this.pipeTurnLeft)
{
case PipeTurn.Up:
path.AddArc(new Rectangle(this.Height / 2, this.Height / 2 * (-1) -1, this.Height, this.Height), 181.0f, -91.0f);
break;
case PipeTurn.Down:
path.AddArc(new Rectangle(this.Height / 2, this.Height / 2, this.Height, this.Height), 180.0f, 90.0f);
break;
default:
path.AddLine(-1, this.Height / 2, this.Height+1, this.Height / 2);
break;
} //虚线路径—中间
if (this.Width > this.Height * 2)
{
path.AddLine(this.Height, this.Height / 2, this.Width - this.Height -1, this.Height / 2);
} //虚线路径—右边
switch (this.pipeTurnRight)
{
case PipeTurn.Up:
path.AddArc(new Rectangle(this.Width - 1 - this.Height * 3 / 2, -this.Height / 2-1 , this.Height, this.Height), 88f, -91.0f);
break;
case PipeTurn.Down:
path.AddArc(new Rectangle(this.Width - 1 - this.Height * 3 / 2, this.Height / 2, this.Height, this.Height), 270.0f, 90.0f);
break;
default:
path.AddLine(this.Width - this.Height, this.Height / 2, this.Width , this.Height / 2);
break;
} //画虚线,关键用笔和路径来
Pen pen = new Pen(this.flowColor, this.flowWidth);
pen.DashStyle = DashStyle.Custom;
pen.DashPattern = new float[]
{
flowLength,flowLengthGap
};
pen.DashOffset = this.startOffset;
g.DrawPath(pen, path);

格式都是一样的,掌握关键代码,肝就对了。

End

C#自定义控件—流动管道的更多相关文章

  1. NodeJS Stream 五:双工流

    双工流就是同时实现了 Readable 和 Writable 的流,即可以作为上游生产数据,又可以作为下游消费数据,这样可以处于数据流动管道的中间部分,即 rs.pipe(rws1).pipe(rws ...

  2. nodeJS之流stream

    前面的话 当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到数据流.NodeJS中通过各种Stream来提供对数据流的操作.本文将详细说明NodeJS中的流strea ...

  3. 基于react-app搭建react-router+redux项目

    前言 总括: 本文采用react+redux+react-router+less+es6+webpack,以实现一个简易备忘录(todolist)为例尽可能全面的讲述使用react全家桶实现一个完整应 ...

  4. 1.3 React 组件

    1.3.1 React 组件介绍 在 React 中组件是第一元素,是 React 的基础,一个 React 应用就是基于 React 组件的组合而成.前面的 JSX 练习过后,大家应该对 React ...

  5. Web 组态运用之用户数据 ARPU 分析图

    前言 作为企业的发展,通过运营的有效管理,增加收入.降低成本,取得更好的经济效益,是核心所在,在电信企业同样如此.电信企业的利润大体上是由业务收入和成本决定的,而收入和成本又可进一步分别分解表达为不同 ...

  6. 极简 Node.js 入门 - 4.5 双工流

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  7. (五十五)c#Winform自定义控件-管道

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  8. OpenFOAM——不对称突变管道中的低雷诺数流动

    本算例来自<ANSYS Fluid Dynamics Verification Manual>中的VMFL064: Low Reynolds Number Flow in a Channe ...

  9. (五十八)c#Winform自定义控件-管道阀门(工业)

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  10. 基于HTML5实现3D监控应用流动效果

    http://www.hightopo.com/guide/guide/core/lighting/examples/example_flowing.html 流动效果在3D领域有着广泛的应用场景,如 ...

随机推荐

  1. 韦东山freeRTOS系列教程之【第三章】任务管理

    目录 系列教程总目录 概述 3.1 基本概念 3.2 任务创建与删除 3.2.1 什么是任务 3.2.2 创建任务 3.2.3 示例1: 创建任务 3.2.4 示例2: 使用任务参数 3.2.5 任务 ...

  2. MFC基于对画框工程笔记->更改窗口图标以及生成的.exe图标

    一.前言 继前一篇生成MFC基于对话框工程->新建MFC对话框后,开始改动对话框图标以及生成的.exe图标. 原对话框图标以及.exe图标: 在菜单栏中选择生成目录为Release 打开Rele ...

  3. Django详细笔记

    django 学习 特点 快速开发 安全性高 可伸缩性强 URL 组成部分 URL: 同意资源定位符 一个URL由以下几部分组成 scheme://host:port/path/?query-stri ...

  4. 阿里云服务器安装Docker Compose

    官网地址:https://docs.docker.com/compose/install/ 1. sudo curl -L "https://github.com/docker/compos ...

  5. 详解Web应用安全系列(10)文件上传漏洞

    文件上传漏洞(File Upload Vulnerabilities)是Web攻击中常见的一种安全漏洞,它允许攻击者上传并执行恶意文件,从而可能对Web服务器造成严重的安全威胁. 一.定义与原理 文件 ...

  6. LabVIEW Actor Framwork (1)________ 边学边做server&client

    初始需求: 现在要做一个类似聊天的demo,一个server端,若干个client端:首先是server启动,通过server可以打开若干个client端,然后每个client可以独立给server发 ...

  7. oeasy教您玩转vim - 16 - # 行内贴靠

    行头行尾 回忆上节课内容 跳跃 向前跳跃是 f 向后跳跃是 F 继续 保持方向是 ; 改变方向是 , 可以加上 [count] 来加速 还有什么好玩的吗? 动手 #这次还是用无配置的方式启动 vi - ...

  8. [oeasy]python0081_ANSI序列由来_终端机_VT100_DEC_VT选项_终端控制序列

    更多颜色 回忆上次内容 上次 首先了解了RGB颜色设置 可以把一些抽象的色彩名字 落实到具体的 RGB颜色 计算机所做的一切 其实就是量化.编码 把生活的一切都进行数字化 标准 是ANSI制定的 这个 ...

  9. 解决“网页源代码编码形式为utf-8,但爬虫代码设置为decode('utf-8')仍出现汉字乱码”的问题

    为了用爬虫获取百度首页的源代码,检查了百度的源代码,显示编码格式为utf-8 但这样写代码,却失败了-.. (这里提示:不要直接复制百度的URL,应该是http,不是https!!!) # 获取百度首 ...

  10. 题解:P10722 [GESP202406 六级] 二叉树

    题意 一颗 \(n\) 节点的二叉树,每个节点非黑即白,给你 \(Q\) 次操作,每次给你一个 \(u\),把 \(u\) 的子树内所有节点颜色反转,问最终每个节点的颜色. 分析 看到数据范围,首先把 ...