避免一次又一次地创造PictureBoxes [英] Avoiding creating PictureBoxes again and again

查看:118
本文介绍了避免一次又一次地创造PictureBoxes的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下问题。我的目的是从右侧移动多个图像左侧的Windows窗体。下面的代码工作相当精细。让我困扰的是,每次创建一个图片对象时,这个过程消耗了内存巨额的事实。从到左右各图象如下先前图像不间断。图像显示的天空从一个侧面移动到另一个。它应该看起来像一个飞机飞在空中。



这怎么可能避免使用过多的内存?有什么我可以用的paintEvent和GDI吗?我不是很熟悉的图形编程

 使用系统; 
使用System.Drawing中;使用System.Windows.Forms的
;
使用System.Collections.Generic;

公共类背景:表
{

私人PictureBox的天空,skyMove;
私人定时器新概念粤语;
私人INT位X = 0,positionY = 0,宽度,高度;
私有列表<&图片框GT; consecutivePictures;


公共背景(INT宽度,高度INT)
{

this.width =宽度;
this.height =高度;

//创建Windows窗体
this.Text =THE FLIGHTER
this.Size =新尺寸(宽,高);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = FALSE;


//的天空的运动由定时器成为可能。
=新概念粤语新定时器();
moveSky.Tick + =新的EventHandler(moveSky_XDirection_Tick);
moveSky.Interval = 10;
moveSky.Start();


consecutivePictures =新的List<&图片框GT;();



skyInTheWindow();

this.ShowDialog();

}

//动作
私人无效moveSky_XDirection_Tick的天空的方向(对象发件人,EventArgs五)
{

的for(int i = 0; I< 100;我++)
{
skyMove = consecutivePictures [I]

skyMove.Location =新的点(skyMove.Location.X - 6,skyMove.Location.Y);

}


}

私人无效skyInTheWindow()
{

为( INT I = 0; I< 100;我++)
{
//天空加载到窗口
天空=新的PictureBox();
sky.Image =新位图(C:/MyPath/Sky.jpg);
sky.SetBounds(位X,positionY,宽度,高度);
this.Controls.Add(天空);

consecutivePictures.Add(天空);

位X + =宽度;
}

}

}


解决方案

您似乎加载位的 100次即可。有你的内存有问题就在那里,不可以 100 图片框秒。 A 图片框应具有低的内存开销,因为它们不包括在他们的记忆消费的形象,它是引用位图这是更可能消耗大量的内存



它很容易固定 - 考虑加载位图的一次,然后将其应用到所有的图片框取值



变化:

 私人无效skyInTheWindow()
{

的for(int i = 0; I< 100;我++)
{
//天空加载到窗口
天空=新的PictureBox();
sky.Image =新位图(C:/MyPath/Sky.jpg);
sky.SetBounds(位X,positionY,宽度,高度);
this.Controls.Add(天空);

consecutivePictures.Add(天空);

位X + =宽度;
}

}



...为:

 私人无效skyInTheWindow()
{
VAR位=新位图(C:/ mypath中/天。 JPG); //加载一次

的for(int i = 0; I< 100;我++)
{
//加载天空到窗口
天空=新图片框();
sky.Image =位图; //现在所有图片框共享相同的图像,从而减少内存
sky.SetBounds(位X,positionY,宽,高);
this.Controls.Add(天空);

consecutivePictures.Add(天空);

位X + =宽度;
}

}

您可能只是有一个 图片框延伸到背景的宽度,但随着时间的推移转变了。当然,你需要画的东西在哪里差距会出现边缘。



您可能会有点闪烁的反复图片框虽然这是我担心的事情之一但它仍然可能有助于。



还是什么我做的是创建一个用户控件并重写的OnPaint ,只是把它变成一个平局位问题,而不是有图片框■在所有。速度更快,高效,无闪烁。 :)这纯粹是可选的。



您有潜力,以消除任何闪烁太多,如果你先画到屏幕外图形位图和象素数据的结果,可见屏幕。




请问你介意给我一些代码,作为一个参考点,因为对我来说很难落实到代码?我不是在图形编程很熟悉,我真的希望相互学习。无闪烁的代码是更好的。




按照要求我已经包含下面的代码:



无闪烁的屏幕外着色用户控件



