From c81e87427a0558bd754249b2666aa39a8ce03549 Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 27 Jun 2018 13:09:27 -0600 Subject: [PATCH] Removed BufferedPaintHandle and separated into BufferedAnimationPainter and BufferedPainter. --- WIndows.Forms/Drawing/BufferedPaint.cs | 219 ++++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 55 deletions(-) diff --git a/WIndows.Forms/Drawing/BufferedPaint.cs b/WIndows.Forms/Drawing/BufferedPaint.cs index c594eb77..dceed647 100644 --- a/WIndows.Forms/Drawing/BufferedPaint.cs +++ b/WIndows.Forms/Drawing/BufferedPaint.cs @@ -10,28 +10,66 @@ using static Vanara.PInvoke.UxTheme; namespace Vanara.Drawing { + /// Buffered painting helper class. public static class BufferedPaint { private static readonly Dictionary> paintAnimationInstances = new Dictionary>(); - static BufferedPaint() { BufferedPaintInit(); } - + /// A method delegate that retrieves a duration, in milliseconds, to use as the time over which buffered painting occurs. + /// The type of the state that is used to determine the transition duration. + /// The old state value. + /// The new state value. + /// A duration, in milliseconds, to use as the time over which buffered painting occurs. public delegate int GetDuration(TState oldState, TState newState); + /// A method delegate to paint a stateful image. + /// The type of the state that is used to determine the image to paint. + /// The type of the parameter that is passed into this method. + /// The graphics instance on which to paint the image. + /// The bounds within which to paint the image. + /// The current state to paint. + /// The custom data passed into this method. public delegate void PaintAction(Graphics graphics, Rectangle bounds, TState currentState, TParam data); - public static void Paint(Graphics graphics, Rectangle bounds, PaintAction paintAction, - TState currentState, TParam data) + /// Performs a buffered paint operation. + /// The type of the state that is used to determine the image to paint. + /// The type of the parameter that is passed into this method. + /// The target DC on which the buffer is painted. + /// Specifies the area of the target DC in which to draw. + /// A method delegate that performs the painting of the control at the provided state. + /// The current state to use to start drawing the animation. + /// User-defined data to pass to the callback. + public static void Paint(Graphics graphics, Rectangle bounds, PaintAction paintAction, TState currentState, TParam data) { using (var g = new SafeDCHandle(graphics)) - using (var bp = new BufferedPaintHandle(g, bounds)) + using (var bp = new BufferedPainter(g, bounds)) paintAction(bp.Graphics, bounds, currentState, data); } + /// Performs a buffered animation operation. The animation consists of a cross-fade between the contents of two buffers over a specified period of time. + /// The type of the state that is used to determine the image to paint. + /// The target DC on which the buffer is animated. + /// The window in which the animations play. + /// Specifies the area of the target DC in which to draw. + /// A method delegate that performs the painting of the control at a given state. + /// The current state to use to start drawing the animation. + /// The final state to use to finish drawing the animation. + /// A method delegate that gets the duration of the animation, in milliseconds. public static void PaintAnimation(Graphics graphics, IWin32Window ctrl, Rectangle bounds, PaintAction paintAction, TState currentState, TState newState, GetDuration getDuration) - => PaintAnimation(graphics, ctrl, bounds, paintAction, currentState, newState, getDuration, 0); + => PaintAnimation(graphics, ctrl, bounds, paintAction, currentState, newState, getDuration, 0); + /// Performs a buffered animation operation. The animation consists of a cross-fade between the contents of two buffers over a specified period of time. + /// The type of the state that is used to determine the image to paint. + /// The type of the parameter that is passed into this method. + /// The target DC on which the buffer is animated. + /// The window in which the animations play. + /// Specifies the area of the target DC in which to draw. + /// A method delegate that performs the painting of the control at a given state. + /// The current state to use to start drawing the animation. + /// The final state to use to finish drawing the animation. + /// A method delegate that gets the duration of the animation, in milliseconds. + /// User-defined data to pass to the callback. public static void PaintAnimation(Graphics graphics, IWin32Window ctrl, Rectangle bounds, PaintAction paintAction, TState currentState, TState newState, GetDuration getDuration, TParam data) { @@ -40,8 +78,7 @@ namespace Vanara.Drawing if (System.Environment.OSVersion.Version.Major >= 6) { // If this handle is running with a different state, stop the animations - Tuple val; - if (paintAnimationInstances.TryGetValue(ctrl.Handle, out val)) + if (paintAnimationInstances.TryGetValue(ctrl.Handle, out Tuple val)) { if (!Equals(val.Item1, currentState) || !Equals(val.Item2, newState)) { @@ -64,14 +101,14 @@ namespace Vanara.Drawing } var animParams = new BP_ANIMATIONPARAMS(BP_ANIMATIONSTYLE.BPAS_LINEAR, getDuration?.Invoke(currentState, newState) ?? 0); - using (var h = new BufferedPaintHandle(ctrl, hdc, bounds, animParams, BP_PAINTPARAMS.NoClip)) + using (var h = new BufferedAnimationPainter(ctrl, hdc, bounds, animParams, BP_PAINTPARAMS.NoClip)) { if (!h.IsInvalid) { if (h.SourceGraphics != null) paintAction(h.SourceGraphics, bounds, currentState, data); - if (h.Graphics != null) - paintAction(h.Graphics, bounds, newState, data); + if (h.DestinationGraphics != null) + paintAction(h.DestinationGraphics, bounds, newState, data); } else { @@ -89,66 +126,138 @@ namespace Vanara.Drawing } } - public class BufferedPaintHandle : SafeHandle + /// Use to paint a buffered animation. + /// + public class BufferedAnimationPainter : IDisposable { - private readonly bool ani; + private static readonly BufferedPaintBlock block = new BufferedPaintBlock(); - public BufferedPaintHandle(SafeDCHandle hdc, Rectangle targetRectangle, BP_PAINTPARAMS paintParams = null, - BP_BUFFERFORMAT fmt = BP_BUFFERFORMAT.BPBF_TOPDOWNDIB) : base(IntPtr.Zero, true) - { - RECT target = targetRectangle; - IntPtr phdc; - var hbp = BeginBufferedPaint(hdc, ref target, fmt, paintParams, out phdc); - if (hbp == IntPtr.Zero) throw new Win32Exception(); - if (phdc != IntPtr.Zero) Graphics = Graphics.FromHdc(phdc); - SetHandle(hbp); - } + private bool disposedValue = false; + private SafeBufferedAnimationHandle hba; - public BufferedPaintHandle(IWin32Window wnd, SafeDCHandle hdc, Rectangle targetRectangle, - BP_ANIMATIONPARAMS? animationParams = null, + /// + /// Initializes a new instance of the class and begins a buffered animation operation. The animation consists of a + /// cross-fade between the contents of two buffers over a specified period of time. + /// + /// The window in which the animations play. + /// A handle of the target DC on which the buffer is animated. + /// Specifies the area of the target DC in which to draw. + /// A structure that defines the animation operation parameters. This value can be . + /// A class that defines the paint operation parameters. This value can be . + /// The format of the buffer. + /// Buffered animation could not initialize. + public BufferedAnimationPainter(IWin32Window wnd, SafeDCHandle hdc, Rectangle targetRectangle, BP_ANIMATIONPARAMS? animationParams = null, BP_PAINTPARAMS paintParams = null, BP_BUFFERFORMAT fmt = BP_BUFFERFORMAT.BPBF_TOPDOWNDIB) - : base(IntPtr.Zero, true) { RECT rc = targetRectangle; var ap = animationParams ?? BP_ANIMATIONPARAMS.Empty; - IntPtr hdcFrom, hdcTo; - var hbp = BeginBufferedAnimation(new HandleRef(wnd, wnd.Handle), hdc, ref rc, fmt, paintParams, ref ap, out hdcFrom, - out hdcTo); - if (hbp == IntPtr.Zero) throw new Win32Exception(); + hba = BeginBufferedAnimation(new HandleRef(wnd, wnd.Handle), hdc, ref rc, fmt, paintParams, ref ap, out IntPtr hdcFrom, out IntPtr hdcTo); + if (hba.IsInvalid) throw new Win32Exception(); if (hdcFrom != IntPtr.Zero) SourceGraphics = Graphics.FromHdc(hdcFrom); - if (hdcTo != IntPtr.Zero) Graphics = Graphics.FromHdc(hdcTo); - SetHandle(hbp); - ani = true; + if (hdcTo != IntPtr.Zero) DestinationGraphics = Graphics.FromHdc(hdcTo); } - public Graphics Graphics { get; } - - public override bool IsInvalid => handle == IntPtr.Zero; - - public Graphics SourceGraphics { get; } - - protected override void Dispose(bool disposing) + /// Finalizes an instance of the class. + ~BufferedAnimationPainter() { - if (disposing) - { - SourceGraphics?.Dispose(); - Graphics?.Dispose(); - } - base.Dispose(disposing); + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); } - protected override bool ReleaseHandle() + /// Gets the destination graphics where the application should paint the final state of the animation. + public virtual Graphics DestinationGraphics { get; } + + /// Gets a value indicating whether this instance is invalid. + /// true if this instance is invalid; otherwise, false. + public virtual bool IsInvalid => hba.IsInvalid; + + /// Gets the source graphics where the application should paint the initial state of the animation. + public virtual Graphics SourceGraphics { get; } + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + void IDisposable.Dispose() { - try + Dispose(true); + GC.SuppressFinalize(this); + } + + /// Releases unmanaged and - optionally - managed resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) { - if (ani) - EndBufferedAnimation(handle, true); - else - EndBufferedPaint(handle, true); - return true; + if (disposing) + { + SourceGraphics?.Dispose(); + DestinationGraphics?.Dispose(); + hba?.Dispose(); + } + + disposedValue = true; + } + } + } + + /// Use to perform buffered painting. + /// + public class BufferedPainter : IDisposable + { + private static readonly BufferedPaintBlock block = new BufferedPaintBlock(); + + private bool disposedValue = false; + private SafeBufferedPaintHandle hbp; + + /// Initializes a new instance of the class and begins a buffered paint operation. + /// The handle of the target DC on which the buffer will be painted. + /// Specifies the area of the target DC in which to paint. + /// The paint operation parameters. This value can be . + /// The format of the buffer. + /// Buffered painting could not initialize. + public BufferedPainter(SafeDCHandle hdc, Rectangle targetRectangle, BP_PAINTPARAMS paintParams = null, + BP_BUFFERFORMAT fmt = BP_BUFFERFORMAT.BPBF_TOPDOWNDIB) + { + RECT target = targetRectangle; + hbp = BeginBufferedPaint(hdc, ref target, fmt, paintParams, out IntPtr phdc); + if (hbp.IsInvalid) throw new Win32Exception(); + if (phdc != IntPtr.Zero) Graphics = Graphics.FromHdc(phdc); + } + + /// Finalizes an instance of the class. + ~BufferedPainter() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + /// Gets the destination graphics on which all painting is done. + public virtual Graphics Graphics { get; } + + /// Gets a value indicating whether this instance is invalid. + /// true if this instance is invalid; otherwise, false. + public virtual bool IsInvalid => hbp.IsInvalid; + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + void IDisposable.Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// Releases unmanaged and - optionally - managed resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + Graphics?.Dispose(); + hbp?.Dispose(); + } + + disposedValue = true; } - catch { } - return false; } } } \ No newline at end of file