Skip to content

Commit 3b436f8

Browse files
committed
enable animation, add option to use gdi+
1 parent 29da8f5 commit 3b436f8

File tree

8 files changed

+480
-18
lines changed

8 files changed

+480
-18
lines changed

Source/DXControls/DXControl.cs

Lines changed: 143 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,45 @@
11
using DirectN;
2+
using System.ComponentModel;
23
using System.Runtime.InteropServices;
34

45
namespace DXControls;
56

67
public class DXControl : Control
78
{
89
private float _dpi = 96.0f;
10+
private readonly VerticalBlankTicker _ticker = new();
11+
private bool _enableAnimation = true;
12+
13+
private int _currentFps = 0;
14+
private int _lastFps = 0;
15+
private DateTime _lastFpsUpdate = DateTime.UtcNow;
16+
917

1018
protected IComObject<ID2D1Factory> Direct2DFactory;
1119
protected IComObject<IDWriteFactory> DWriteFactory;
1220
protected ID2D1HwndRenderTarget? RenderTarget;
1321
protected ID2D1DeviceContext? DeviceContext;
14-
protected DXGraphics? DGraphics;
22+
protected DXGraphics? D2Graphics;
23+
24+
/// <summary>
25+
/// Request to update frame by <see cref="OnFrame"/> event.
26+
/// </summary>
27+
protected bool RequestUpdateFrame { get; set; } = true;
28+
29+
/// <summary>
30+
/// Enable FPS measurement.
31+
/// </summary>
32+
protected bool CheckFPS { get; set; } = false;
33+
1534

35+
public event EventHandler<RenderEventArgs>? RenderDX;
36+
public event EventHandler<PaintEventArgs>? RenderGDIPlus;
37+
public event EventHandler<FrameEventArgs>? Frame;
1638

39+
40+
/// <summary>
41+
/// Gets, sets the DPI for drawing when using <see cref="DXGraphics"/>.
42+
/// </summary>
1743
public float BaseDpi
1844
{
1945
get => _dpi;
@@ -26,9 +52,48 @@ public float BaseDpi
2652
}
2753
}
2854

55+
56+
/// <summary>
57+
/// Gets, sets the DPI for text drawing when using <see cref="DXGraphics"/>.
58+
/// </summary>
2959
public float TextDpi { get; set; } = 96.0f;
3060

3161

62+
/// <summary>
63+
/// Gets FPS info when the <see cref="CheckFPS"/> is set to <c>true</c>.
64+
/// </summary>
65+
public int FPS => _lastFps;
66+
67+
68+
/// <summary>
69+
/// Enables animation support for the control.
70+
/// </summary>
71+
[Category("Animation")]
72+
[DefaultValue(true)]
73+
public bool EnableAnimation
74+
{
75+
get => _enableAnimation;
76+
set
77+
{
78+
_enableAnimation = value;
79+
80+
if (!_enableAnimation)
81+
{
82+
if (_ticker.IsRunning) _ticker.Stop(1000);
83+
}
84+
else
85+
{
86+
if (!_ticker.IsRunning)
87+
{
88+
_ticker.ResetTicks();
89+
_ticker.Start();
90+
}
91+
}
92+
}
93+
}
94+
95+
96+
3297
public DXControl()
3398
{
3499
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.SupportsTransparentBackColor, true);
@@ -47,10 +112,14 @@ protected override void CreateHandle()
47112
if (DesignMode) return;
48113

49114
DoubleBuffered = false;
50-
51115
CreateGraphicsResources();
116+
117+
118+
_ticker.Tick += Ticker_Tick;
119+
_ticker.Start();
52120
}
53121

122+
54123
protected override void WndProc(ref Message m)
55124
{
56125
const int WM_ERASEBKGND = 0x0014;
@@ -90,15 +159,17 @@ protected override void WndProc(ref Message m)
90159
}
91160
}
92161

162+
93163
protected override void Dispose(bool disposing)
94164
{
95-
base.Dispose(disposing);
165+
_ticker.Stop(1000);
166+
_ticker.Tick -= Ticker_Tick;
96167

97168
Direct2DFactory.Dispose();
98169
DWriteFactory.Dispose();
99170

100-
DGraphics?.Dispose();
101-
DGraphics = null;
171+
D2Graphics?.Dispose();
172+
D2Graphics = null;
102173

103174
if (DeviceContext != null)
104175
{
@@ -111,6 +182,10 @@ protected override void Dispose(bool disposing)
111182
Marshal.ReleaseComObject(RenderTarget);
112183
RenderTarget = null;
113184
}
185+
186+
GC.Collect();
187+
188+
base.Dispose(disposing);
114189
}
115190

116191

@@ -133,6 +208,10 @@ protected override void OnSizeChanged(EventArgs e)
133208
}
134209

135210

211+
/// <summary>
212+
/// Control background is painted in <see cref="OnPaint(PaintEventArgs)"/>.
213+
/// </summary>
214+
/// <param name="e"></param>
136215
protected override void OnPaintBackground(PaintEventArgs e)
137216
{
138217
if (DesignMode)
@@ -142,7 +221,9 @@ protected override void OnPaintBackground(PaintEventArgs e)
142221
}
143222

144223