从本质上讲这是什么做的是创建一个屏幕外的位图,我们将以此为第一。它的尺寸与该用户控件相同。该控制的的OnPaint 要求 DrawOffscreen 传入图形即附着在屏幕外位图。在这里,我们循环周围只呈现瓷砖/天空可见的,而忽略其他人,以便提高性能。



一旦它的全部完成,我们扎普整个屏幕外的位图显示在一个操作中。这是为了消除:




  • 闪烁

  • 撕裂效果(通常与横向运动有关)



有一个定时已计划更新基于所有瓷砖的位置自上次更新的时间。这使得一个更现实的运动和负载下避免速度提升和缓慢起伏。瓷砖在的OnUpdate 方法移动



一些重要属性:




  • DesiredFps - 所需的帧/秒。这直接控制如何频繁的OnUpdate 方法被调用。它不直接控制如何频繁的OnPaint 被称为


  • NumberOfTiles - 我已经将它设置为你的100(云图)


  • 速度 - 以像素为单位的速度/秒的位图移动。绑 DesiredFps 。这是一个与负载无关;计算机性能独立的价值




绘画
。如果你在注意为 Timer1OnTick 代码,我调用的Invalidate(边界); 后动画的一切。这不会导致立即漆而Windows将排队的涂料操作以在稍后的时间来完成。连续挂起操作将融合成一体。这意味着,我们可以更频繁动画位置比大负荷期间画画。 动画技工是独立的油漆的。这是一个很好的事情,你不想等待发生的油漆。



您会注意到,我重写 OnPaintBackground ,基本上什么也不做。我这样做是因为我不想.NET清除背景并调用之前我的OnPaint 造成不必要的闪烁。我甚至不打扰擦除 DrawOffscreen 背景,因为我们只是去反正在它上面绘制位图。但是,如果对照大小比天空位图的高度大,如果它是一个要求,那么你可能想。性能命中是非常微不足道的,我想,当你可以说是绘制多天的位图呢。



当你构建的代码,你可以plonk的它在任何表格。控制将在工具箱中可见。下面我plonked它在我的的MainForm





控制也显示出的设计时属性的,默认其你可以看到下面。这些,似乎也为我工作的设置。试着改变他们不同的效果。





如果您的码头的控制,你的形式是可调整大小的,那么你可以调整在运行的应用程序。有用的性能测试。的WinForms不是特别的硬件加速(不像WPF),这样的我不建议窗口为过大



代码:

 #区域
$ b $使用系统b;
使用System.Collections.Generic;
使用System.ComponentModel;使用System.Diagnostics程序
;
使用System.Drawing中;
使用System.Linq的;使用System.Windows.Forms的
;使用SkyAnimation.Properties
;

#endregion

