Aggiornamento dell’interfaccia utente in C # utilizzando il timer

Sto lavorando per rendere la mia applicazione che legge i dati dalla porta seriale e aggiorna un indicatore sull’interfaccia utente più efficiente e volevo chiedere qualche consiglio sul mio codice che elabora le modifiche all’interfaccia utente. Ho un timer impostato per controllare i dati inviati alla porta COM e un altro timer che aggiorna l’interfaccia utente con la variabile ricevuta dalla porta COM. Fondamentalmente quello che sta succedendo è che sto ruotando un calibro. Ecco il mio codice per la gestione della grafica …

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

Mi chiedo se c’è un modo più efficiente in cui posso ruotare l’immagine sullo schermo. Mi sento come se ci fosse, ma non riesco a capirlo.

Questa è la seconda volta che fornisco una soluzione WPF per un problema di vinci.

Basta copiare e incollare il mio codice in un file -> nuovo progetto -> Applicazione WPF e vedere i risultati per te.

Dai un’occhiata anche a quanto sia semplice questo codice (sto usando valori casuali, quindi puoi rimuoverlo e adattarlo alle tue esigenze).

Il disegno che ho usato (la parte in XAML) non è adeguato per un Gauge. Ho appena avuto quel Path già disegnato e sono troppo pigro per crearne uno nuovo. Dovresti creare un nuovo disegno (ti consiglio di usare Expression Blend). Ma puoi vedere la Rotazione applicata e quanto velocemente funziona.

 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:

                     

È difficile rispondere alla tua domanda perché la tua richiesta di una rotazione “più efficiente” dell’immagine è piuttosto vaga. Non sono sicuro se per più efficiente intendi:

  • migliore prestazione;
  • meno utilizzo della memoria;
  • o semplicemente meno, o più elegante, codice

In ogni caso, a meno che tu non stia parlando di rendere il codice più “elegante” di quello che posso inventare è che potresti, e probabilmente dovresti, riutilizzare la stessa immagine / bitmap. Invece di crearne uno nuovo ogni volta che potresti cancellare quello che stai usando e ridisegnare la tua immagine.

Potresti anche voler controllare la frequenza di aggiornamento del tuo timer che viene utilizzato per aggiornare l’interfaccia utente. Una frequenza fotogrammi di circa 24 – 30 fps dovrebbe essere sufficiente. Qualche cosa in più è eccessivo in questo scenario e per lo più sprecherà solo i cicli della CPU.

Dovresti anche abilitare il doppio buffering per evitare lo sfarfallio.

MODIFICARE

In base ai tuoi commenti, sembra che il problema non siano le prestazioni, ma una discrepanza tra l’intervallo del timer della porta COM e il timer dell’interfaccia utente. Sembra che il timer che aggiorna l’interfaccia utente non funzioni abbastanza velocemente da rilevare una modifica. Quali sono i tuoi intervalli?

Sembra che lo stai facendo in Windows Forms? Uso:

Graphics.RotateTransform

Se posso suggerire umilmente, però, se stai cercando di fare qualcosa anche solo lontanamente interessante dal punto di vista grafico, potrebbe valere la pena di investire in WPF. Windows Form si basa sulle vecchie API di GDI che non sono accelerate dall’hardware (a differenza di WPF che è basato su DirectX), rendendolo una piattaforma scadente per qualsiasi tipo di grafica seria. Non importa quanto sia “efficiente” con le winform, non sarai mai in grado di competere con qualsiasi cosa supportata dall’accelerazione hardware.

La rotazione di una bitmap tramite GDI + sarà lenta, punto. Il più grande aumento di prestazioni che potresti probabilmente fare è smettere di usare una bitmap per questo scopo e solo personalizzare il tuo indicatore con grafica vettoriale GDI +. Puoi ancora usare una bitmap per lo sfondo e usare la grafica vettoriale per disegnare l’ago dell’indicatore, se applicabile. Sarebbe un ordine di grandezza più veloce della rotazione di una bitmap.

La prossima cosa che guarderei è se usare una picture box in combinazione con una bitmap dynamic (cioè in continua evoluzione) sia davvero la strada giusta da percorrere; la casella immagine potrebbe eseguire un’ulteriore elaborazione sulla bitmap ogni volta che viene aggiornata, il che è in realtà solo cicli sprecati. Perché non disegnare da solo la bitmap sullo schermo? Inoltre, assicurati che la tua bitmap sia creata con il formato pixel corretto per ottenere prestazioni di disegno ottimali (PArgb32bpp).

Infine, a meno che i dati di input non siano un stream costante di valori variabili, prenderei in considerazione l’ipotesi di abbandonare completamente il timer e di utilizzare BeginInvoke per segnalare il thread dell’interfaccia utente quando è il momento di ridisegnare lo schermo. La tua soluzione attuale potrebbe risentire di una latenza non necessaria tra i tick del timer e potrebbe anche ridisegnare lo strumento più spesso del necessario.