目录

介绍

背景

使用代码

1. MatrixRain

1、设定参数

2、开始动画

3、绘制新框架

MatrixRainWpfApp


  • 下载应用程序-40.1 KB

介绍

本文适用于那些想了解在WPF中DrawingVisual 是如何工作的人。

如MSDN中所述,DrawingVisual 是一种轻量级的绘图类,用于呈现形状、图像或文本。此类被认为是轻量级的,因为它不提供布局、输入、焦点或事件处理,从而提高了其性能。

背景

在开始编码之前,我查阅了MSDN页面,以了解DrawingVisual Objects和WPF Graphics Rendering Overview的基础。

我们在WPF中通常使用许多元素/控制,如Button,ComboBox,Shape,和其他等,具有以下特征:

  • 可以由多个元素组成,每个组成元素提供focus方法,事件处理和许多特征,这些特征使我们可以自由编程,但如果需要执行一些绘图,则有很多“开销”。
  • 扩展不是针对特定目的而是针对通用服务进行了优化的通用对象。

DrawingVisual的范围是提出一种轻量级的对象绘制方法。

关于矩阵雨效应,我对如何从CodePen进行开发提出了一些想法,CodePen是一个在线社区,用于测试和展示用户创建的HTML,CSS和JavaScript代码段。

我假设读者知道WPF调度程序。简要地说,当您执行WPF应用程序时,它会自动创建一个新Dispatcher对象并调用其Run方法。所有视觉元素都将由调度程序线程创建,并且所有对视觉元素所做的修改都必须在Dispatcher线程上执行。

使用代码

我的示例由两个项目组成:

1. MatrixRain

这是解决方案的核心。该项目实现了一个模拟Matrix数字雨效果的UserControl。UserControl可以在任何窗口/页等中使用

1、设定参数

SetParameter方法允许设置一些动画参数:

...
public void SetParameter(int framePerSecond = 0, FontFamily fontFamily = null,int fontSize = 0, Brush backgroundBrush = null, Brush textBrush = null, String characterToDisplay = "")
...
  • framePerSecond:每秒刷新一次(此参数影响下雨的“速度”)
  • fontFamily:使用的字体系列
  • fontSize:使用的字体尺寸
  • backgroundBrush:用于背景的画笔
  • textBrush:用于文本的笔刷
  • characterToDisplay:从string中随机选择用于下雨的字符

2、开始动画

Start和Stop方法允许启动和停止动画:

public void Start() {_DispatcherTimer.Start();
}
public void Stop() {_DispatcherTimer.Stop();
}
...

动画是通过System.Timers.Timer控制的。与System.Windows.Threading.DispatcherTimer相比,我更喜欢此解决方案,因为在每个Dispatcher循环的顶部都会重新评估DispatcherTimer,并且不能保证计时器在发生时间间隔时准确执行。

每次滴答时,都会调用_DispatcherTimerTick(object sender, EventArgs e)方法。此方法不在Dispatcher线程上执行,因此第一件事是同步Dispatcher线程上的调用,因为我们需要使用只能由主线程访问的某些资源。

