2013-02-05 44 views
0

我正致力于使我的应用程序从串行端口读取数据并更新UI上的标尺更高效,并且我想要询问有关处理UI更改的代码的一些建议。我有一个计时器设置为检查发送到COM端口的数据,另一个计时器使用从COM端口收到的变量更新UI。基本上发生了什么是我旋转仪表。这里是我处理图形的代码...使用定时器更新C#中的用户界面

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(); 
} 

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

+1

你能具体谈谈您希望它是 “更有效”?例如,你觉得定时器回调在没有必要时被调用(自上次回调以来没有改变),方法是否太慢,使用的内存太多?你看到一些可衡量的性能问题吗? – dgvid

+0

这更多的是性能问题,当我尝试在我的笔记本电脑上运行应用程序时,响应非常缓慢。例如,对传感器的响应应该在1-3秒后出现。 – Bubo

+0

标题是关于使用计时器更新UI的问题,但您实际上担心图形效率?你有很多无关的细节,只是浑水。 –

回答

2

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

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

也看看这个代码真的很简单(我使用随机值,所以你可以删除它,并适应你的需要)。

我使用的图纸(XAML中的<Path/>零件)不适合测量仪。我刚刚制定了Path,我懒得创建一个新的。你应该创建一个新的绘图(我建议使用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:

<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> 
1

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

  • 更好的表现;
  • 较少的内存使用量;
  • 或者干脆以下,更优雅,代码

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

您可能还想检查用于更新UI的计时器的刷新率。大约24-30fps的帧率应该足够了。在这种情况下,任何更多的东西都是矫枉过正的,它大多只会浪费CPU周期。

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

编辑

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

+0

作为一个方面的评论,如果你想'更好的性能','更少的内存使用量,和/或'简单更少或更优雅的代码'完全删除winforms并使用WPF。 –

+1

@HighCore - 我不一定同意你的看法。 WPF很棒,原因很多,但它并没有暗示提供上述任何一个。它当然不会使用更少的内存。它并不总是提供更好的表现。最后,就代码优雅而言 - 这很大程度上取决于程序员的技能。您可以使用任何框架编写漂亮的代码和使用任何框架(反之亦然)! –

+0

真的吗?看看[这](http://stackoverflow.com/questions/14565773/improving-winforms-performance-with-large-number-of-controls/14566539#comment20386098_14566539) –

1

看起来像你在Windows窗体中执行此操作?用途:

Graphics.RotateTransform

如果我可以谦卑地建议,不过,如果你试图做任何事情,甚至远程有趣图形,它可能是值得的投资加紧WPF。 Windows Forms依赖于不是硬件加速的旧GDI apis(与基于DirectX构建的WPF不同),使其成为任何形式的严重图形的糟糕平台。无论您使用winform获得的“高效率”,您都无法与任何受硬件加速支持的竞争对手竞争。

+0

我会不同意你的意见。 WPF对于普通用户界面中可能发生的大多数图形来说相当不错,但是如果你正在做任何严肃的事情,WPF将会以你的方式进行,因为即使它基于DirectX,它也不是直接的,而是中级模式DX。如果你需要真正快速的绘制,你可以放弃WPF,只需去C++/Direct2D –

+0

这是绝对正确的。 WPF旨在用于开发用户界面,而不是游戏。但即使在这方面,它仍然远远领先于Winforms。 – sircodesalot

+0

在许多方面是的。这是一个更好的框架。但具有讽刺意味的是,如果你有一个用户界面,你需要做很多自定义绘图 - 尽管很少见 - 你会发现自己很快与WPF战斗。下面是一个例子:信号的实时图形.. –

0

使用GDI +旋转位图的速度会变慢。您可能会做的最大的性能提升是停止使用位图来达到此目的,并且只需使用GDI +矢量图形自定义绘图仪。如果适用,您仍然可以使用位图作为背景,并使用矢量图形绘制测量仪的针。这将比旋转位图快几个数量级。

接下来我要看的是,是否将图片框与动态位图结合使用(即,不断变化)真的是正确的路要走;每次更新时,图片框可能会对位图执行额外的处理,这实际上只是浪费了周期。为什么不自己在屏幕上绘制位图?此外,请确保您的位图是使用正确的像素格式创建的,以获得最佳的绘图性能(PArgb32bpp)。最后,除非你的输入数据是一个不断变化的数值流,否则我会考虑完全抛弃这个定时器,并且只需使用BeginInvoke在你重绘屏幕的时候发出你的UI线程信号。您当前的解决方案可能会受到计时器滴答声之间不必要的延迟的影响,并且它也可能比必要时更频繁地重新绘制仪器。

+0

我不明白怎么回事要做到这一点就是问题所在。我一直在研究这一段时间,并且我很难理解图形库。我怎样才能改变我的代码,使它不使用动态位图的图片框 – Bubo

+0

听起来你需要通过一些关于自定义控件的教程(不要与用户控件混淆)。与其他一些框架相比,GDI +中的图形方法非常简单;您不必担心顶点缓冲区或选择画笔等等。对于您的特定情况,您可能只需要DrawImage方法和DrawLine。 – RogerN

+0

好的,我会努力的,谢谢! – Bubo