2013-02-05 76 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