Click here to Skip to main content
15,887,326 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
hey guys,

i wrote an analog clock in c#, it works but after a few seconds it gets really unresponsive. i think this has something to do with the timer_tick method of the clock class. i posted the code below, sorry for the german comments, i will translate them if you want.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;

namespace Stoppuhr
{
    class Program
    {
        //private static Label digiZeit = new Label ();
        //private static Timer digi = new Timer ();
        private static Form frm;
        private static Clock clock;

        public Program (Size size)
        {
            initComponents (size);
            frm.Size = size;
            clock.Location = new Point ( new Size ( frm.ClientRectangle.Size.Width / 6, frm.ClientRectangle.Size.Height / 6 ) );
            //digi.Interval = 1000;
            //digi.Start ();
            //digiZeit.Location = new Point ( frm.Size.Width / 2, frm.Size.Height - ( frm.Size.Height ) / 10 );
            //frm.FormBorderStyle = FormBorderStyle.FixedDialog;                                    
            //frm.Controls.Add ( digiZeit );
            frm.Controls.Add ( clock );
            //digiZeit.AutoSize = true;
            //digiZeit.Font = new Font ( "Arial", 20 );
            //digi.Tick += new EventHandler ( digi_Tick );
            frm.BackColor = Color.Gray;
            frm.TransparencyKey = Color.Gray;
            frm.FormBorderStyle = FormBorderStyle.None;
            frm.ShowDialog ();
        }
        public static void Main ( String [] args )
        {
            Program uhr = new Program ( new Size ( 400, 400 ) );
        }        

        private static void initComponents (Size size)
        {
            frm = new Form ();            
            clock = new Clock (size);            
        }

        //static void digi_Tick ( object sender, EventArgs e )
        //{
        //    digiZeit.Text = DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second;
        //}       
    }

    class Clock : Panel
    {
        /**
         * Eigenschaften eines Clock-Objektes
         * */
        private static Zeiger sekunde;
        private static Zeiger minute;
        private static Zeiger stunde;
        private static Timer tmr;
        private static Ziffernblatt blatt;
        private static int counter;
        private static PaintEventArgs pea;
        

        
        /**
         * Standard Konstruktor der Klasse Clock
         * */
        public Clock ( Size size )
        {            
            this.Size = size;
            initComponents ();
            initZeiger ();
            this.SetStyle ( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true );
            tmr = new Timer ();
            tmr.Interval = 1000;
            tmr.Start ();
        }

        /**
         * Eigenschaften initialisieren
         * */
        private void initComponents ()
        {
            blatt = new Ziffernblatt ( this.Size );
            sekunde = new Zeiger ( new Pen ( Color.Black, 2 ) );
            minute = new Zeiger ( new Pen ( Color.Red, 4 ) );
            stunde = new Zeiger ( new Pen ( Color.Blue, 5 ) );
            counter = 0;            
        }
        

        /**
         * Zeiger-Eigenschaften beim Start setzen
         * */
        private void initZeiger ()
        {
            sekunde.Laenge = 140;
            sekunde.Size = this.Size;

            minute.Laenge = 140;
            minute.Size = this.Size;

            stunde.Laenge = 100;
            stunde.Size = this.Size;

            sekunde.Winkel = DateTime.Now.Second * 6;
            minute.Winkel = DateTime.Now.Minute * 6;
            stunde.Winkel = ( DateTime.Now.Hour % 12 ) * 30;
        }

        /**
         * Methode überschreibt die OnPaint-Methode eines Panels
         * */
        protected override void OnPaint ( PaintEventArgs e )
        {            
            base.OnPaint ( e );
            pea = e;
            blatt.paint (e);
            sekunde.paint ( e );
            minute.paint ( e );
            stunde.paint ( e );
            tmr.Tick += new EventHandler ( tmr_Tick );            
        }

        /**
         * Nach jedem Tick des Tickers stelle Winkel von Zeigern um...
         * */
        private void tmr_Tick ( object sender, EventArgs e )
        {         
            sekunde.Winkel = DateTime.Now.Second * 6;
            minute.Winkel = DateTime.Now.Minute * 6;
            stunde.Winkel = ( DateTime.Now.Hour % 12 ) * 30;
            this.Refresh ();
        }        
    }

    class Ziffernblatt : Panel
    {    
        /**
         * Standard-Konstruktor eines Ziffernblatts
         * */
        public Ziffernblatt ( Size size )
        {
            this.BackColor = Color.Transparent;
            //Methode setzt die Größe eines Ziffernblatts
            this.Size = new Size ( size.Width - size.Width / 3, size.Height - size.Height / 3 ); 
            //this.BorderStyle = BorderStyle.FixedSingle;
        }

        /**
         * Methode dient dem umgehen des protected-Modifikators der OnPaint-Override Methode
         * */
        public void paint ( PaintEventArgs e )
        {
            this.OnPaint ( e );
        }

