公司需要我写几个GUI程序,让虚拟机(guest)内部可以控制虚拟机(host)外部的硬件。

控制外部的硬件的方法就是开一个串口,这样虚拟机与宿主机就可以相互通讯,此时就可以让虚拟机发送命令,宿主机执行命令,并返回结果

我需要一行行地展示内容,比如这样:

使用的是 WinForm,很容易找到 FlowLayoutPanel这个组件,结果其效果可谓是大跌眼镜:

  1. 画面残影

  1. 画面出现大量空白

这两种现象,微软官方称为flicker,中文翻译为闪烁。可以见官方文档:

https://learn.microsoft.com/zh-cn/dotnet/api/system.windows.forms.flowlayoutpanel?view=windowsdesktop-8.0

可见微软也承认自己的东西有这个毛病了,所以微软我xxx。

我后面和同学聊了下这个问题,他说Qt也有这个毛病,我几乎没用过Qt,所以不知道具体是什么样子的。

查了很多方法,网上的说法清一色的是设置DoubleBuffered,算是有点用处,结果就是“残影”没了,大量空白出现了,拖动条也变得卡顿了,真就给抄成习惯了……

然而浏览器、文件管理器的拖动条是正常的,这说明这个问题是可以被解决的。

顺便一提,拖动界面时可以出现界面刷新率低的问题,刷新率低到10也可以,但绝对不能出现闪烁问题。降低帧率似乎就是浏览器的做法,这也提醒我在界面绘制完成之前不要进行下一次绘制。

公司里没有人会 C#,所以也没有人可问,没办法,只能另寻它路。

想法一:逆向

首先想到的是逆向。浏览器不值得看,因为其主要界面大概率不是,文件管理器大概率可以。下载了几个界面分析软件,包括 SPY++,GUI-wizard等等,拿到的窗口的类名是没有见过的,所以放弃了这种做法

想法二:找其他开源框架

问GPT,推荐了ReaLTaiizor,SunnyUI,CSharpSkin之类的,只有ReaLTaiizor可以看到源代码,就下载它来用了。而它确实没有出现这个闪烁的问题。

虽然更换到这个开源框架也行,但是发现它自带的滚动界面不能满足我的要求,而且目前也已经实现了所有功能了,只差这个问题就可以提交给测试了,我希望快点做完,于是决定参考其代码做一个简单的组件。

ReaLTaiizor 对滚动界面的实现是:手写界面更新逻辑。也就是说重写了OnPaint方法,自己定义了一套绘制逻辑,要画线就调用划线的方法直接操作界面,要绘图就就调用绘图的方法直接操作界面……

想法三:参考ReaLTaiizor

我需要的是一个组件而不是一个GUI库,基于这样的想法,我确定了这个组件必须做成什么样子的:

  1. 表格形式,每一行的高度相同,每一列的宽度可以自定义,每一格中存储的内容可以是图片或者文字,文字需要支持换行。和本文第一张图相似的表格就行了。图片和文字就够了,拿两张图片,再绑定一个回调就可以做一个开关,这样按钮也有了
  2. 最重要的一点,绝对不能够闪烁,且滚动条需要与界面显示同步
  3. 需要一个回调,回调的参数是点击到的行,点击到的列,然后该行绑定到的对象,这一行的内容
  4. 由3引申出来的,即每一行都可以绑定一个对象
  5. 只支持上下滚动,左右滚动不支持
  6. 可以增加行、更新行或删除行,操作之后界面必须体现出来

所以就开始抄这个源代码了。因为是公司的代码所以不好贴出来,总体分为这几步:

  1. 基本验证

我不知道手写界面绘制能否解决问题,所以还是验证了一下。验证的方法很简单,每次鼠标滚轮事件触发了,就给界面换一个颜色,手机拍摄这整个过程,逐帧播放,检查是否有闪烁的问题。

验证之后,发现正常,所以继续实现了。

  1. 界面显示与同步滚动条与界面显示位置同步

界面显示就是每一次鼠标滚动滚轮,或者拖动滚动条,都调用界面刷新方法,将界面刷新,手动的把图片、文字刷新上去。

前文提到的“界面绘制完成之前不要进行下一次绘制”也在代码中实现了,方法很简单,添加一个bool isupdating,进入OnPaint之前检查它的值,为false就设置为true,然后在结束的时候设置回false;为true就直接返回,放弃这一回绘制。虽然可能没有用吧,但万一呢?

界面位置与滚动条位置同步问题,其实就是一个比例转换的问题,总体就是一个公式:

\[\frac{界面显示的位置}{界面的总高度}=\frac{滚动条位置}{滚动条总长度}
\]

关于这个公式,网上有一篇博客讲的不错。

https://www.cnblogs.com/lesliexin/p/13440927.html

虽然讲的不错,但是他没有解决闪烁问题,源代码我下载过来试了,评论里也有人说他没有解决这个问题

  1. 开启DoubleBuffered

其实手写了界面绘制之后,发现闪烁问题依然没有解决。此时我猜想是没有开启DoubleBuffered导致的。开启之后,问题解决了。

到此我猜想,DoubleBuffered的语义确实是微软描述的那样:界面先绘制到缓冲区,然后在一口气把缓冲区的内容显示到界面上。

但是为什么FlowLayoutPanel开启了这个功能,效果也没有多好?下班后我逆向了FlowLayoutPanel的源代码,发现它压根没有走OnPaint的绘制逻辑,所以DoubleBuffered应该是无效的。

