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. podman安装mysql容器

    前言 mysql如果正式安装,卸载起来比较麻烦.如果是自己测试用的话,可以用podman拉取一个镜像来使用. 这里使用的是mysql5.7版本,对应的docker镜像是mysql:5.7 (如果拉取较 ...

  2. 从Java开发者到.NET Core初级工程师学习路线:C#语言基础

    1. C#语言基础 1.1 C#语法概览 欢迎来到C#的世界!对于刚从Java转过来的开发者来说,你会发现C#和Java有很多相似之处,但C#也有其独特的魅力和强大之处.让我们一起来探索C#的基本语法 ...

  3. IDEA+Maven+Spring5.X项目创建

    创建maven 添加依赖 pom.xml <dependencies> <dependency> <groupId>org.springframework</ ...

  4. 网易数帆开源贡献获业界肯定,轻舟API网关获OSCAR尖峰开源技术创新奖

    2020年10月16日,由中国信息通信研究院主办的"2020开源产业大会"在北京线下与线上同步召开,主办方在会上公布了"OSCAR尖峰开源奖项"各个奖项的评选结 ...

  5. mysql大数据表添加字段

    方案一.老表数据迁移四部曲方案1.新建老表t_order_goods的备份表t_order_goods_bak,同时加一个字段:isVirtual 并给默认值2.迁移老表t_order_goods数据 ...

  6. socket通讯原理及例程(一看就懂

    来源:https://blog.csdn.net/jiushimanya/article/details/82684525 里面有疑问或者不正确的地方可以给我留言 对TCP/IP.UDP.Socket ...

  7. [oeasy]python0098_个人计算机浪潮_IBM5100_微软成立_苹果II_VisCalc

    个人计算机浪潮 回忆上次内容 个人电脑(PC) 在爱好者之间疯传 人人都有一台计算机 从attair-8800到apple-1 个人电脑 离普通人 更近了 如果 人人都有 自己的电脑 谁还去 用终端连 ...

  8. 基础-数组_C语言

    C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合.数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量. 数组的声明并不是声明一个个单独的变量,比如 runoob0. ...

  9. 搭建lnmp环境-nginx(第一步)

    建议: 本次lnmp采用yum形式安装,编译安装过于繁琐,操作不好还不如yum安装,所以不推荐. 全部安装在宿主机上,如果需要安装多个版本的软件才使用docker nginx无所谓版本了 刚安装好系统 ...

  10. c语言模拟Python的命名参数

    最近在书里看到的,让c语言去模拟其他语言里有的命名函数参数.觉得比较有意思所以记录一下. 目标 众所周知c语言里是没有命名函数参数这种东西的,形式参数虽然有自己的名字,但传递的时候并不能通过这个名字来 ...