1

I created an analog clock that will work properly according to real time.

I don't want you to solve it for me or anything - my code is already working, but it's flickering heavily and with my limited knowledge I wasn't able to determine what exactly it is I need to change to stop the flicker.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ClockAndStuff
{

    public partial class TheName : Form
    {

        public TheName()
        {
            InitializeComponent();
            timer1.Start();
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics surfaceBckg;
            Graphics surfaceMin;
            Graphics surfaceH;
            Graphics surfaceSec;

            Pen penMin = new Pen(Color.Pink,2);
            Pen penH = new Pen(Color.Red,3);
            Pen penSec = new Pen(Color.DeepPink,1);
            Pen black = new Pen(Color.Black, 4F);

            surfaceBckg = panel1.CreateGraphics();
            surfaceBckg.TranslateTransform(250, 250);
            surfaceBckg.DrawEllipse(black,-220,-220,440,440);

            surfaceMin = panel1.CreateGraphics();
            surfaceMin.TranslateTransform(250, 250);
            surfaceMin.RotateTransform(6 * DateTime.Now.Minute);

            surfaceH = panel1.CreateGraphics();
            surfaceH.TranslateTransform(250, 250);
            surfaceH.RotateTransform(30 * DateTime.Now.Hour);

            surfaceSec = panel1.CreateGraphics();
            surfaceSec.TranslateTransform(250, 250);
            surfaceSec.RotateTransform(6 * DateTime.Now.Second);

            surfaceMin.DrawLine(penMin, 0, 0, 0, -200);
            surfaceH.DrawLine(penH, 0, 0, 0, -120);
            surfaceSec.DrawLine(penSec, 0, 0, 1, -180);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            panel1.Refresh();
        } 
    }
}

It's correct according to my assignment, but I can't help but to think there must be better way to do it.

I'm not yet big on C# so if you could give me code sample as an answer there's bigger possibility I'll be able to get to understand it...

This is what it looks like

rene
  • 41,474
  • 78
  • 114
  • 152
Kešu
  • 92
  • 10
  • How often does the timer_tick get called? – rene Jan 04 '15 at 11:15
  • 2
    you can find many solution like [this](http://stackoverflow.com/questions/16882921/how-to-fix-panel-flickering-when-redrawing), [this](http://stackoverflow.com/questions/8046560/how-to-stop-flickering-c-sharp-winforms) or [this](http://stackoverflow.com/questions/4777135/how-can-i-draw-on-panel-so-it-does-not-blink) – Grundy Jan 04 '15 at 11:18
  • @Grundy I saw all of these links already, but I simply haven't been able to determine how to use them to solve my problem, that's why I asked again **with** the specifics of my problem – Kešu Jan 04 '15 at 11:31
  • If you cannot figure out the difference between the duplicate's solution and yours: remove **all** calls to `.CreateGraphics()` and use `e.Graphics` instead. Read the duplicate for an explanation. – nvoigt Jan 04 '15 at 11:36
  • @nvoigt I actually did read the duplicate question and the answer for it prior to posting this one, but I suppose still not getting it leaves me with a whole different problem to research here :) – Kešu Jan 04 '15 at 11:40
  • 1
    Well you did not follow the duplicates solution *and* you did not indicate *what* you did not understand. My comment included a really simple one-sentence-solution. If you still don't know what to do, I don't know how to explain it any better. – nvoigt Jan 04 '15 at 11:51

2 Answers2

1

I would suggest creating a custom control that enables proper double buffering:

public class Canvas : Control
{
    public Canvas()
    {
        this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
        this.SetStyle( ControlStyles.OptimizedDoubleBuffer, true );
        this.SetStyle( ControlStyles.UserPaint, true );
        this.SetStyle( ControlStyles.ResizeRedraw, true );
    }
}

Hook your paint code to the Paint event and use Invalidate to trigger a redraw. Never use CreateGraphics, paint to the graphics object supplied to the event. This will get you completely flicker free drawing!

