Please take a look at following screen shot! It looks like 3D
application using Direct X technology, but it's GDI+ based normal
Windows.Forms application with 3D like background picture! Some of
you might be interested in 3D application. This article describes how to
create 3D "Like" 2D application.
Before you jump into this, please note "3D Modeling is really time
consuming"!! It took almost two weeks for me to create the CPU Meter
background image! If you don't have enough time, I'd strongly recommend
you to stay out of the 3D modeling!!
There are many 3D modeling software, but most of them are pretty
expensive. If you have tons of money, buy what ever you want and take a
training course. But, fortunately, there is greate free 3D modeling
software, "Blender". I used "Blender" to make this application, but
"Blender" is designed as multi-platform software, and it has very unique
GUI and operations. If you know someone who is familiar with "Blender",
please do not hesitate to ask him how to use "Blender"! It's so unusual!
Now I'm assuming that you can create following 3D image with
Blender.
When you download "3DCPUMeter.blend"
and double click, following "Blender" will come up.
Now, press F12 key to render current frame. Then, following Render
image will be created.
Press F3 key to save image as "3DCPU.png".
Please trim black background and save.
Now it's time to implement CPU Meter. There are two steps.
First, we create a CPUMeterPanel control class derived from "PictureBox"
control.
You don't want the CPU meter heavy enough to consume your CPU
power. That's why I'm making the CPUMeterPanel control class derived
from the PictureBox control. Because the PictureBox control has double
buffering capability and it's very efficient to draw graphics. Also it
can handle images easily.
1: using System;
2: using System.Drawing;
3: using System.Drawing.Drawing2D;
4: using System.ComponentModel;
5:
6: namespace Uchukamen.CPUMeter
7: {
8: /// <summary>
9: /// </summary>
10: public class CPUMeterPanel: System.Windows.Forms.PictureBox
11: {
12: private System.ComponentModel.IContainer components;
13:
14: public CPUMeterPanel()
15: {
16: //
17: //
18: InitializeComponent();
19:
20: // Initialize Brush and Image
21: handBrush = new SolidBrush(handColor);
22: LoadImage();
23: }
24:
25: /// <summary>
26: /// </summary>
27: protected override void Dispose( bool disposing )
28: {
29: if( disposing )
30: {
31: if (components != null)
32: {
33: components.Dispose();
34: }
35: }
36: base.Dispose( disposing );
37: }
38:
39: #region Windows
40: /// <summary>
41: /// </summary>
42: private void InitializeComponent()
43: {
44: this.performanceCounter1 = new System.Diagnostics.PerformanceCounter();
45: this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog();
46: ((System.ComponentModel.ISupportInitialize)(this.performanceCounter1)).BeginInit();
47: //
48: // performanceCounter1
49: //
50: this.performanceCounter1.CategoryName = "Processor";
51: this.performanceCounter1.CounterName = "% Processor Time";
52: this.performanceCounter1.InstanceName = "_Total";
53: //
54: // CPUMeterPanel
55: //
56: this.Resize += new System.EventHandler(this.CPUMeterPanel_Resize);
57: ((System.ComponentModel.ISupportInitialize)(this.performanceCounter1)).EndInit();
58:
59: }
60: #endregion
61:
62: #region Properties
63: private Color handColor = Color.White;
64: [Category( "Meter" )]
65: [Description( "Hand Color" )]
66: [DefaultValue( typeof(Color), "White" )]
67: public Color HandColor
68: {
69: get {return this.handColor;}
70: set
71: {
72: handColor = value;
73: handBrush = new SolidBrush(value);
74: Refresh();
75: }
76: }
77:
78: private double startAngle = -125.0;
79: [Category( "Meter" )]
80: [Description( "Start Angle" )]
81: [DefaultValue( typeof(double), "-125.0" )]
82: public double StartAngle
83: {
84: get {return startAngle;}
85: set
86: {
87: startAngle = value;
88: Refresh();
89: }
90: }
91:
92: private double endAngle = 125.0F;
93: [Category( "Meter" )]
94: [Description( "End Angle" )]
95: [DefaultValue( typeof(double), "125.0" )]
96: public double EndAngle
97: {
98: get {return endAngle;}
99: set
100: {
101: endAngle = value;
102: Refresh();
103: }
104: }
105:
106: private Bitmap originalBitmap = null;
107: private System.Diagnostics.PerformanceCounter performanceCounter1;
108:
109: [Category( "Meter" )]
110: [Description( "Background Bitmap" )]
111: [DefaultValue( typeof(Bitmap), "" )]
112: public Bitmap OriginalBitmap
113: {
114: get {return originalBitmap;}
115: set
116: {
117: originalBitmap = value;
118: LoadImage();
119: }
120: }
121:
122: #endregion
123:
124:
125: private const double pai = Math.PI;
126: private SolidBrush handBrush;
127: <summary>
128: /// </summary>
129: private int r; // Meter Radius
130:
131: private GraphicsPath handPath;
132: private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1;
133:
134: // Current Meter Value
135: static float meterRead = 0.0F;
136: /// <summary>
137: /// Draw Hand
138: /// </summary>
139: /// <param name="g"></param>
140: /// <param name="currentSpeed"></param>
141: public void drawHand(Graphics g, float currentSpeed)
142: {
143: double totalAngle = endAngle - startAngle;
144:
145: // Delta = Current Value - Current Meter Read
146: // Get the current meter closer to the current value with delta/10 step.
147: float delta = currentSpeed - meterRead;
148: float step = delta/10F;
149: meterRead += step;
150: GraphicsState gs = g.Save();
151: // Rotate and draw the meter hand.
152: g.RotateTransform((float)(startAngle + meterRead * totalAngle/100F));
153: if (handPath != null)
154: g.FillPath(handBrush, handPath);
155: g.Restore(gs);
156: }
157:
158: /// <summary>
159: /// Re calculate size related values
160: /// </summary>
161: public void Recalculate(int width, int height)
162: {
163: // Radius
164: r = (int)(0.6 * Math.Min(width/2, height/2));
165:
166: // Graphics Path of the meter hand
167: int handWidth = (int)(Math.Max(1.0F, r*0.02F));
168: handPath = new GraphicsPath();
169: Point [] p = new Point[5];
170: p[0] = new Point((int)(-r*0.1F), 0);
171: p[1] = new Point(0, -handWidth);
172: p[2] = new Point(r, 0);
173: p[3] = new Point(0, handWidth);
174: p[4] = new Point((int)(-r*0.1F), 0);
175: handPath.AddLines(p);
176: handPath.CloseAllFigures();
177: }
178:
179: public void LoadImage()
180: {
181: if (OriginalBitmap == null)
182: return;
183: if (Width <= 0 || Height <= 0)
184: return;
185: if (BackgroundImage != null)
186: BackgroundImage.Dispose();
187: BackgroundImage = new Bitmap(OriginalBitmap, Width, Height);
188: }
189:
190: protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
191: {
192: float val = performanceCounter1.NextValue();
193: Graphics g = e.Graphics;
194: g.TranslateTransform(Width/2, Height/2);
195: g.RotateTransform(-90.0F);
196: g.SmoothingMode = SmoothingMode.AntiAlias;
197: drawHand(g, val);
198: }
199:
200: private void CPUMeterPanel_Resize(object sender, System.EventArgs e)
201: {
202: Recalculate(Width, Height);
203: LoadImage();
204: }
205: }
206: }