代码改变世界

WM中实现Splash动画效果

2009-09-14 08:08  老羽  阅读(1218)  评论(2编辑  收藏  举报

通过一个Demo演示动画效果的Splash窗体,同时也是一个全屏的演示DEMO;动画效果通过切换不同的图片实现。类图如下:

Splash_Class

(连线可能有问题,请指正,呵呵:)

实现思路:

1.SplashForm为动画效果展示窗体,通过线程Timer定时播放,播放的具体操作由接口ISplashStrategy完成;

2.主界面通过后台线程加载操作,保证Splash不会堵塞;

代码如下:

TestForm为主窗体,调用SplashForm,显示动画效果; 

public partial class TestForm : BaseFullScreenForm
    {
        #region Fields
        private SplashScreen _splashScreen = null;

        #endregion

        public TestForm()
        {
            _splashScreen = new SplashScreen(new ImageSplashScreen());
            _splashScreen.ShowFromOwner(this, 500);

            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            AsyncLoad();
        }

        private void AsyncLoad()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncLoadInvoke));
        }

        private void AsyncLoadInvoke(object target)
        {
            //do something
            //测试加载过程
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(50);
            }
            this.Invoke(new ThreadStart(CloseSplashInvoke));
        }

        private void CloseSplashInvoke()
        {
            //_splashScreen.Close();
            _splashScreen.Close(1000);
            //演示结束
            this.Close();
        }
       
    }

 

SplashScreen为Splash控制器,封装了调用、关闭与显示效果的操作;SplashScreen注册了SplashForm的两个事件:Paint与Ticks(自定义事件,由定时器触发),并通过接口ISplashStrategy实现背景与动画效果的展现;
public class SplashScreen : IDisposable
    {
        #region Fields

        private SplashForm _splashForm;
        private readonly object _lockDraw = new object();
        private readonly object _lockSplashForm = new object();
        private ISplashStrategy _splshStategy;
        private Graphics _graphics = null;

        #endregion

        public SplashScreen(ISplashStrategy splashStategy)
        {
            _splshStategy = splashStategy;
        }

        public void Close(int delay)
        {
            Thread.Sleep(delay);
            Close();
        }

        public void Close()
        {
            if (_graphics != null)
            {
                _graphics.Dispose();
                _graphics = null;
            }
            if (_splashForm != null)
            {
                _splashForm.Dispose();
                _splashForm = null;
            }
            if (_splshStategy != null)
            {
                _splshStategy.Dispose();
                _splshStategy = null;
            }
        }

        public void ShowFromOwner(Form parentForm, int speed)
        {
            SplashForm.Initialize(speed, parentForm);
        }

        private SplashForm SplashForm
        {
            get
            {
                if (_splashForm == null)
                {
                    lock (this)
                    {
                        if (_splashForm == null)
                        {
                            _splashForm = CreateSplashForm();
                        }
                    }
                }
                return _splashForm;
            }
        }

        private SplashForm CreateSplashForm()
        {
            SplashForm result = new SplashForm();
            result.Paint += new PaintEventHandler(result_Paint);
            result.Ticks += new EventHandler(SplashForm_Ticks);
            return result;
        }

        void result_Paint(object sender, PaintEventArgs e)
        {
            if (this._splshStategy != null)
            {
                this._splshStategy.OnPaintBackground(Graphics);
            }
        }

        void SplashForm_Ticks(object sender, EventArgs e)
        {
            if (this._splshStategy != null)
            {
                this._splshStategy.OnTicks(Graphics);
            }
        }

        public Graphics Graphics
        {
            get
            {
                if (_graphics == null)
                {
                    lock (_lockDraw)
                    {
                        if (_graphics == null)
                        {
                            _graphics = this.CreateGraphics();
                        }
                    }
                }
                return _graphics;
            }
        }

        protected virtual Graphics CreateGraphics()
        {
            if (_splashForm != null && !_splashForm._isDisposed)
            {
                return _splashForm.CreateGraphics();
            }
            return null;
        }

        #region IDisposable 成员

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        #endregion
    }

SplashForm为具体的动画窗体,内置定时器,并定时抛出事件 Ticks;SplashForm继承BaseFullScreenForm,实现全屏的效果,实现全屏不是一定的,看需要,是否通过全屏展现动画效果;

