Updating user interface in C # using timer

I am working on making my application that reads data from the serial port and updating the metric in the user interface more efficient, and I wanted to ask for advice on my code that handles user interface changes. I have a timer set to verify that the data has been sent to the COM port and another timer that updates the user interface using a variable received from the COM port. Basically what happens, I rotate the caliber. Here is my graphics processing code ...

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

I am wondering if there is a more efficient way to rotate the screen image. I feel it should be, but I can’t understand it.

0
user-interface c # visual-studio-2010 timer winforms
source share
4 answers

This is the second time I provide a WPF solution for the winforms problem.

Just copy and paste my code into a file -> new project -> WPF application and see the results for yourself.

Also consider how simple this code is (I use random values, so you can remove them and adapt to your needs).

The drawing I used (the <Path/> part in XAML) is not enough for calibration. I have just drawn this Path, and I'm too lazy to create a new one. You must create a new drawing (I recommend using Expression Blend). But you can see what Rotation is applied and how fast it works.

 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> 
+2
source share

It is difficult to answer your question because your request for "more efficient" image rotation is rather vague. I'm not sure if you mean more efficiently:

  • better performance;
  • less memory usage;
  • or just smaller or more elegant code

In any case, if you are not talking about making the code more "elegant" than the only thing I can think of is that you could and probably should have reused the same image / bitmap . Instead of creating a new one, every time you can just clear the one you are using and re-draw the image.

You can also check the refresh rate of your timer, which is used to update the user interface. A frame rate of about 24-30 frames per second should be sufficient. There is still too much in this scenario, and basically it will just waste processor cycles.

You should also enable double buffering to prevent flickering.

EDIT

Based on your comments, it seems that the problem is not in performance, but in the discrepancy between the COM port timer interval and the user interface timer. A timer sounds, which updates the user interface, does not work fast enough to detect changes. What are your intervals?

+1
source share

Looks like you are doing this on Windows Forms? Using:

Graphics.RotateTransform

If I can humbly suggest, however, if you are trying to make something even more distantly interesting graphically, it might be worth investing in WPF. Windows Forms relies on the old GDI apis, which is not hardware accelerated (unlike WPF, which is built on DirectX), which makes it a poor platform for any serious graphics. No matter how "efficient" you get with winforms, you can never compete with anything backed by hardware acceleration.

+1
source share

Rotating a bitmap using GDI + will be a slow period. The biggest performance increase you could probably make is to stop using a bitmap for this purpose and just set up your drawing using GDI + vector graphics. You can still use the bitmap for the background and use vector graphics to draw the calibration needle, if applicable. This will be an order of magnitude faster than rotating the bitmap.

The next thing I will look at is whether using a window with a picture in combination with a dynamic bitmap (i.e. constantly changing) is really the right way; the image field can do extra processing on your bitmap every time it is updated, which is actually just wasted. Why not just draw a bitmap on the screen yourself? Also, make sure your bitmap is created with the correct pixel format for optimal drawing performance (PArgb32bpp).

Finally, if your input is not a constant stream of changing values, I would think of completely disabling the timer and just using BeginInvoke to signal your UI thread when it is time to redraw the screen. Your current solution may be due to an unnecessary delay between timer ticks, and it may also redraw the calibration more often than necessary.

0
source share

All Articles