命名空间SkyAnimation
{
///<总结>
///< /总结>
公共部分类NoFlickerControl:用户控件
{
#地区的字段

私人只读表<&的RectangleF GT; _tiles =新的List<&的RectangleF GT;();
私人的DateTime _lastTick;
私人位图_offscreenBitmap;
私有图形_offscreenGraphics;
私人位图_skyBitmap;

#endregion

#区域构造

公共NoFlickerControl()
{
//默认设置第一
DesiredFps = Defaults.DesiredFps;
NumberOfTiles = Defaults.NumberOfTiles;
速度= Defaults.Speed​​;

的InitializeComponent();



如果(的designMode)
{
的回报;
}

_lastTick = DateTime.Now;


timer1.Tick + = Timer1OnTick;
timer1.Interval = 1000 / DesiredFps; //如何frequenty做我们想要重计算位置
timer1.Enabled = TRUE;

}

#endregion

#区域属性

///<总结>
///这控制我们如何重新计算经常物体位置
///< /总结>
///<&言论GT;
///这可以使FPS
的独立///< /言论>
///< VALUE>
///帧每秒。
///< /值>
[默认值(Defaults.DesiredFps)
公众诠释DesiredFps {搞定;组; }

[默认值(Defaults.NumberOfTiles)
公众诠释NumberOfTiles {搞定;组; }

///<总结>
///获取或设置天空划出。
///< /总结>
///< VALUE>
///天空。
///< /值>
[可浏览(假)]
公共位图星空{搞定;组; }

///<总结>
///获取或设置像素/秒的速度。
///< /总结>
///< VALUE>
///速度。
///< /值>
[默认值(Defaults.Speed​​)
公众持股量速度{搞定;组; }

#endregion

#地区的方法

私人无效HandleResize()
{
//控制已调整,时间重建我们的屏幕外的位图
//和图形上下文

如果(宽度== 0
||身高== 0)
{
//没有在这里做
}

_offscreenBitmap =新位图(宽度,高度);
_offscreenGraphics = Graphics.FromImage(_offscreenBitmap);
}

私人无效NoFlickerControl_Load(对象发件人,EventArgs五)
{
SkyInTheWindow();

HandleResize();
}

私人无效NoFlickerControl_Resize(对象发件人,EventArgs五)
{
HandleResize();
}

///<总结>
///来处理NoFlickerControl控制的SizeChanged事件。
///< /总结>
///< PARAM NAME =发件人>该事件的源< /参数>
///< PARAM NAME =E>的<见CREF =EventArgs的/> 。例如包含事件数据< /参数>
私人无效NoFlickerControl_SizeChanged(对象发件人,EventArgs五)
{
HandleResize();
}

///<总结>
///引发的<见CREF =E:System.Windows.Forms.Control.Paint/>事件。
///< /总结>
///< PARAM NAME =E>将<见CREF =T:System.Windows.Forms.PaintEventArgs/>包含事件数据。 < /参数>
保护覆盖无效的OnPaint(PaintEventArgs的E)
{
变种G = e.Graphics;
变种RC = e.ClipRectangle;

如果(_offscreenBitmap == NULL
|| _offscreenGraphics == NULL)
{
g.FillRectangle(Brushes.Gray,RC);
的回报;
}

DrawOffscreen(_offscreenGraphics,ClientRectangle);

g.DrawImageUnscaled(_offscreenBitmap,0,0);

}

私人无效DrawOffscreen(图形克,的RectangleF边界)
{
//我们不关心擦除的背景,因为我们'在它重新
//绘制反正
//g.FillRectangle(Brushes.White,边界);

//g.SetClip(bounds);



的foreach(在_tiles VAR瓦)
{
如果(!(bounds.Contains(瓦)|| bounds.IntersectsWith(瓦) ))
{
继续;
}

g.DrawImageUnscaled(_skyBitmap,新的点((INT)tile.Left,(INT)tile.Top));
}
}

///<总结>
///绘制控件的背景。
///< /总结>
///< PARAM NAME =E>将<见CREF =T:System.Windows.Forms.PaintEventArgs/>包含事件数据< /参数>
保护覆盖无效OnPaintBackground(PaintEventArgs的E)
{
// NOP

//我们不关心这里绘制背景,因为
// 1.我们想要做的是屏幕外
// 2。背景是图片反正
}

///<总结>
///负责更新/翻译游戏对象,不拉丝
///< /总结>
///< PARAM NAME =totalMillisecondsSinceLastUpdate方式>自上次更新<总毫秒; /参数>
///<&言论GT;
///这值得注意的是,的OnUpdate可以被称为每
多次///第二比的OnPaint。这可以。那么通常
///渲染只是走一个较长的迹象,但我们可以通过
,以弥补自上次更新
///< ///跟踪时间; /言论>
私人无效的OnUpdate(双totalMillisecondsSinceLastUpdate)
{
//请记住,我们测量速度每秒的像素,因此
// totalMillisecondsSinceLastUpdate
//这使得我们拥有流畅的动画,当
//渲染花费更长的时间某些帧为

补偿(INT I = 0; I< _tiles.Count;我++)
$ { b $ b VAR瓦= _tiles [I]
tile.Offset((浮点)( - 速度* totalMillisecondsSinceLastUpdate / 1000F),0);
_tiles [I] =瓦;
}

}

私人无效SkyInTheWindow()
{
_tiles.Clear();

//这里我从嵌入的资源
加载位图//但你可以很容易地只是做一个新的位图(C:/MyPath/Sky.jpg);

_skyBitmap = Resources.sky400x400;

VAR边界=新的Rectangle(0,0,_skyBitmap.Width,_skyBitmap.Height);

为(VAR I = 0; I< NumberOfTiles;我++)
{
//天空加载到窗口
_tiles.Add(边界);
bounds.Offset(bounds.Width,0);
}
}

私人无效Timer1OnTick(对象发件人,EventArgs EventArgs的)
{
如果(的designMode)
{
返回;
}

VAR ellapsed = DateTime.Now - _lastTick;
的OnUpdate(ellapsed.TotalMilliseconds);

_lastTick = DateTime.Now;


//队列中导致重绘
//重要的是要认识到,重绘进行排队和融合
//如果一起消息泵繁忙$ B $是很重要的b //换句话说,有可能不是一个1:的OnUpdate 1:的OnPaint
的Invalidate(界);
}

#endregion
}

公共静态类默认值
{
公共const int的DesiredFps = 30;
公共const int的NumberOfTiles = 100;
公共常量浮动速度= 300F;
}
}


I've got the following problem. My intention is to move several images from the right to the left in a Windows Form. The code below works quite fine. What bothers me is the fact that every time a PictureBox object is created, this procedure eats up enormous amounts of memory. Each image follows the previous image uninterruptedly from the right to the left. The images display a sky moving from one side to another. It should look like a plane's flying through the air.

How is it possible to avoid using too much memory? Is there something I can do with PaintEvent and GDI? I'm not very familiar with graphics programming.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Background : Form
    {

        private PictureBox sky, skyMove;
        private Timer moveSky;
        private int positionX = 0, positionY = 0, width, height;
        private List<PictureBox> consecutivePictures;


        public Background(int width, int height)
        {

            this.width = width;
            this.height = height;

            // Creating Windows Form
            this.Text = "THE FLIGHTER";
            this.Size = new Size(width, height);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;


            // The movement of the sky becomes possible by the timer.
            moveSky = new Timer();
            moveSky.Tick += new EventHandler(moveSky_XDirection_Tick);
            moveSky.Interval = 10;
            moveSky.Start();


            consecutivePictures = new List<PictureBox>();



            skyInTheWindow();

            this.ShowDialog();

        }

        // sky's direction of movement
        private void moveSky_XDirection_Tick(object sender, EventArgs e)
        {

            for (int i = 0; i < 100; i++)
            {
                skyMove = consecutivePictures[i];

                skyMove.Location = new Point(skyMove.Location.X - 6, skyMove.Location.Y);

            }


        }

        private void skyInTheWindow()
        {

            for (int i = 0; i < 100; i++)
            {
                // Loading sky into the window
                sky = new PictureBox();
                sky.Image = new Bitmap("C:/MyPath/Sky.jpg");
                sky.SetBounds(positionX, positionY, width, height);
                this.Controls.Add(sky);

                consecutivePictures.Add(sky);

                positionX += width;
            }   

        }

    }

解决方案

You seem to be loading the same bitmap 100 times. There's your memory problem right there, not the 100 PictureBoxs. A PictureBox should have a low memory overhead because they don't include the image in their memory consumption, it is the referenced Bitmap that is much more likely to consume large amounts of memory.

It's easily fixed - consider loading the bitmap once and then applying it to all your PictureBoxs.

Change:

    private void skyInTheWindow()
    {

        for (int i = 0; i < 100; i++)
        {
            // Loading sky into the window
            sky = new PictureBox();
            sky.Image = new Bitmap("C:/MyPath/Sky.jpg");
            sky.SetBounds(positionX, positionY, width, height);
            this.Controls.Add(sky);

            consecutivePictures.Add(sky);

            positionX += width;
        }   

    }

...to:

    private void skyInTheWindow()
    {
        var bitmap = new Bitmap("C:/MyPath/Sky.jpg");  // load it once

        for (int i = 0; i < 100; i++)
        {
            // Loading sky into the window
            sky = new PictureBox();
            sky.Image = bitmap; // now all picture boxes share same image, thus less memory
            sky.SetBounds(positionX, positionY, width, height);
            this.Controls.Add(sky);

            consecutivePictures.Add(sky);

            positionX += width;
        }

    }

You could just have a single PictureBox stretched to the width of the background but shift it over time. Of course you'll need to draw something on the edge where a gap would appear.

You might get a bit of flicker with repeated PictureBox though which is one of the things I'm worried about but it might still serve.

Or what I'd do is create a UserControl and override OnPaint and just turn it into a draw bitmap issue and not have PictureBoxs at all. Much faster and efficient and no flicker. :) This is purely optional

You have the potential to eliminate any flicker too if you draw first to an offscreen Graphics and Bitmap and "bitblit" the results to the visible screen.

Would you mind giving me some code which serves as a point of reference because for me it's hard to implement into code? I'm not very familiar in graphics programming and I really want to learn from one another. The code without flickering is better

As requested I have included the code below:

Flicker Free Offscreen Rendering UserControl

Essentially what this does is to create an offscreen bitmap that we will draw into first. It is the same size as the UserControl. The control's OnPaint calls DrawOffscreen passing in the Graphics that is attached to the offscreen bitmap. Here we loop around just rendering the tiles/sky that are visible and ignoring others so as to improve performance.

Once it's all done we zap the entire offscreen bitmap to the display in one operation. This serves to eliminate:

  • Flicker
  • Tearing effects (typically associated with lateral movement)

There is a Timer that is scheduled to update the positions of all the tiles based on the time since the last update. This allows for a more realistic movement and avoids speed-ups and slow-downs under load. Tiles are moved in the OnUpdate method.

Some important properties:

  • DesiredFps - desired frames/second. This directly controls how frequently the OnUpdate method is called. It does not directly control how frequently OnPaint is called

  • NumberOfTiles - I've set it to your 100 (cloud images)

  • Speed - the speed in pixels/second the bitmaps move. Tied to DesiredFps. This is a load-independent; computer-performance-independent value

Painting If you note in the code for Timer1OnTick I call Invalidate(Bounds); after animating everything. This does not cause an immediate paint rather Windows will queue a paint operation to be done at a later time. Consecutive pending operations will be fused into one. This means that we can be animating positions more frequently than painting during heavy load. Animation mechanic is independent of paint. That's a good thing, you don't want to be waiting for paints to occur.

You will note that I override OnPaintBackground and essentially do nothing. I do this because I don't want .NET to erase the background and causing unnecessary flicker prior to calling my OnPaint. I don't even bother erasing the background in DrawOffscreen because we're just going to draw bitmaps over it anyway. However if the control was resized larger than the height of the sky bitmap and if it is a requirement then you may want to. Performance-hit is pretty negligible I suppose when you are arguably drawing multiple sky-bitmaps anyway.

When you build the code, you can plonk it on any Form. The control will be visible in the Toolbox. Below I have plonked it on my MainForm.

The control also demonstrates design-time properties and defaults which you can see below. These are the settings that seem to work well for me. Try changing them for different effects.

If you dock the control and your form is resizable then you can resize the app at runtime. Useful for measuring performance. WinForms is not particularly hardware-accelerated (unlike WPF) so I wouldn't recommend the window to be too large.

Code:

#region

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using SkyAnimation.Properties;

#endregion

namespace SkyAnimation
{
    /// <summary>
    /// </summary>
    public partial class NoFlickerControl : UserControl
    {
        #region Fields

        private readonly List<RectangleF> _tiles = new List<RectangleF>();
        private DateTime _lastTick;
        private Bitmap _offscreenBitmap;
        private Graphics _offscreenGraphics;
        private Bitmap _skyBitmap;

        #endregion

        #region Constructor

        public NoFlickerControl()
        {
            // set defaults first
            DesiredFps = Defaults.DesiredFps;
            NumberOfTiles = Defaults.NumberOfTiles;
            Speed = Defaults.Speed;

            InitializeComponent();



            if (DesignMode)
            {
                return;
            }

            _lastTick = DateTime.Now;


            timer1.Tick += Timer1OnTick;
            timer1.Interval = 1000/DesiredFps; // How frequenty do we want to recalc positions
            timer1.Enabled = true;

        }

        #endregion

        #region Properties

        /// <summary>
        ///     This controls how often we recalculate object positions
        /// </summary>
        /// <remarks>
        ///     This can be independant of rendering FPS
        /// </remarks>
        /// <value>
        ///     The frames per second.
        /// </value>
        [DefaultValue(Defaults.DesiredFps)]
        public int DesiredFps { get; set; }

        [DefaultValue(Defaults.NumberOfTiles)]
        public int NumberOfTiles { get; set; }

        /// <summary>
        ///     Gets or sets the sky to draw.
        /// </summary>
        /// <value>
        ///     The sky.
        /// </value>
        [Browsable(false)]
        public Bitmap Sky { get; set; }

        /// <summary>
        ///     Gets or sets the speed in pixels/second.
        /// </summary>
        /// <value>
        ///     The speed.
        /// </value>
        [DefaultValue(Defaults.Speed)]
        public float Speed { get; set; }

        #endregion

        #region Methods

        private void HandleResize()
        {
            // the control has resized, time to recreate our offscreen bitmap
            // and graphics context

            if (Width == 0
                || Height == 0)
            {
                // nothing to do here
            }

            _offscreenBitmap = new Bitmap(Width, Height);
            _offscreenGraphics = Graphics.FromImage(_offscreenBitmap);
        }

        private void NoFlickerControl_Load(object sender, EventArgs e)
        {
            SkyInTheWindow();

            HandleResize();
        }

        private void NoFlickerControl_Resize(object sender, EventArgs e)
        {
            HandleResize();
        }

        /// <summary>
        ///     Handles the SizeChanged event of the NoFlickerControl control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        private void NoFlickerControl_SizeChanged(object sender, EventArgs e)
        {
            HandleResize();
        }

        /// <summary>
        ///     Raises the <see cref="E:System.Windows.Forms.Control.Paint" /> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs" /> that contains the event data. </param>
        protected override void OnPaint(PaintEventArgs e)
        {
            var g = e.Graphics;
            var rc = e.ClipRectangle;

            if (_offscreenBitmap == null
                || _offscreenGraphics == null)
            {
                g.FillRectangle(Brushes.Gray, rc);
                return;
            }

            DrawOffscreen(_offscreenGraphics, ClientRectangle);

            g.DrawImageUnscaled(_offscreenBitmap, 0, 0);

        }

        private void DrawOffscreen(Graphics g, RectangleF bounds)
        {
            // We don't care about erasing the background because we're
            // drawing over it anyway
            //g.FillRectangle(Brushes.White, bounds);

            //g.SetClip(bounds);



            foreach (var tile in _tiles)
            {
                if (!(bounds.Contains(tile) || bounds.IntersectsWith(tile)))
                {
                    continue;
                }

                g.DrawImageUnscaled(_skyBitmap, new Point((int) tile.Left, (int) tile.Top));
            }
        }

        /// <summary>
        ///     Paints the background of the control.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs" /> that contains the event data.</param>
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // NOP

            // We don't care painting the background here because
            // 1. we want to do it offscreen
            // 2. the background is the picture anyway
        }

        /// <summary>
        ///     Responsible for updating/translating game objects, not drawing
        /// </summary>
        /// <param name="totalMillisecondsSinceLastUpdate">The total milliseconds since last update.</param>
        /// <remarks>
        ///     It is worth noting that OnUpdate could be called more times per
        ///     second than OnPaint.  This is fine.  It's generally a sign that
        ///     rendering is just taking longer but we are able to compensate by
        ///     tracking time since last update
        /// </remarks>
        private void OnUpdate(double totalMillisecondsSinceLastUpdate)
        {
            // Remember that we measure speed in pixels per second, hence the
            // totalMillisecondsSinceLastUpdate
            // This allows us to have smooth animations and to compensate when
            // rendering takes longer for certain frames

            for (int i = 0; i < _tiles.Count; i++)
            {
                var tile = _tiles[i];
                tile.Offset((float)(-Speed * totalMillisecondsSinceLastUpdate / 1000f), 0);
                _tiles[i] = tile;
            }

        }

        private void SkyInTheWindow()
        {
            _tiles.Clear();

            // here I load the bitmap from my embedded resource
            // but you easily could just do a new Bitmap ("C:/MyPath/Sky.jpg");

            _skyBitmap = Resources.sky400x400;

            var bounds = new Rectangle(0, 0, _skyBitmap.Width, _skyBitmap.Height);

            for (var i = 0; i < NumberOfTiles; i++)
            {
                // Loading sky into the window
                _tiles.Add(bounds);
                bounds.Offset(bounds.Width, 0);
            }
        }

        private void Timer1OnTick(object sender, EventArgs eventArgs)
        {
            if (DesignMode)
            {
                return;
            }

            var ellapsed = DateTime.Now - _lastTick;
            OnUpdate(ellapsed.TotalMilliseconds);

            _lastTick = DateTime.Now;


            // queue cause a repaint
            // It's important to realise that repaints are queued and fused
            // together if the message pump gets busy
            // In other words, there may not be a 1:1 of OnUpdate : OnPaint
            Invalidate(Bounds);
        }

        #endregion
    }

    public static class Defaults
    {
        public const int DesiredFps = 30;
        public const int NumberOfTiles = 100;
        public const float Speed = 300f;
    }
}

这篇关于避免一次又一次地创造PictureBoxes的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