internal partial class SplashForm : BaseFullScreenForm
    {
        #region Fileds

        private ThreadTimer _timer = null;
        internal bool _isDisposed = false;
        private bool _isInit = false;
        private bool _loaded = false;

        #endregion

        internal SplashForm()
        {
            
        }

        public void Initialize(int speed,Form parentForm)
        {
            CreateTimer(speed);
            //SetWindowLongPtr(this.Handle, GWL_HWNDPARENT, IntPtr.Zero);
            this.TopMost = true;
            this.Show();
            //SetForegroundWindow(this.Handle);
            _isInit = true;

            //WaitForLoading(-1);
        }

        protected bool WaitForLoading(int millisecondsTimeout)
        {
            while (!_loaded)
            {
                Application.DoEvents();
                Thread.Sleep(10);
            }
            return true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (_isInit)
            {
                Start();
                _isInit = false;
                _loaded = true;
            }
        }


        private void Start()
        {
            if (_timer != null)
            {
                _timer.Start();
            }
        }

        private void CreateTimer(int speed)
        {
            if (_timer == null)
            {
                _timer = new ThreadTimer(0, speed);
                _timer.Tick += new EventHandler(Timer_Tick);
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            try
            {
                OnTicks(e);
            }
            catch { }
        }

        [DllImport("Coredll.dll", EntryPoint = "SetWindowLong")]
        public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

        public const int GWL_EXSTYLE = -20;
        public const int GWL_HWNDPARENT = -8;
        public const int GWL_ID = -12;
        public const int GWL_STYLE = -16;
        public const int GWL_WNDPROC = -4;

        #region Events

        public event EventHandler Ticks;

        protected virtual void OnTicks(EventArgs e)
        {
            if (Ticks != null)
            {
                Ticks(this, e);
            }
        }
 

        #endregion
    }

BaseFullScreenForm,封装了实现全屏的操作,重要的是两个地方:OnActivated与OnResize方法,如果不响应这两个方法,并设置全屏,会发现开始菜单栏屏蔽不了(网上很多代码没有这个,基本用不了);

public class BaseFullScreenForm : Form
    {
        public BaseFullScreenForm()
        {
            this.FormBorderStyle = FormBorderStyle.None;
            SetFullScreen();
        }

        protected override void OnActivated(EventArgs e)
        {
            SetFullScreen();
            base.OnActivated(e);
        }

        protected override void OnResize(EventArgs e)
        {
            SetFullScreen();
            base.OnResize(e);
        }

        protected void SetFullScreen()
        {
            Rectangle rc = Screen.PrimaryScreen.Bounds;
            IntPtr hWnd = this.Handle;
            SHFullScreen(hWnd, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON);
            MoveWindow(hWnd, rc.Left, rc.Top, rc.Width, rc.Height, true);
            //SetWindowPos(this.Handle, -1, 0, 0, 0, 0, SWP_NOSIZE |
            //                                         SWP_NOMOVE |
            //                                         SWP_FRAMECHANGED |
            //                                         SWP_NOACTIVATE);
        }

        #region Mobile
        private const int SHFS_SHOWTASKBAR = 0x0001;
        private const int SHFS_HIDETASKBAR = 0x0002;
        private const int SHFS_SHOWSIPBUTTON = 0x0004;
        private const int SHFS_HIDESIPBUTTON = 0x0008;
        private const int SHFS_SHOWSTARTICON = 0x0010;
        private const int SHFS_HIDESTARTICON = 0x0020;
        [DllImport("aygshell.dll")]
        private static extern bool SHFullScreen(IntPtr hWnd, int dwState);
        [DllImport("coredll.dll")]
        public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int width, int height, bool repaint);

        [DllImport("coredll.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("coredll.dll")]
        public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter,
            int X, int Y, int cx, int cy, int Flags);

        public const int SWP_NOSIZE = 0x0001;
        public const int SWP_NOMOVE = 0x0002;
        public const int SWP_NOZORDER = 0x0004;
        public const int SWP_NOREDRAW = 0x0008;
        public const int SWP_NOACTIVATE = 0x0010;
        public const int SWP_FRAMECHANGED = 0x0020;  /* The frame changed: send WM_NCCALCSIZE */
        public const int SWP_SHOWWINDOW = 0x0040;
        public const int SWP_HIDEWINDOW = 0x0080;
        public const int SWP_NOCOPYBITS = 0x0100;
        public const int SWP_NOOWNERZORDER = 0x0200;/* Don't do owner Z ordering */
        public const int SWP_NOSENDCHANGING = 0x0400; /* Don't send WM_WINDOWPOSCHANGING */
        public const int SWP_DRAWFRAME = SWP_FRAMECHANGED;
        public const int SWP_NOREPOSITION = SWP_NOOWNERZORDER;

        private static int MenuHeight = SystemInformation.MenuHeight;

        #endregion
    }

接口ISplashStrategy,抽象具体展现Splash的操作;

public interface ISplashStrategy : IDisposable
   {
       void OnPaintBackground(Graphics g);
       void OnTicks(Graphics g);
   }

ImageSplashScreen,实现接口ISplashStrategy,实现具体显示Splash效果的操作,这里就是切换图片

public class ImageSplashScreen : ISplashStrategy
    {
        #region Fields

        private int _index = 1;
        private string[] _images;
        #endregion

        public ImageSplashScreen()
        {
            _images = CreateImages();
            if (_images == null && _images.Length == 0)
            {
                throw new ArgumentException("Splash图片不存在!");
            }
        }

        #region ISplashStategy 成员

        public void OnPaintBackground(System.Drawing.Graphics g)
        {
            g.DrawImage(ImageSplashScreen.FormFile(_images[0]), 0, 0);
        }

        public void OnTicks(System.Drawing.Graphics g)
        {
            int i = _index % _images.Length == 0 ? _images.Length - 1: _index % _images.Length;
            g.DrawImage(ImageSplashScreen.FormFile(_images[i]), 0, 0);
            _index++;
        }

        #endregion

        protected virtual string[] CreateImages()
        {
            string[] imgs = new string[3]
            {
                    Path.Combine(ApplicationEx.StartupPath,"splash1.png"),
                    Path.Combine(ApplicationEx.StartupPath,"splash2.png"),
                    Path.Combine(ApplicationEx.StartupPath,"splash3.png")
            };
            return imgs;
        }

        private static Image FormFile(string file)
        {
            Image result = null;
            if (File.Exists(file))
            {
                using (FileStream input = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    byte[] buffer = new byte[(int)input.Length];
                    input.Read(buffer, 0, buffer.Length);
                    MemoryStream m = new MemoryStream(buffer, 0, buffer.Length, false, true);
                    try
                    {
                        result = new Bitmap(m);
                    }
                    finally
                    {
                        m.Close();
                    }
                }
            }
            return result;
        }

        #region IDisposable 成员

        public void Dispose()
        {
            _images = null;
        }

        #endregion
    }

结束语

这个代码尚未考虑,如何展现进度条,如何展现文本提示,以后再发文实现。



作者:老羽Michael-Zhangyu's Blog
出处:http://michael-zhangyu.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。