While learning data binding for Windows.Forms, I noticed that changes made to data in the DataGridView weren't written back into the local SQL Server database file when the form was closing, even after calling TableAdapter.Update(). Turns out that Visual C# Express 2008 (and VS 2008 in general) copies your local data file each time you build your project to the output folder. The original version of the database file is only modified if you change the schema or edit data using VS. This also means that if you make changes to the data while programming, then change the database schema in VS, the changes you have made in the database file in the output folder would be overwritten.
Nuts and bolts about programming applications, databases and spreadsheets. Note: Comments are moderated to filter comment spam. Mobile version
2009-11-17
2008-11-08
Simple Clock Custom Control using Swing and Windows.Forms
This article describes how to create a simple analogue clock using Java Swing and .Net Windows.Forms, and it will cover creating a custom control, simple 2D drawing, updating the display regularly with a timer. In addition, the control can display the time in a user-selectable time zone and is resizable.
To give you an idea of the goal, below are images of the Swing and Windows.Forms control, respectively, in a test application:


Define Custom Control
The custom control has to draw a clock face and hands. We specify the height and width of the clock face and use a high-quality drawing mode to smooth out the lines and avoid jaggies. By default, the control will display the time in the current time zone.
Java Swing
Sub-class JComponent
and override the paintComponent()
method:
public class ClockPanel extends JPanel { private double cx, cy, diameter, radius; private final double RADIAN = 180.0 / Math.PI; private TimeZone timeZone = null; public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); paintFace(g2); paintHands(g2); } }
Windows.Forms
Sub-class Control
and override OnPaint()
method:
public partial class ClockControl : Control { private double cx, cy, diameter, radius; private const double RADIAN = 180.0 / Math.PI; private TimeZoneInfo tzi = TimeZoneInfo.Local; protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; PaintFace(g); PaintHands(g); } }
2D Drawing
The clock is drawn in two steps: first a circular clock face is drawn, then the hands are drawn on top of the face. The face is circular, so the minimum of the height and width of the drawable area is used.
Clock Face
To drawing the clock face's numbers, we convert each number into string, then an image, compute that image's mid-point and place it numbers using that mid-point. If we don't use the mid-point, then each number will be placed too high and on the right of the tip of the clock hands.
Java Swing
private void paintFace(Graphics2D g2) { g2.setPaint(Color.GRAY); g2.fill(new Ellipse2D.Double(0, 0, diameter, diameter)); g2.setPaint(Color.LIGHT_GRAY); g2.fill(new Ellipse2D.Double(20, 20, diameter - 40, diameter - 40)); g2.setPaint(Color.BLACK); for (int i = 1; i <= 12; i++) { TextLayout tl = new TextLayout(Integer.toString(i), getFont(), g2.getFontRenderContext()); Rectangle2D bb = tl.getBounds(); double angle = (i * 30 - 90) / RADIAN; double x = (radius - 10) * Math.cos(angle) - bb.getCenterX() / 2; double y = (radius - 10) * Math.sin(angle) - bb.getCenterY() / 2; tl.draw(g2, (float)(cx + x), (float)(cy + y)); } }
Windows.Forms
private void PaintFace(Graphics g) { SolidBrush brush = new SolidBrush(Color.DarkGray); g.FillEllipse(brush, 0, 0, (float)diameter, (float)diameter); brush.Color = Color.LightGray; g.FillEllipse(brush, 20, 20, (float)(diameter - 40), (float)(diameter - 40)); brush.Color = Color.Black; for (int i = 1; i <= 12; i++) { string num = Convert.ToString(i); Size size = TextRenderer.MeasureText(g, num, Font, new Size(int.MaxValue, int.MaxValue), TextFormatFlags.NoPadding); double angle = (i * 30 - 90) / RADIAN; double x = (radius - 10) * Math.Cos(angle) - size.Width/2; double y = (radius - 10) * Math.Sin(angle) - size.Height/2; g.DrawString(num, Font, brush, (float)(cx+x), (float)(cy+y)); } brush.Dispose(); }
Calculate Hand Positions
For each tick, we calculate the position of the hour, minute and second hands based on the current time and time zone. The hour hand is moved slightly forward each minute, and the minute hand is moved slightly forward each second, so that they don't jump at the start of the next hour and minute, respectively.
Java Swing
private void paintHands(Graphics2D g2) { Calendar now = Calendar.getInstance(timeZone); int hour = now.get(Calendar.HOUR_OF_DAY); int minute = now.get(Calendar.MINUTE); int second = now.get(Calendar.SECOND); double angle = 0.0; angle = (hour % 12 * 30 + minute / 2) / RADIAN; paintHand(g2, 0.4, angle, 6, Color.YELLOW); angle = (minute * 6 + second / 10) / RADIAN; paintHand(g2, 0.6, angle, 4, Color.BLUE); angle = second * 6 / RADIAN; paintHand(g2, 0.8, angle, 2, Color.RED); }
Windows.Forms
private void PaintHands(Graphics g) { DateTime dt = DateTime.UtcNow + tzi.GetUtcOffset(DateTimeOffset.UtcNow); double angle = 0.0; angle = (dt.Hour % 12 * 30 + dt.Minute / 2) / RADIAN; PaintHand(g, 0.4, angle, 6f, Color.Yellow); angle = (dt.Minute * 6 + dt.Second / 10) / RADIAN; PaintHand(g, 0.6, angle, 4f, Color.Blue); angle = dt.Second * 6 / RADIAN; PaintHand(g, 0.8, angle, 2f, Color.Red); }
Draw Hands
Now, we draw each clock hand.
Java Swing
private void paintHand(Graphics2D g2, double proportion, double angle, float width, Color color) { double x = radius * proportion * Math.sin(angle); double y = -radius * proportion * Math.cos(angle); g2.setPaint(color); g2.setStroke(new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g2.draw(new Line2D.Double(cx, cy, cx + x, cy + y)); }
Windows.Forms
private void PaintHand(Graphics g, double proportion, double angle, float width, Color color) { double x = radius * proportion * Math.Sin(angle); double y = -radius * proportion * Math.Cos(angle); Pen pen = new Pen(color, width); pen.EndCap = System.Drawing.Drawing2D.LineCap.Round; g.DrawLine(pen, (float)cx, (float)cy, (float)(cx + x), (float)(cy + y)); pen.Dispose(); }
Timer
The clock control updates itself every second using a timer. We set up the timer in the control's constructor.
Java Swing
Each 1000 ms, the timer will add a repaint request to the Swing event queue, which eventually results in calling the object's paintComponent()
method.
public ClockPanel() { super(); ... setTimeZone(TimeZone.getDefault()); Timer t = new Timer(1000, new ActionListener() { public void actionPerformed(ActionEvent e) { repaint(); } }); t.start(); }
Windows.Forms
Each 1000 ms, the timer will call the timer_Tick()
function, which in turn invalidates the clock control so that Windows.Forms will generate a OnPaint()
event (that's how I think it works).
public ClockControl() { CalculateSize(); // Control's size is available at this point. Timer timer = new System.Windows.Forms.Timer(); timer.Enabled = true; timer.Interval = 1000; timer.Tick += new EventHandler(timer_Tick); } void timer_Tick(object sender, EventArgs e) { Invalidate(); }
Time Zones
For interest, the clock control displays the time is a specified time zone, so we provide methods for an external caller to get and set the control's time zone.
Java Swing
public TimeZone getTimeZone() { return timeZone; } public void setTimeZone(TimeZone tz) { timeZone = tz; }
Windows.Forms
public TimeZoneInfo TZI { get { return tzi; } set { tzi = value; } }
Test Code
To test the clock control, create a test application.
NetBeans
- Using NetBeans, create a new application sample form called SimpleClockView.
- Drag the clock's source code icon from the Projects window into SimpleClockView's Design pane.
- Drag a
JComboBox
, called timeZoneList into the Design pane. - Add the following action handler into timeZoneList to change the clock's time zone when the user selects a new time zone:
private void timeZoneListActionPerformed(java.awt.event.ActionEvent evt) { // TODO add your handling code here: String tzID = (String) this.timeZoneList.getSelectedItem(); clockPanel1.setTimeZone(TimeZone.getTimeZone(tzID)); }
- Initialize timeZoneList with a list of time zones in SimpleClockView's constructor:
public class SimpleClockView extends FrameView { public SimpleClockView(SingleFrameApplication app) { super(app); initComponents(); String[] sortedTzID = TimeZone.getAvailableIDs(); Arrays.sort(sortedTzID); timeZoneList.setModel(new javax.swing.DefaultComboBoxModel(sortedTzID)); ...
SharpDevelop
- Using SharpDevelop, create a new Windows Applications Form called Form1.
- Drag your new clock control from the Toolbox into the Form1's Designer pane.
- Drag a
ListBox
, called timeZoneList into the Design pane. - Bind timeZoneList's SelectedIndexChange event to the timeZoneList_SelectedIndexChange() function to change the clock's time zone when the user selects a new time zone:
private void timeZoneList_SelectedIndexChanged(object sender, EventArgs e) { String tzId = this.timeZoneList.SelectedValue as String; if (tzId != null) this.clockControl1.TZI = TimeZoneInfo.FindSystemTimeZoneById(tzId); }
- Initialize timeZoneList with a list of time zones in Form1's constructor:
namespace ClockNS { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.timeZoneList.DataSource = TimeZoneInfo.GetSystemTimeZones(); this.timeZoneList.DisplayMember = "DisplayName"; this.timeZoneList.SelectedValue = "Id"; } ... } }
Handling Resizing
A nice additional feature is for the clock control to resize itself when the test application's is resized.
Java Swing
Add a componentResized handler to recalculate the component's size and request Swing to repaint the component. Your component may have a small inset within its boundary, so you should take that into account when calculating the clock's size.
public ClockPanel() { super(); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { calculateSize(); repaint(); } }); ... } private void calculateSize() { Insets insets = getInsets(); int width = getWidth() - insets.left - insets.right; int height = getHeight() - insets.top - insets.bottom; diameter = Math.min(width, height); cx = cy = radius = diameter / 2; }
Windows.Forms
Override the base class' OnResize()
function to recalculate the clock's size and redraw the control.
protected override void OnResize(EventArgs e) { base.OnResize(e); CalculateSize(); Invalidate(); } private void CalculateSize() { diameter = Math.Min(this.Width, this.Height) - 2; cx = cy = radius = diameter / 2; }
Conclusion
I've described how to create a custom control in Java Swing + NetBeans and .Net Windows.Forms + SharpDevelop using (nearly) the same procedure and structure. I experiment with both environments because a feature in one motivates me to find an equivalent feature in the other and to synthesize a common (and hopefully better) solution.
See Also
- Solving Common Painting Problems explains that a Java Swing JPanel has an opaque background by default, while a JComponent does not.
- Control Type Recommendations discusses whether to sub-class Control or UserControl in Windows.Forms.
2008-08-27
What's the time?
14 quick ways to find the current time on your computer.
Cmd.exe has two built-in commands for the date and time. You have to add the /t option when calling these commands otherwise you are prompted to set the system time:
> date /t Wed 27/08/2008 > time /t 07:42 PM
GnuWin's date command prints the date, time and time zone:
> date Wed Aug 27 19:43:23 AUS Eastern Standard Time 2008
You can use the POSIX module in Perl to get the current date and time:
> perl -e "use POSIX; print asctime(localtime());" Wed Aug 27 19:44:21 2008
Python has a time module similar to Perl's:
> python -c "import time; print time.asctime()" Wed Aug 27 19:48:07 2008
PHP's time and date functions return an array, which you can dump using the print_r()
function:
> php -r "print_r(get_date());" Array ( [seconds] => 49 [minutes] => 34 [hours] => 14 [mday] => 30 [wday] => 6 [mon] => 8 [year] => 2008 [yday] => 242 [weekday] => Saturday [month] => August [0] => 1220070889 )
Ruby has a Time class:
> ruby -e "print Time.now" Wed Aug 27 19:45:32 +1000 2008
PowerShell has a get-date cmdlet:
> get-date Wednesday, 27 August 2008 7:50:13 PM
Or use the .Net System.DateTime.Now property in PowerShell:
> [System.DateTime]::Now Thursday, 28 August 2008 9:53:21 AM
Firefox can tell you the time using the Javascript Date() object. Enter the following statement in your browser's address bar:
javascript:Date() Wed Aug 27 2008 20:11:27 GMT+1000 (AUS Eastern Standard Time)
MSIE6 has a similar object but the output is different from Firefox's:
javascript:Date() Thu Aug 28 10:06:59 2008
Groovy (and Java) has a java.util.Date object which defaults to the current time:
new java.util.Date() Result: Thu Aug 28 09:58:45 EST 2008
2008-01-08
Four Function Calculator using C# and Windows Forms
Wrote an article about converting an simple Java program to C# and Windows Forms and discussed development features in Visual Studio C# 2008 Express.
This blog entry is used for discussion, if any.