/* * AnimationAdapter - An adaptor that gives animation capacity to a Control * * Author: Phillip Piper * Date: 08/02/2010 6:18 PM * * Change log: * 2010-03-30 JPP - Optimize invalidating so that we don't call Invalidate * dozens of times unnecessarily * 2010-02-08 JPP - Initial version * * To do: * * Copyright (C) 2010 Phillip Piper * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using System.Reflection; using System.Drawing; using System.Drawing.Text; using System.Drawing.Drawing2D; namespace BrightIdeasSoftware { /// /// An AnimationAdapter makes the given Control able to show an Animation. /// /// /// /// To function correctly, the given control must trigger Paint events. /// That is: panels, buttons, labels, picture boxes, user controls, numeric spin controls, /// and (oddly enough) data grid view. /// /// /// /// AnimationAdapter animatedControl = new AnimationAdapter(this.userControl1); /// Animation animation = animatedControl.Animation; /// // add sprites to animation /// animation.Start(); /// public class AnimationAdapter { #region Life and death public AnimationAdapter(Control control) { this.Animation = new Animation(); this.Control = control; this.Animation.Started += new EventHandler(Animation_Started); this.Animation.Stopped += new EventHandler(Animation_Stopped); this.Animation.Redraw += new EventHandler(Animation_Redraw); this.Animation.Ticked += new EventHandler(Animation_Ticked); // Make the given control double buffered. // Use reflection to get around DoubleBuffered being protected PropertyInfo pi = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic); if (pi != null) pi.SetValue(control, true, null); // Default values this.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; this.SmoothingMode = SmoothingMode.HighQuality; } #endregion #region Public properties /// /// Gets or sets the control on which the animation will be drawn /// public Control Control { get { return control; } private set { control = value; } } private Control control; /// /// Gets or sets the control on which the animation will be drawn /// public Animation Animation { get { return animation; } private set { animation = value; } } private Animation animation; /// /// Gets or sets the smoothing mode that will be applied to the /// graphic context that is used to draw the animation /// public SmoothingMode SmoothingMode { get { return smoothingMode; } private set { smoothingMode = value; } } private SmoothingMode smoothingMode; /// /// Gets or sets the text rendering hint that will be applied to the /// graphic context that is used to draw the animation /// public TextRenderingHint TextRenderingHint { get { return textRenderingHint; } private set { textRenderingHint = value; } } private TextRenderingHint textRenderingHint; #endregion #region Event handlers protected virtual void Control_Disposed(object sender, EventArgs e) { this.Animation.Stop(); } protected virtual void Control_Paint(object sender, PaintEventArgs e) { // Lock this section so we are aren't troubled by interrupts from the ticker thread lock (myLock) { // Setup the graphics context and draw the animation Graphics g = e.Graphics; g.TextRenderingHint = this.TextRenderingHint; g.SmoothingMode = this.SmoothingMode; this.Animation.Draw(g); // Allow new invalidates on the control allowInvalidate = true; } } protected virtual void Animation_Started(object sender, StartAnimationEventArgs e) { this.SetAnimationBounds(); this.Control.Paint += new PaintEventHandler(Control_Paint); this.Control.Disposed += new EventHandler(Control_Disposed); } /// /// Give the animation its outer bounds. /// /// /// This is normally the DisplayRectangle of the underlying Control. /// protected virtual void SetAnimationBounds() { this.Animation.Bounds = this.Control.DisplayRectangle; } protected virtual void Animation_Stopped(object sender, StopAnimationEventArgs e) { this.Control.Paint -= new PaintEventHandler(Control_Paint); this.Control.Disposed -= new EventHandler(Control_Disposed); } protected virtual void Animation_Redraw(object sender, RedrawEventArgs e) { // Don't trigger multiple invalidates lock (myLock) { if (allowInvalidate) { this.Control.Invalidate(); allowInvalidate = false; } } } object myLock = new object(); protected virtual void Animation_Ticked(object sender, TickEventArgs e) { } #endregion #region Private variables private bool allowInvalidate = true; #endregion } }