使用Timer在C#中更新UI

我正在努力使我的应用程序从串行端口读取数据并更新UI上的仪表更高效,我想要求我处理UI更改的代码的一些建议。 我有一个定时器设置来检查发送到COM端口的数据和另一个定时器,它使用从COM端口接收的变量更新UI。 基本上发生的事情是我正在旋转仪表。 这是我处理图形的代码……

void timer_Tick(object sender, EventArgs e) //Timer regulates how often the gauge is updated on the UI { if (pictureBox1.Image != null) pictureBox1.Image.Dispose(); // dispose old image (you might consider reusing it rather than making a new one each frame) Point test = new Point((int)_xCor, (int)_yCor); Image img = new Bitmap(400, 400); // The box tht contains the image <--- Play around with this more pictureBox1.Image = img; // Setting the img Image to the pictureBox class? Graphics g = Graphics.FromImage(pictureBox1.Image); // G represents a drawing surface Matrix mm1 = new Matrix(); // mm1.RotateAt((float)(90 + (((12.5 * state) - 20.95) * 6)), new Point((int)_xrotate, (int)_yrotate), MatrixOrder.Append); GraphicsPath gp = new GraphicsPath(); g.Transform = mm1; // transform the graphics object so the image is rotated g.DrawImage(imgpic, test); // if the image needs to be behind the path, draw it beforehand mm1.Dispose();// prevent possible memory leaks gp.Dispose();// prevent possible memory leaks g.Dispose(); // prevent possible memory leaks pictureBox1.Refresh(); } 

我想知道是否有一种更有效的方法可以在屏幕上旋转图像。 我觉得必须有,但我无法理解。

这是我第二次为winforms问题提供WPF解决方案。

只需将我的代码复制并粘贴到文件 – >新项目 – > WPF应用程序中,然后自己查看结果。

另外看一下这个代码到底有多简单(我使用的是随机值,所以你可以删除它并根据你的需要调整它)。

我使用的绘图(XAML中的部分)不适合Gauge。 我刚刚绘制了那条路径而且我懒得创建一个新路径。 您应该创建一个新图形(我建议使用Expression Blend)。 但是你可以看到旋转被应用以及它的工作速度。

 using System; using System.Threading; using System.Windows; using System.ComponentModel; namespace WpfApplication4 { public partial class Window2 { public Window2() { InitializeComponent(); DataContext = new ViewModel(); } } public class ViewModel: INotifyPropertyChanged { private double _value; public double Value { get { return _value; } set { _value = value; NotifyPropertyChange("Value"); } } private int _speed = 100; public int Speed { get { return _speed; } set { _speed = value; NotifyPropertyChange("Speed"); Timer.Change(0, value); } } public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChange(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } private System.Threading.Timer Timer; public ViewModel() { Rnd = new Random(); Timer = new Timer(x => Timer_Tick(), null, 0, Speed); } private void Timer_Tick() { Application.Current.Dispatcher.BeginInvoke((Action) (NewValue)); } private Random Rnd; private void NewValue() { Value = Value + (Rnd.Next(20) - 10); } } } 

XAML:

                     

很难回答你的问题,因为你要求“更有效”的图像旋转是非常模糊的。 我不确定你的意思是否更高效:

  • 更好的性能;
  • 内存使用量减少;
  • 或者只是更少或更优雅的代码

在任何情况下,除非你在谈论使代码更“优雅”,而不是我能想到的唯一的事情是你可以,也许应该重复使用相同的图像/位图。 每次只清除正在使用的那个并重新绘制图像时,不要创建新的。

您可能还需要检查用于更新UI的计时器的刷新率。 帧速率约为24 – 30 fps就足够了。 在这种情况下,任何更多的东西都是矫枉过正的,它将主要浪费CPU周期。

您还应启用双缓冲以防止闪烁。

编辑

根据您的评论,听起来问题不是性能,而是COM端口计时器的间隔和UI计时器之间的差异。 听起来更新UI的计时器运行速度不够快,无法检测到更改。您的间隔是多少?

看起来你在Windows Forms中这样做了吗? 使用:

Graphics.RotateTransform

但是,如果我谦虚地建议,如果你试图以图形方式做任何有趣的事情,那么投资WPF可能是值得的。 Windows Forms依赖于旧的GDI apis,它不是硬件加速的(与基于DirectX的WPF不同),使其成为任何类型的严重图形的糟糕平台。 无论你使用winforms多么“高效”,你都无法与硬件加速支持的任何东西竞争。

使用GDI +旋转位图将会很慢,周期。 您可能实现的最大性能提升是停止使用位图来实现此目的,只需使用GDI +矢量图形自定义绘制仪表。 您仍然可以使用位图作为背景,并使用矢量图形绘制仪表针(如果适用)。 这比旋转位图快几个数量级。

接下来我要看的是,将图片框与动态位图结合使用(即不断变化)是否真的是正确的方法; 每次更新时,图片框可能会对您的位图进行额外处理,这实际上只是浪费了周期。 为什么不自己将位图绘制到屏幕上? 此外,请确保使用正确的像素格式创建位图以获得最佳绘图性能(PArgb32bpp)。

最后,除非你的输入数据是一个恒定的变化值流,否则我会考虑完全抛弃计时器,只需要在重绘屏幕时使用BeginInvoke来指示你的UI线程。 您当前的解决方案可能会受到计时器滴答之间不必要的延迟的影响,并且可能还会比必要时更频繁地重新绘制仪表。