145-
[Obsolete("Use 'OnRender' to paint the control.", true)]
224+
/// <summary>
225+
/// Use <see cref="OnRender(DXGraphics)"/> or <see cref="OnRender(Graphics)"/> to paint.
226+
/// </summary>
146227
protected override void OnPaint(PaintEventArgs e)
147228
{
148229
if (DesignMode)
@@ -158,17 +239,38 @@ protected override void OnPaint(PaintEventArgs e)
158239

159240
// make sure the
160241
CreateGraphicsResources();
161-
if (DeviceContext == null || DGraphics == null) return;
242+
if (DeviceContext == null || D2Graphics == null) return;
162243

163244

245+
// Use Direct2D graphics to draw
164246
// start drawing session
165247
var bgColor = BackColor.Equals(Color.Transparent) ? Parent.BackColor : BackColor;
166-
DGraphics.BeginDraw(new(bgColor.ToArgb()));
167-
168-
OnRender(DGraphics);
248+
D2Graphics.BeginDraw(new(bgColor.ToArgb()));
249+
OnRender(D2Graphics);
169250

170251
// end drawing session
171-
DGraphics.EndDraw();
252+
D2Graphics.EndDraw();
253+
254+
255+
// Use GDPI+ to draw
256+
e.Graphics.Clear(bgColor);
257+
OnRender(e.Graphics);
258+
259+
260+
// calculate FPS
261+
if (CheckFPS)
262+
{
263+
if (_lastFpsUpdate.Second != DateTime.UtcNow.Second)
264+
{
265+
_lastFps = _currentFps;
266+
_currentFps = 0;
267+
_lastFpsUpdate = DateTime.UtcNow;
268+
}
269+
else
270+
{
271+
_currentFps++;
272+
}
273+
}
172274
}
173275

174276

@@ -178,13 +280,31 @@ protected override void OnPaint(PaintEventArgs e)
178280
#region Virtual functions
179281

180282
/// <summary>
181-
/// Paints the control using <see cref="DXGraphics"/>.
283+
/// Paints the control using Direct2D <see cref="DXGraphics"/>.
182284
/// </summary>
183285
protected virtual void OnRender(DXGraphics g)
184286
{
185-
287+
RenderDX?.Invoke(this, new RenderEventArgs(g));
288+
}
289+
290+
291+
/// <summary>
292+
/// Paints the control using GDI+ <see cref="Graphics"/>.
293+
/// </summary>
294+
protected virtual void OnRender(Graphics g)
295+
{
296+
RenderGDIPlus?.Invoke(this, new(g, ClientRectangle));
186297
}
187298

299+
300+
/// <summary>
301+
/// Process animation logic when frame changes
302+
/// </summary>
303+
protected virtual void OnFrame(FrameEventArgs e)
304+
{
305+
Frame?.Invoke(this, e);
306+
}
307+
188308
#endregion
189309

190310

@@ -211,8 +331,17 @@ private void CreateGraphicsResources()
211331

212332

213333
DeviceContext = (ID2D1DeviceContext)RenderTarget;
334+
D2Graphics = new DXGraphics(DeviceContext, DWriteFactory);
335+
}
336+
}
337+
214338

215-
DGraphics = new(DeviceContext, DWriteFactory);
339+
private void Ticker_Tick(object? sender, VerticalBlankTickerEventArgs e)
340+
{
341+
if (EnableAnimation && RequestUpdateFrame)
342+
{
343+
OnFrame(new(e.Ticks));
344+
Invalidate();
216345
}
217346
}
218347

Source/DXControls/DXControls.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,4 @@
1111
<ProjectReference Include="..\WicNet\WicNet.csproj" />
1212
</ItemGroup>
1313

14-
<ItemGroup>
15-
<Folder Include="Graphics\" />
16-
</ItemGroup>
17-
1814
</Project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.ComponentModel;
5+
using System.Globalization;
6+
using System.Linq;
7+
using System.Numerics;
8+
using System.Reflection;
9+
using System.Runtime.InteropServices;
10+
using System.Text;
11+
12+
namespace DirectN
13+
{
14+
public static class Extensions
15+
{
16+
public static string CapitalizeFirst(this string thisString, CultureInfo culture = null)
17+
{
18+
if (string.IsNullOrEmpty(thisString))
19+
return null;
20+
21+
if (culture == null)
22+
return char.ToUpper(thisString[0]) + thisString.Substring(1);
23+
24+
return char.ToUpper(thisString[0], culture) + thisString.Substring(1);
25+
}
26+
27+
public static bool EqualsIgnoreCase(this string thisString, string text) => EqualsIgnoreCase(thisString, text, false);
28+
public static bool EqualsIgnoreCase(this string thisString, string text, bool trim)
29+
{
30+
if (trim)
31+
{
32+
thisString = thisString.Nullify();
33+
text = text.Nullify();
34+
}
35+
36+
if (thisString == null)
37+
return text == null;
38+
39+
if (text == null)
40+
return false;
41+
42+
if (thisString.Length != text.Length)
43+
return false;
44+
45+
return string.Compare(thisString, text, StringComparison.OrdinalIgnoreCase) == 0;
46+
}
47+
48+
public static string Nullify(this string text)
49+
{
50+
if (text == null)
51+
return null;
52+
53+
if (string.IsNullOrWhiteSpace(text))
54+
return null;
55+
56+
string t = text.Trim();
57+
return t.Length == 0 ? null : t;
58+
}
59+
60+
}
61+
}

0 commit comments

Comments
 (0)