...private void _DispatcherTimerTick(object sender, EventArgs e)
{if (!Dispatcher.CheckAccess()) {//synchronize on main threadSystem.Timers.ElapsedEventHandler dt = _DispatcherTimerTick;Dispatcher.Invoke(dt,sender,e);return;}....
}

3、绘制新框架

一旦来自计时器的调用位于调度程序线程上,它将执行两个操作:

  • 1、设计新框架

框架是通过_RenderDrops()方法创建的。这是一个新对象DrawingVisual,它的DrawingContext被创建来绘制对象。绘图上下文允许绘制线条、椭圆、几何形状、图像等等。

DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();

首先,该方法会创建不透明度为10%的黑色背景(我将在后面解释为什么我将不透明度设为10%)。

之后,我们滚动浏览一个名为_Drops的数组。

该数组代表绘制字母所沿的列(请参见图像中的红色列)。数组的值表示必须绘制新字母的行(请参见图像中的蓝色圆圈)。当墨滴的值达到图像的“底部”时,墨滴会立即或在一系列循环后随机从顶部重新开始。

...
//looping over drops
for (var i = 0; i < _Drops.Length; i++) {// new drop positiondouble x = _BaselineOrigin.X + _LetterAdvanceWidth * i;double y = _BaselineOrigin.Y + _LetterAdvanceHeight * _Drops[i];// check if new letter does not goes outside the imageif (y + _LetterAdvanceHeight < _CanvasRect.Height) {// add new letter to the drawingvar glyphIndex = _GlyphTypeface.CharacterToGlyphMap[_AvaiableLetterChars[_CryptoRandom.Next(0, _AvaiableLetterChars.Length - 1)]];glyphIndices.Add(glyphIndex);advancedWidths.Add(0);glyphOffsets.Add(new Point(x, -y));}//sending the drop back to the top randomly after it has crossed the image//adding a randomness to the reset to make the drops scattered on the Y axisif (_Drops[i] * _LetterAdvanceHeight > _CanvasRect.Height && _CryptoRandom.NextDouble() > 0.775) {_Drops[i] = 0;}//incrementing Y coordinate_Drops[i]++;
}
// add glyph on drawing context
if (glyphIndices.Count > 0) {GlyphRun glyphRun = new GlyphRun(_GlyphTypeface,0,false,_RenderingEmSize,glyphIndices,_BaselineOrigin,advancedWidths,glyphOffsets,null,null,null,null,null);drawingContext.DrawGlyphRun(_TextBrush, glyphRun);
}
...

总结一下方法,_RenderDrops()生成包含不透明背景和新的下落字母的DrawingVisual。例如:


帧1


帧2


帧3


帧4

  • 2、将新框架复制到上一帧(frame)

如前所述,新帧仅生成“新”字母,但是我们如何淡出以前的字母呢?

这是由具有10%不透明度的黑色框架背景执行的。当我们在前一帧上复制新帧时,融合就可以了。如以下示例所示,“copy over”削弱了先前字母的亮度:


最终帧1 =黑色背景+帧1


最终帧2 =最终帧1 +帧2


最终帧3 =最终帧2 +帧3


最终帧4 =最终帧3 +帧4

PS:我在RenderTargetBitmap上以可视方式绘图。我可以将其直接应用到我的图像上:

_MyImage.Source = _RenderTargetBitmap

该解决方案的问题在于,在每个周期,此操作在每个周期分配大量内存。为了解决这个问题,我使用WriteableBitmap,它在初始化代码中仅在内存中分配一次。

...
_WriteableBitmap.Lock();
_RenderTargetBitmap.CopyPixels(new Int32Rect(0, 0, _RenderTargetBitmap.PixelWidth,_RenderTargetBitmap.PixelHeight), _WriteableBitmap.BackBuffer, _WriteableBitmap.BackBufferStride * _WriteableBitmap.PixelHeight,_WriteableBitmap.BackBufferStride);
_WriteableBitmap.AddDirtyRect(new Int32Rect(0, 0, _RenderTargetBitmap.PixelWidth, _RenderTargetBitmap.PixelHeight));
_WriteableBitmap.Unlock();
...

MatrixRainWpfApp

该项目参考MatrixRain并展示了MatrixRain用户控件的潜力。该代码未注释,因为它非常简单,不需要注释。

1、在MainWindow.xaml,将一个MatrixRain控件添加到窗口:

...
xmlns:MatrixRain="clr-namespace:MatrixRain;assembly=MatrixRain"
...
<MatrixRain:MatrixRain x:Name="mRain" HorizontalAlignment="Left" Height="524" Margin="10,35,0,0" VerticalAlignment="Top" Width="1172"/>
...

2、在初始化期间,我从嵌入式资源中读取了一种特殊字体,并将其传递给MatrixRain控件:

FontFamily rfam = new FontFamily(new Uri("pack://application:,,,"), "./font/#Matrix Code NFI");
mRain.SetParameter(fontFamily: rfam);

请注意字体。这是我找到它的链接:https://www.1001fonts.com/matrix-code-nfi-font.html。这是免费的,仅供个人使用。

3、两个按钮:Start和Stop; 命令动画:

private void _StartButtonClick(object sender, RoutedEventArgs e)
{mRain.Start();
}private void _StopButtonClick(object sender, RoutedEventArgs e)
{mRain.Stop();
}

4、两个按钮:Set1和Set2; 命令文本颜色:

private void _ChangeColorButtonClick(object sender, RoutedEventArgs e)
{mRain.SetParameter(textBrush: ((Button)sender).Background);
}

PS:为了随机生成字母,我使用了个人的“CryptoRandom”类(包括源代码)而不是规范的Random方法。这是因为Random方法会生成“伪随机”数。如果您想深入了解,请点击此链接。

使用WPF的C#中的矩阵样式雨相关推荐

  1. wpf修改theme中的样式_WPF Mahapps.Metro 设置主题样式

    /// /// 设置App样式 /// /// 窗口标题栏样式 /// 背景样式 private void ChangeTheme(string accentName, string themeNam ...

  2. MathType可以在Word、PPT中插入矩阵吗

    工科学生或者老师在写论文时最头痛的就是编辑公式,因为word自带的公式编辑器往往满足不了专业的公式需求,MathType就很好的解决了这个问题.在进行公式编辑时,难免会遇到输入矩阵的情况,那么怎么输入 ...

  3. 缓解 WPF 应用程序中的空域问题

    介绍 WPF 为构建 Windows 应用程序提供了一种现代方法,但它直接构建在 Win32(Windows 中的传统 UI 基础结构)之上.因为 Win32 是在 CPU/GPU 马力比现在更加有限 ...

  4. checkbox wpf 背景图片_WPF的CheckBox样式总结

    背景 很多时候我们使用WPF开发界面的时候经常会用到各种空间,很多时候我们需要去自定义控件的样式来替换默认的样式,今天通过两个方法来替换WPF中的CheckBox样式,透过这两个例子我们可以掌握基本的 ...

  5. 年度巨献-WPF项目开发过程中WPF小知识点汇总(原创+摘抄)

    WPF中Style的使用 Styel在英文中解释为"样式",在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,通过设置样式,使其WPF控件外 ...

  6. #711 – 在拖拽的过程中改变鼠标样式(Changing the Mouse Cursor While Dragging)

    原文地址:https://wpf.2000things.com/2012/12/13/711-changing-the-mouse-cursor-while-dragging/ 在WPF拖拽的过程中, ...

  7. 关于WPF的ComboBox中Items太多而导致加载过慢的问题

                                         [WFP疑难]关于WPF的ComboBox中Items太多而导致加载过慢的问题                         ...

  8. 如何使用pyecharts中的主题样式?

    如何使用pyecharts中的主题样式? pyechart为用户提供了一套使用方便的主题风格. 本篇图文将总结pyecharts.globals中ThemeType所有主题风格并进行详细的解释. cl ...

  9. 一文读懂深度学习中的矩阵微积分

    点击视学算法标星,更快获取CVML新技术 鱼羊 编译整理 量子位 报道 | 公众号 QbitAI 想要真正了解深度神经网络是如何训练的,免不了从矩阵微积分说起. 虽然网络上已经有不少关于多元微积分和线 ...

最新文章

  1. 色彩(颜色)空间原理(实现代码)
  2. 海洋主题绘画_神奇宝贝:海洋生物的艺术世界绘画比赛获奖作品展来啦!
  3. halcon的仿射变换算子的介绍
  4. 云计算的下半场:云原生
  5. linux内存爆了会怎样,linux系统中内存爆满之后会如何?
  6. 了解活动目录操作主机角色及GUI命令行查看方法
  7. Sprint Application bootstrap的研究和调试
  8. P4201-[NOI2008]设计路线【结论,树形dp】
  9. SQL 个版本下载地址
  10. 从主流安全开发框架看软件供应链安全保障的落地
  11. php+ajax+打开新页面跳转,ajax怎样跳转到新的jsp页面(附代码)
  12. 4G手机网络通信是如何被黑客远程劫持的?
  13. winform:關於画非客户区
  14. 设置无效,为什么下载分数经常变化?
  15. PS完美抠取头发丝----更换证件照背景完美去白边/蓝边/红边-----超实用方法
  16. 图像检索系列——利用深度学习实现以图搜图
  17. nginx.conf配置文件中timeout超时时间设置
  18. 为了得到一个数的“相反数“,我们将这个数的数字顺序颠倒,然后再加上原先的数得到“相反数“。
  19. hpux oracle INS-06006 PRVF-7546
  20. 区块链对医疗行业的影响

热门文章

  1. matlab控制算法C语言,PID算法Matlab仿真程序和C程序
  2. 如何学机器计算机,学习编程的你要了解程序是如何被计算机所执行的?
  3. python程序执行时间毫秒_如何使用python解析包含毫秒的时间字符串?
  4. java的string访问某个元素_架构师必懂的——RBAC基于角色的访问权限设计
  5. subd计算机系统结构,计算机体系结构第2章试题答案.doc
  6. 精美的案例教你如何像杂志一样排版!
  7. linux位置变量的应用,llinux中变量的运用
  8. mysql 数据库被覆盖_理解MySQL数据库覆盖索引
  9. python标准输出_Python(2.7)-标准输入输出,标准错误输出
  10. Linux开机启动过程(4):切换到64位模式-长模式(直到内核解压缩之前)