private void canvas1_Paint( object sender, PaintEventArgs e )
{
    // do your painting here, using the Graphics object passed to
    // you in the PaintEventArgs

    Pen penMin = new Pen( Color.Pink, 2 );
    Pen penH = new Pen( Color.Red, 3 );
    Pen penSec = new Pen( Color.DeepPink, 1 );
    Pen black = new Pen( Color.Black, 4f );

    e.Graphics.TranslateTransform( 250, 250 );
    e.Graphics.DrawEllipse( black, -220, -220, 440, 440 );

    e.Graphics.ResetTransform();
    e.Graphics.TranslateTransform( 250, 250 );
    e.Graphics.RotateTransform( 6 * DateTime.Now.Minute );
    e.Graphics.DrawLine( penMin, 0, 0, 0, -200 );

    e.Graphics.ResetTransform();
    e.Graphics.TranslateTransform( 250, 250 );
    e.Graphics.RotateTransform( 30 * DateTime.Now.Hour );
    e.Graphics.DrawLine( penH, 0, 0, 0, -120 );

    e.Graphics.ResetTransform();
    e.Graphics.TranslateTransform( 250, 250 );
    e.Graphics.RotateTransform( 6 * DateTime.Now.Second );        
    e.Graphics.DrawLine( penSec, 0, 0, 1, -180 );
}
Chris
  • 5,442
  • 17
  • 30
  • Just what do you mean by "never use CreateGraphics"? I mean, it obviously tells me not to use it, but I don't know what else to do about it really. But I suppose limited knowledge is my problem here.. – Kešu Jan 04 '15 at 11:36
  • When I change it, it simply draws no arms. E.g.: `e.Graphics.TranslateTransform(250, 250);` `e.Graphics.RotateTransform(6 * DateTime.Now.Second);` `e.Graphics.DrawLine(penSec, 0, 0, 1, -180);` etc. – Kešu Jan 04 '15 at 11:51
  • That's because when you in your original code create a new graphics object, you lose the previous transforms. But now when you use the same graphics object your transforms are *added* to (technically, multiplied with) the previous ones, ending up somewhere else. You can use `ResetTransform` to start over! – Chris Jan 04 '15 at 11:52
  • Is this approach applicable if - in the original code - each clock arm was on a different surface so that I could rotate each separately in different time intervals? – Kešu Jan 04 '15 at 11:55
  • That would give you whole new issues with transparency and stuff like that, so I would advice against it. Simply call `ResetTransform` before you draw each part and your original code should work just fine. – Chris Jan 04 '15 at 11:58
  • Then how can I rotate them at all? :/ I can't seem to figure it out (sorry for being a noob) – Kešu Jan 04 '15 at 12:08
  • That's alright. Check the updated answer. – Chris Jan 04 '15 at 12:13
0

The flickering comes from the timer ticks, which is very fast, you could create a check within the timer1_tick method that makes sure that a second have actually have passed a second then update the drawing.

you could also use a timer, where you tell it when to start and how often you want it to update. By that you can follow your own computers seconds in realtime

  • Woo! So I tried that and it stopped almost entirely - only every once in a while it flickers few times but then goes back to normal. Would never have thought it was something this simple :) – Kešu Jan 04 '15 at 11:22
  • Since its a school project i won't tell you how to do it, but i can give you a place to look, but there is a interval property on the timer you are already using :) – Henrik Bøgelund Lavstsen Jan 04 '15 at 11:27
  • The flickering is only *less* with a larger timer interval. Please have a look at the solution to see how to make it *stop*. This is just a bandaid, not a cure. – nvoigt Jan 04 '15 at 11:32
  • I solved it using an integer to store the current time (seconds) in, comparing it to current time each tick to see if a second has already passed. Guess it isn't as _pro_ as it could be, but it seems to be working :) – Kešu Jan 04 '15 at 11:33