        /**
         * Methode überschreibt die OnPaint Methode eines Panels
         * */
        protected override void OnPaint ( PaintEventArgs e )
        {
            this.Dispose ();
            base.OnPaint ( e );

            //Verschiebe 0,0 Koordinate in die Mitte des Panels
            e.Graphics.TranslateTransform ( this.Size.Width / 2, this.Size.Height / 2 );
            Brush brush = Brushes.Black;
            Rectangle rect = new Rectangle ( -5, -5, 11, 11 );

            e.Graphics.FillRectangle ( brush, rect );

            //Zeichne die Striche des Ziffernblatts
            for ( int x = 1; x <= 60; x++ )
            {
                Pen pen = new Pen ( Color.Black, 2 );
                e.Graphics.DrawLine ( pen, 0, -this.Height / 2, 0, -( this.Height / 2 - this.Height / 20 ) );
                e.Graphics.RotateTransform ( 6 );
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                //Jede 6°, setze einen Strich
                if ( ( x % 5 ) == 0 )
                {
                    e.Graphics.DrawLine ( pen, 0, -this.Height / 2, 0, -( this.Height / 2 - this.Height / 10 ) );
                }
                //Jede 90° setze einen dicken Strich
                if ( ( x % 15 ) == 0 )
                {
                    pen.Width = 5;
                    e.Graphics.DrawLine ( pen, 0, -this.Height / 2, 0, -( this.Height / 2 - this.Height / 10 ) );

                    e.Graphics.DrawString ( "II", new Font ( "Arial", 11 ), brush, -5, -( this.Height / 2 - this.Height / 9 ) );
                }
            }
        }
    }

    class Zeiger : Panel
    {
        /**
         * Eigenschaften eines Zeigers
         * */
        Pen pen;
        int laenge;
        int breite;
        int winkel;

        /**
         * Standard Konstruktor eines Zeigers
         * */
        public Zeiger ( Pen pen )
        {
            laenge = new int ();
            breite = new int ();
            this.pen = pen;
        }

        /**
         * set und get Methoden der laenge-Eigenschaft eines Zeigers
         * */
        public int Laenge
        {
            get { return laenge; }
            set { laenge = value; }
        }

        /**
         * set und get Methoden der winkel-Eigenschaft eines Zeigers
         * */
        public int Winkel
        {
            get { return winkel; }
            set { winkel = value; }
        }

        /**
         * Methode dient dem Umgehen des protected-Modifikators der OnPaint-Override Methode
         * */
        public void paint ( PaintEventArgs e )
        {
            this.OnPaint ( e );
        }

        /**
         * Methode dient dem Überschreiben der OnPaint Methode eines Panels
         * */
        protected override void OnPaint ( PaintEventArgs e )
        {
            this.Dispose ();
            Graphics g = e.Graphics;
            //Setze Position und Winkel eines Zeigers
            g.ResetTransform ();
            g.TranslateTransform ( this.Width / 3, this.Height / 3 );
            g.RotateTransform ( winkel );
            //Zeichne einen Zeiger
            g.DrawLine ( pen, 0, 0, 0, -this.Laenge );
        }
    }
}


as you can see, after i initialize a form i populate it with a clock-object which in turn holds 3 Zeiger(clock hand) objects and a ziffernblatt(number wheel) object. these 4 objects are then put into the clock-panel. i implemented a counter in the timer_tick method and noticed that it accumulated a huge value after a while. this is why i think the fault lies in said method.

i hope you can point me in the right direction here...
Posted
Comments
n.podbielski 11-Oct-12 6:14am    
You should stop timer i its tick event and start after to prevent to handlers run in the same time. I am not sure if this is a problem but this general solution for that kind of problem.

in each Clock.OnPaint you add a new timer eventhandler. AFAIK you only need one single eventhandler to trigger the refresh.

Try the following in the Clock ctor:
C#
tmr = new Timer ();
tmr.Interval = 1000;
tmr.Tick += new EventHandler ( tmr_Tick );
tmr.Start ();


And remove
tmr.Tick += new EventHandler ( tmr_Tick );
from the Clock.OnPaint()
 
Share this answer
 
C#
protected override void OnPaint ( PaintEventArgs e )
{
    base.OnPaint ( e );
    pea = e;
    blatt.paint (e);
    sekunde.paint ( e );
    minute.paint ( e );
    stunde.paint ( e );
    tmr.Tick += new EventHandler ( tmr_Tick );
}

There is your problem!

You are adding a handler to the handler list every single time the clock changes!

And you are surprised it "becomes unresponsive"? Particularly when all of the Tick handlers refresh the display, and this cause a Paint?
 
Share this answer
 
The solution is that you shouldn't put the line
C#
tmr.Tick += new EventHandler ( tmr_Tick );

in the OnPaint method of the Clock.
Instead you should put it in the Clock .ctor just before the line
C#
tmr.Start ();


OnPaint will be called maybe a million times, so the call count of the tmr_Tick-EventHandler will rise exponentially.
 
Share this answer
 
ok guys thanks for your answers, it works flawlessly now. no more unresponsiveness.

i thought that something like this was happening, as the counter i implemented before seemed to increase its value exponentially. now it increases its value in correspondence with the interval of the timer, great help!

thanks again
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900