使用计时器更新C#中的UI
我正在努力使我的应用程序从串行端口读取数据并更新UI上的仪表更高效,我想就处理UI更改的代码征求一些建议。我设置了一个计时器来检查发送到COM端口的数据,另一个计时器用从COM端口接收的变量更新UI。基本上,我正在旋转一个量具。这是我处理图形的代码使用计时器更新C#中的UI,c#,winforms,visual-studio-2010,user-interface,timer,C#,Winforms,Visual Studio 2010,User Interface,Timer,我正在努力使我的应用程序从串行端口读取数据并更新UI上的仪表更高效,我想就处理UI更改的代码征求一些建议。我设置了一个计时器来检查发送到COM端口的数据,另一个计时器用从COM端口接收的变量更新UI。基本上,我正在旋转一个量具。这是我处理图形的代码 void timer_Tick(object sender, EventArgs e) //Timer regulates how often the gauge is updated on the UI { if (pictureB
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();
}
void timer\u Tick(object sender,EventArgs e)//计时器调节仪表在UI上更新的频率
{
if(pictureBox1.Image!=null)
PrimeBox 1.Mig.Puffic();/ /处置旧图像(您可能考虑重用它,而不是每帧创建一个新的)。
点测试=新点((int)xCor,(int)yCor);
Image img=new Bitmap(400400);//包含图像的框很难回答您的问题,因为您要求图像的“更高效”旋转非常模糊。我不确定您所说的“更高效”是否指:
- 更好的表现
- 更少的内存使用
- 或者简单地说是更少,或者更优雅的代码
在任何情况下,除非你说的是让代码更“优雅”,否则我唯一能想到的是,你可以,也可能应该,重复使用相同的图像/位图。而不是每次都创建一个新的图像/位图,你可以清除正在使用的图像并重新绘制图像
您可能还需要检查用于更新UI的计时器的刷新率。大约24-30 fps的帧速率就足够了。在这种情况下,任何更多的都是多余的,它将主要浪费CPU周期
您还应该启用双缓冲以防止闪烁
编辑
根据您的评论,问题似乎不在于性能,而在于COM端口计时器和UI计时器的间隔不一致。似乎更新UI的计时器运行速度不够快,无法检测到更改。您的间隔是多少?很难回答您的问题,因为您要求“更高效”图像的旋转非常模糊。我不确定“更高效”是指:
- 更好的表现
- 更少的内存使用
- 或者简单地说是更少,或者更优雅的代码
在任何情况下,除非你说的是让代码更“优雅”,否则我唯一能想到的是,你可以,也可能应该,重复使用相同的图像/位图。而不是每次都创建一个新的图像/位图,你可以清除正在使用的图像并重新绘制图像
您可能还需要检查用于更新UI的计时器的刷新率。大约24-30 fps的帧速率就足够了。在这种情况下,任何更多的都是多余的,它将主要浪费CPU周期
您还应该启用双缓冲以防止闪烁
编辑
根据您的评论,问题似乎不是性能问题,而是COM端口计时器和UI计时器的间隔不一致。似乎更新UI的计时器运行速度不够快,无法检测到更改。您的间隔是多少?看起来您是在Windows窗体中执行此操作的。使用:
但是,如果我谦虚地建议,如果您试图以图形化的方式进行任何有趣的工作,那么升级到WPF可能是值得的。Windows窗体依赖于旧的GDI API,这些API不是硬件加速的(与基于DirectX的WPF不同),这使得它对于任何类型的严肃图形来说都是一个糟糕的平台。无论winforms有多“高效”,你都无法与任何有硬件加速支持的东西竞争。看起来你是在Windows窗体中这样做的?使用:
但是,如果我谦虚地建议,如果您试图以图形化的方式进行任何有趣的工作,那么升级到WPF可能是值得的。Windows窗体依赖于旧的GDI API,这些API不是硬件加速的(与基于DirectX的WPF不同),这使得它对于任何类型的严肃图形来说都是一个糟糕的平台。无论winforms有多“高效”,你永远无法与任何有硬件加速支持的东西竞争。使用GDI+旋转位图的速度会很慢,周期性地。你可能会获得的最大性能提升是停止使用位图这就是目的,只需使用GDI+矢量图形自定义绘制仪表。如果适用,您仍然可以使用位图作为背景,并使用矢量图形绘制仪表指针。这将比旋转位图快几个数量级
接下来我要看的是,是否将图片框与动态位图结合使用(即,不断变化)这确实是正确的方法;每次更新位图时,图片框可能会对其进行额外处理,这实际上只是浪费了周期。为什么不自己在屏幕上绘制位图呢?另外,确保使用正确的像素格式创建位图,以获得最佳的绘图性能(PArgb32bpp)
最后,除非输入数据是不断变化的值流,否则我会考虑完全放弃定时器,而只是在开始重新绘制屏幕的时候使用NealJooCK来向UI线程发出信号。您当前的解决方案可能会在定时器滴答之间产生不必要的延迟,并且它也可能经常重新绘制量规。使用GDI+旋转位图的速度会很慢,周期。您可能会提高的最大性能是停止使用位图,只需使用GDI+矢量图形自定义绘制仪表。您仍然可以使用位图作为背景,使用矢量图形绘制仪表指针,如果适用。这将比旋转位图快几个数量级
下一个
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);
}
}
}
<Window x:Class="WpfApplication4.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2" WindowState="Maximized">
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<TextBlock Text="Delay (MS):" Margin="2"/>
<Slider Width="200" Minimum="100" SmallChange="1" LargeChange="10" Maximum="1500" Value="{Binding Speed}" Margin="2"/>
<TextBlock Text="Current Value:" Margin="2"/>
<TextBox Text="{Binding Value}" Margin="2"/>
</StackPanel>
<Path Data="M0.95991516,0.5 L73.257382,1.866724 90.763535,1.866724 90.763535,90.822725 66.430534,90.822725 66.430534,26.075016 0.5,24.828653 z" Fill="#FF506077" RenderTransformOrigin="0.861209625003783,0.507482926584064" Stretch="Fill" Stroke="Black">
<Path.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleY="1" ScaleX="-1"/>
<SkewTransform AngleY="0" AngleX="0"/>
<RotateTransform Angle="{Binding Value}" x:Name="Rotation"/>
<TranslateTransform/>
</TransformGroup>
</Path.LayoutTransform>
</Path>
</DockPanel>
</Window>