// ClockOffscreenApplication.cs Copyright (c) Kari Laitinen // http://www.naturalprogramming.com/ // 2008-02-26 File created. // 2022-12-20 Tested in Developer Command Prompt for VS 2022. /* This is a modified version of ClockApplication.cs This version shows how so-called double buffering can be used in C# GUI applications. Double buffering means that everything that needs to be drawn on the screen is first drawn on an offscreen drawing surface (an image) and then this image is drawn to the screen. Thus there is only a single drawing operation involved in each painting operation. By using an offscreen drawing surface it is possible to prevent screen flickering. */ using System ; using System.Windows.Forms ; using System.Drawing ; using System.Threading ; class ClockForm : Form { const int WINDOW_WIDTH = 600 ; const int WINDOW_HEIGHT = 435 ; Thread thread_that_runs_the_clock ; Bitmap offscreen_drawing_surface ; public ClockForm() { Text = "C# PROGRAM THAT RUNS A CLOCK" ; Size = new Size( WINDOW_WIDTH, WINDOW_HEIGHT ) ; } protected override void OnActivated( EventArgs event_data ) { if ( thread_that_runs_the_clock == null ) { ThreadStart method_to_run_the_clock = new ThreadStart( run_the_clock ) ; thread_that_runs_the_clock = new Thread( method_to_run_the_clock ); thread_that_runs_the_clock.Start() ; Console.Write( "\n Clock thread activated." ) ; } } protected override void OnDeactivate( EventArgs event_data ) { if ( thread_that_runs_the_clock != null ) { thread_that_runs_the_clock.Abort() ; thread_that_runs_the_clock = null ; Console.Write( "\n Clock thread deactivated." ) ; } } public void run_the_clock() { // The following is an infinite loop that will be terminated // when the Abort() method is called for the Thread object. while ( true ) { Thread.Sleep( 1000 ) ; // Delay of 1000 milliseconds. Invalidate() ; } } protected override void OnSizeChanged( EventArgs event_data ) { // The window size has been modified. We'll ensure that // a larger offscreen_drawing_surface is allocated in // the next paint operation. if ( offscreen_drawing_surface != null ) { offscreen_drawing_surface.Dispose() ; offscreen_drawing_surface = null ; } base.OnSizeChanged( event_data ) ; } protected override void OnPaint( PaintEventArgs paint_data ) { if( offscreen_drawing_surface == null ) { offscreen_drawing_surface = new Bitmap( ClientSize.Width, ClientSize.Height ) ; } Graphics image_graphics = Graphics.FromImage( offscreen_drawing_surface ) ; // When double buffering is used, we need to paint the // window background here. Brush background_brush = new SolidBrush( Color.PaleGreen ) ; image_graphics.FillRectangle( background_brush, 0, 0, offscreen_drawing_surface.Width, offscreen_drawing_surface.Height ) ; String[] days_of_week = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } ; String[] names_of_months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } ; DateTime time_now = DateTime.Now ; int current_year = time_now.Year ; int current_day = time_now.Day ; int month_number = time_now.Month ; int index_of_day_of_week = (int) time_now.DayOfWeek ; String current_month = names_of_months[ month_number - 1 ] ; String current_day_of_week = days_of_week[ index_of_day_of_week ] ; int current_hours = time_now.Hour ; int current_minutes = time_now.Minute ; int current_seconds = time_now.Second ; Brush black_brush = new SolidBrush( Color.Black ) ; Pen black_pen = new Pen( Color.Black ) ; image_graphics.DrawString( "" + current_day_of_week + ", " + current_month + " " + current_day + ", " + current_year, Font, black_brush, 20, 20 ) ; image_graphics.DrawString( current_hours + ":" + current_minutes.ToString("D2") + ":" + current_seconds.ToString("D2"), Font, black_brush, 20, 40 ) ; int clock_center_point_x = 300 ; int clock_center_point_y = 200 ; int[] minute_hand_end_points_x = { 0, 11, 21, 31, 41, 50, 59, 67, 74, 81, 87, 91, 95, 97, 99, 100, 99, 97, 95, 91, 87, 81, 74, 67, 59, 50, 41, 31, 21, 11, 0, -11, -21, -31, -41, -50, -59, -67, -74, -81, -87, -91, -95, -97, -99, -100, -99, -97, -95, -91, -87, -81, -74, -67, -59, -50, -41, -31, -21, -11 } ; int[] minute_hand_end_points_y = { -100, -99, -97, -95, -91, -87, -81, -74, -67, -59, -50, -41, -31, -21, -11, 0, 11, 21, 31, 41, 50, 59, 67, 74, 81, 87, 91, 95, 97, 99, 100, 99, 97, 95, 91, 87, 81, 74, 67, 59, 50, 41, 31, 21, 11, 0, -11, -21, -31, -41, -50, -59, -67, -74, -81, -87, -91, -95, -97, -99 } ; int[] hour_hand_end_points_x = { 0, 7, 13, 19, 24, 30, 35, 40, 44, 48, 52, 55, 57, 58, 59, 60, 59, 58, 57, 55, 52, 48, 44, 40, 35, 30, 24, 19, 13, 7, 0, -7, -13, -19, -24, -30, -35, -40, -44, -48, -52, -55, -57, -58, -59, -60, -59, -58, -57, -55, -52, -48, -44, -40, -35, -30, -24, -19, -13, -7 } ; int[] hour_hand_end_points_y = { -60, -59, -58, -57, -55, -52, -48, -44, -40, -35, -30, -24, -19, -13, -7, 0, 7, 13, 19, 24, 30, 35, 40, 44, 48, 52, 55, 57, 58, 59, 60, 59, 58, 57, 55, 52, 48, 44, 40, 35, 30, 24, 19, 13, 7, 0, -7, -13, -19, -24, -30, -35, -40, -44, -48, -52, -55, -57, -58, -59 } ; // Let's print an 8-point dot in the center of the clock. image_graphics.FillEllipse( black_brush, clock_center_point_x - 4, clock_center_point_y - 4, 8, 8 ) ; // The following loop prints dots on the clock circle. int minute_index = 0 ; while ( minute_index < 60 ) { image_graphics.FillEllipse( black_brush, clock_center_point_x + minute_hand_end_points_x[ minute_index ] - 2, clock_center_point_y + minute_hand_end_points_y[ minute_index ] - 2, 4, 4 ) ; minute_index = minute_index + 5 ; } // A clock goes through its 12-hour scale twice every day. int hour_index ; if ( current_hours >= 12 ) { hour_index = current_hours - 12 ; } else { hour_index = current_hours ; } // Remember that we have 60 minutes in every hour, // but not 60 hours in a day. hour_index = hour_index * 5 + current_minutes / 12 ; // Let's draw the hour hand of the clock. Pen wider_black_pen = new Pen( Color.Black, 3 ) ; image_graphics.DrawLine( black_pen, clock_center_point_x, clock_center_point_y, clock_center_point_x + hour_hand_end_points_x[ hour_index ], clock_center_point_y + hour_hand_end_points_y[ hour_index ] ) ; // The minute and second hands are longer than // the hour hand. Therefore we use different // coordinates to print them. image_graphics.DrawLine( black_pen, clock_center_point_x, clock_center_point_y, clock_center_point_x + minute_hand_end_points_x[ current_minutes ], clock_center_point_y + minute_hand_end_points_y[ current_minutes ] ) ; image_graphics.DrawLine( black_pen, clock_center_point_x, clock_center_point_y, clock_center_point_x + minute_hand_end_points_x[ current_seconds ], clock_center_point_y + minute_hand_end_points_y[ current_seconds ] ) ; image_graphics.Dispose() ; // Everything is now drawn to the image. Next we'll draw the // image to the screen. Graphics graphics = paint_data.Graphics ; graphics.DrawImageUnscaled( offscreen_drawing_surface, 0, 0 ) ; } protected override void OnPaintBackground( PaintEventArgs paint_data ) { // Don't allow the background to be painted } } public class ClockOffscreenApplication { static void Main() { Application.Run( new ClockForm() ) ; } } /* NOTE: You can easily improve the clock if you draw the minute and hour hands with a wider pen, such as Pen wider_black_pen = new Pen( Color.Black, 3 ) ; */