到此界面效果就是下图了:

录制滚动界面的视频,逐帧播放,效果都与上图相似。当然,这个界面依然还有问题,见问题4。

  1. 实现回调

没什么好说的,就是将点击位置转换为行坐标与列坐标

  1. 界面模糊与字体锯齿严重

这界面模糊可以见这篇博客。

https://www.cnblogs.com/Wonderful-Life/p/10250575.html

字体抗拒齿严重,GPT说这么做就行了,事实证明这确实有效:

protected override void OnPaint(PaintEventArgs e)
{
Debug.Assert(rows.Count == bindObj.Count);
base.OnPaint(e);
if (rows.Count == 0) return;
double bar_position = GetScrollBarPosition(vsb.Value);
Graphics graphics = e.Graphics;
// graphics.TextRenderingHint = System.Drawing.Text.TextRendering
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
Rectangle rect = new Rectangle(0, 0, base.Width, base.Height);

另一个尝试

这个组件只支持表格形式,我后面想试试能不能做一个更加通用的组件,即每行一个,但这一行的内容可以是WinForm中任何一种组件。

简单写了一个Demo,其基本想法是,不在界面以内的不显示,在界面以内的计算其位置并显示,绘制的方法用默认的。

效果如下图。它确实不闪烁了,但是界面变得割裂了!肉眼可见的界面从上面往下面刷新!

所以如果WinForm下想要彻底解决闪烁问题,其工作量估计和做一个GUI库差不多了。

C# 图形界面编程之 FlowLayoutPanel 界面闪烁问题解决的更多相关文章

  1. 界面编程之QT的线程20180731

    /*******************************************************************************************/ 一.为什么需 ...

  2. 界面编程之QT的Socket通信20180730

    /*******************************************************************************************/ 一.linu ...

  3. 界面编程之QT的基本介绍与使用20180722

    /*******************************************************************************************/ 一.qt介绍 ...

  4. 界面编程之QT的数据库操作20180801

    /*******************************************************************************************/ 一.数据库连 ...

  5. 界面编程之QT窗口系统20180726

    /*******************************************************************************************/ 一.坐标系统 ...

  6. 界面编程之QT的信号与槽20180725

    /*******************************************************************************************/ 一.指定父对 ...

  7. 界面编程之QT绘图和绘图设备20180728

    /*******************************************************************************************/ 一.绘图 整 ...

  8. 界面编程之QT的文件操作20180729

    /*******************************************************************************************/ 一.QT文件 ...

  9. 界面编程之QT的事件20180727

    /*******************************************************************************************/ 一.事件 1 ...

  10. Java图形界面学习---------简易登录界面

    /** * @author Administrator * Java图形界面学习---------简易登录界面 * date:2015/10/31 */ import java.awt.BorderL ...

随机推荐

  1. 聊一聊 C#线程池 的线程动态注入 (中)

    一:背景 1. 讲故事 上一篇我们用 Thread.Sleep 的方式演示了线程池饥饿场景下的动态线程注入,可以观察到大概 1s 产生 1~2 个新线程,很显然这样的增长速度扛不住上游请求对线程池的D ...

  2. Windows 记录开机后应用启动慢的问题

    最近大屏产品经常报一些开机启动的问题,工厂反馈厂测软件有些模块测试不通过,家里开发测试均发现Launcher等软件首次启动需要加载10多秒. 经过小伙伴们排查,发现是刷母盘后首次开机问题概率比较大.使 ...

  3. Qt音视频开发11-ffmpeg常用命令

    一.前言 大部分的格式转换工具比如格式化工厂等,都用到了ffmpeg来处理,ffmpeg编译后生成的ffmpeg.exe.ffplay.exe.ffprobe.exe等可执行文件,其实就封装了众多牛逼 ...

  4. 揭秘vivo百亿级厂商消息推送平台的高可用技术实践

    本文由vivo 互联网服务器团队Yu Quan分享,本文收录时有内容修订和重新排版. 1.引言 如今,Android端的即时通讯IM这类应用想实现离线消息推送,难度越来越大(详见<Android ...

  5. 决策单调性优化 DP

    前言 本文将介绍决策单调性优化 DP 的相关内容.持续更新修正,如有差错请指出. 1.四边形不等式优化 DP 1.1 四边形不等式与决策单调性 四边形不等式:如果对于任意的 \(a \le b \le ...

  6. 2021 年万圣节 Github 彩蛋

    记录每年 Github 万圣节彩蛋,也记录有来项目成长历程. 2021 万圣节彩蛋 2020 万圣节彩蛋

  7. Diary -「NOI 2022」尘降

      又一次,以这样一种身份来到国赛赛场.起跑线延长出赛场外,我将于此开始又一场已知"无用"的竞技. 虚无中 我的尘埃盲目漂泊摇晃   时间回到数个月前的省选,\(600\) 分的总 ...

  8. CF div2 994 (A~E)

    VP赛时三题,自我感觉发挥不错,唯一不满意的地方在于D题完全没有思路. A 答案最多为2,因为最坏情况即为先将整个区间合并为一个数,若这个数不是0,则再将这个数变为0. 所以3种情况分类讨论即可: 全 ...

  9. weblogic-copy

    一.简介 WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发.集成.部署和管理大型分布式Web ...

  10. C#定点执行任务测试案例

    定时方法实现类 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text ...