C# 避免一次又一次地创建PictureBox

C# 避免一次又一次地创建PictureBox,c#,winforms,memory,picturebox,C#,Winforms,Memory,Picturebox,我有以下问题。我的意图是在Windows窗体中从右向左移动多个图像。下面的代码工作得很好。困扰我的是,每次创建PictureBox对象时,这个过程都会消耗大量内存。每个图像从右到左不间断地跟随前一个图像。这些图像显示天空从一侧移动到另一侧。它应该看起来像一架飞机在空中飞行 如何避免使用过多内存?我能用PaintEvent和GDI做些什么吗?我对图形编程不是很熟悉 using System; using System.Drawing; using System.Windows.Forms; usi

我有以下问题。我的意图是在Windows窗体中从右向左移动多个图像。下面的代码工作得很好。困扰我的是,每次创建PictureBox对象时,这个过程都会消耗大量内存。每个图像从右到左不间断地跟随前一个图像。这些图像显示天空从一侧移动到另一侧。它应该看起来像一架飞机在空中飞行

如何避免使用过多内存?我能用PaintEvent和GDI做些什么吗?我对图形编程不是很熟悉

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;
            }   

        }

    }
使用系统;
使用系统图;
使用System.Windows.Forms;
使用System.Collections.Generic;
公共课背景:表格
{
私人图片盒天空,天空移动;
私人定时器移动天空;
专用int位置X=0,位置Y=0,宽度,高度;
私人列表连续图片;
公共背景(整数宽度、整数高度)
{
这个。宽度=宽度;
高度=高度;
//创建Windows窗体
this.Text=“飞行者”;
此尺寸=新尺寸(宽度、高度);
this.StartPosition=FormStartPosition.CenterScreen;
this.FormBorderStyle=FormBorderStyle.FixedSingle;
this.ebox=false;
//通过计时器,天空的运动成为可能。
moveSky=新计时器();
Tick+=新事件处理程序(moveSky\u XDirection\u Tick);
时间间隔=10;
moveSky.Start();
连续图片=新列表();
天空窗口();
this.ShowDialog();
}
//天空的运动方向
私有void moveSky\u XDirection\u Tick(对象发送方,事件参数e)
{
对于(int i=0;i<100;i++)
{
skyMove=连续图片[i];
skyMove.Location=新点(skyMove.Location.X-6,skyMove.Location.Y);
}
}
私有窗口()
{
对于(int i=0;i<100;i++)
{
//将天空载入窗口
天空=新图片框();
sky.Image=新位图(“C:/MyPath/sky.jpg”);
天空.立根(位置X、位置Y、宽度、高度);
this.Controls.Add(天空);
连续图片。添加(天空);
位置X+=宽度;
}   
}
}

您似乎正在加载相同的
位图100次。这就是你的记忆问题,而不是100
PictureBox
s。
PictureBox
应该具有较低的内存开销,因为它们的内存消耗中不包括图像,而引用的
位图更可能消耗大量内存

<强>它很容易被固定< /强> -考虑加载位图一次,然后将它应用到所有的<代码>图片框< /代码> S.

更改:

    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;
        }   

    }

这并不是对这个问题的直接回答——我认为这主要是因为您正在创建的所有
位图
图像。您应该只创建一个,然后问题就消失了

我在这里建议的是另一种编码方式,它极大地减少了代码

我所有的代码在
this.MaximizeBox=false一行之后直接进入你的
Background
构造函数。之后的一切都被移除

因此,从加载图像开始:

var image = new Bitmap(@"C:\MyPath\Sky.jpg");
接下来,根据传入的
宽度
高度
计算出需要在表单上平铺多少个图片框:

var countX = width / image.Width + 2;
var countY = height / image.Height + 2;
现在创建将填充屏幕的实际图片框:

var pictureBoxData =
(
    from x in Enumerable.Range(0, countX)
    from y in Enumerable.Range(0, countY)
    let positionX = x * image.Width
    let positionY = y * image.Height
    let pictureBox = new PictureBox()
    {
        Image = image,
        Location = new Point(positionX, positionY),
        Size = new Size(image.Width, image.Height),
    }
    select new
    {
        positionX,
        positionY,
        pictureBox,
    }
).ToList();
接下来,将它们全部添加到
控件
集合:

pictureBoxData.ForEach(pbd => this.Controls.Add(pbd.pictureBox));
最后,使用Microsoft的反应式框架(NuGet
Rx WinForms
)创建一个计时器,用于更新图片框的
左侧位置:

var subscription =
    Observable
        .Generate(
            0,
            n => true,
            n => n >= image.Width ? 0 : n + 1,
            n => n,
            n => TimeSpan.FromMilliseconds(10.0))
        .ObserveOn(this)
        .Subscribe(n =>
        {
            pictureBoxData
                .ForEach(pbd => pbd.pictureBox.Left = pbd.positionX - n);
        });
最后,在启动对话框之前,我们需要一种方法来清理上面的所有内容,以便窗体可以干净地关闭。这样做:

var disposable = new CompositeDisposable(image, subscription);
this.FormClosing += (s, e) => disposable.Dispose();
现在您可以执行
ShowDialog

this.ShowDialog();
就这样

除了nugetting
Rx WinForms
,您还需要使用
语句在代码顶部添加以下

using System.Reactive.Linq;
using System.Reactive.Disposables;
这一切对我来说都很好:


变量和名称尚未翻译成英语。尽管如此,我还是希望大家都能理解

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


/// <summary>
/// Scrolling Background - Bewegender Hintergrund
/// </summary>


public class ScrollingBackground : Form
{

    /*      this = fremde Attribute und Methoden, 
     * ohne this = eigene Attribute und Methoden
     */


    private PictureBox picBoxImage;
    private PictureBox[] listPicBoxAufeinanderfolgendeImages;
    private Timer timerBewegungImage;


    private const int constIntAnzahlImages = 2,
                      constIntInterval = 1,
                      constIntPositionY = 0;

    private int intPositionX = 0,
                intFeinheitDerBewegungen,
                intBreite,
                intHoehe;

    private string stringTitel,
                   stringBildpfad;




    // Konstruktor der Klasse Hintergrund

    /// <summary>
    /// Initialisiert eine neue Instanz der Klasse Hintergrund unter Verwendung der angegebenen Ganzzahlen und Zeichenketten.
    /// Es wird ein Windows-Fenster erstellt, welches die Möglichkeit hat, ein eingefügtes Bild als bewegenden Hintergrund darzustellen.
    /// </summary>
    /// <param name="width">Gibt die Breite des Fensters an und passt den darin befindlichen Hintergrund bzgl. der Breite automatisch an.</param>
    /// <param name="height">Gibt die Höhe des Fensters an und passt den darin befindlichen Hintergrund bzgl. der Höhe automatisch an.</param>
    /// <param name="speed">Geschwindigkeit der Bilder</param>
    /// <param name="title">Titel des Fensters</param>
    /// <param name="path">Pfad des Bildes, welches als Hintergrund dient</param>

    public ScrollingBackground(int width, int height, int speed, string title, string path) 
    {

        // Klassennutzer können Werte setzen
        intBreite = width;
        intHoehe = height;
        intFeinheitDerBewegungen = speed;
        stringTitel = title;
        stringBildpfad = path;


        // Windows-Fenster wird erschaffen
        this.Text = title;
        this.Size = new Size(this.intBreite, this.intHoehe);
        this.StartPosition = FormStartPosition.CenterScreen;
        this.FormBorderStyle = FormBorderStyle.FixedSingle;
        this.MaximizeBox = false;


        // Die Bewegung des Bildes wird durch den Timer ermöglicht.
        timerBewegungImage = new Timer();
        timerBewegungImage.Tick += new EventHandler(bewegungImage_XRichtung_Tick);
        timerBewegungImage.Interval = constIntInterval;
        timerBewegungImage.Start();


        listPicBoxAufeinanderfolgendeImages = new PictureBox[2];


        imageInWinFormLadenBeginn();


        this.ShowDialog();

    }

    // Bewegungsrichtung des Bildes
    private void bewegungImage_XRichtung_Tick(object sender, EventArgs e)
    {

        for (int i = 0; i < constIntAnzahlImages; i++)
        {

            picBoxImage = listPicBoxAufeinanderfolgendeImages[i];


            // Flackerreduzierung - Minimierung des Flackerns zwischen zwei Bildern
            this.DoubleBuffered = true;


            // Bilder werden in X-Richtung bewegt
            picBoxImage.Location = new Point(picBoxImage.Location.X - intFeinheitDerBewegungen, picBoxImage.Location.Y);


            // Zusammensetzung beider gleicher Bilder, welche den Effekt haben, die Bilder ewig fortlaufend erscheinen zu lassen
            if (listPicBoxAufeinanderfolgendeImages[1].Location.X <= 0)
            {

                imageInWinFormLadenFortsetzung();

            }

        }

    }

    // zwei PictureBoxes mit jeweils zwei gleichen Bildern werden angelegt
    private void imageInWinFormLadenBeginn()
    {

        Bitmap bitmapImage = new Bitmap(stringBildpfad);


        for (int i = 0; i < constIntAnzahlImages; i++)
        {

            // Bild wird in Fenster geladen
            picBoxImage = new PictureBox();
            picBoxImage.Image = bitmapImage;


            // Bestimmung der Position und Größe des Bildes
            picBoxImage.SetBounds(intPositionX, constIntPositionY, intBreite, intHoehe);
            this.Controls.Add(picBoxImage);


            listPicBoxAufeinanderfolgendeImages[i] = picBoxImage;


            // zwei PictureBoxes mit jeweils zwei gleichen Bildern werden nebeneinander angefügt
            intPositionX += intBreite;

        }

    }

    // Wiederholte Nutzung der PictureBoxes
    private void imageInWinFormLadenFortsetzung()
    {

        // erste PictureBox mit Image wird wieder auf ihren Anfangswert "0" gesetzt - Gewährleistung der endlos laufenden Bilder
        picBoxImage = listPicBoxAufeinanderfolgendeImages[0];
        picBoxImage.SetBounds(intPositionX = 0, constIntPositionY, intBreite, intHoehe);


        // zweite PictureBox mit Image wird wieder auf ihren Anfangswert "intBreite" gesetzt - Gewährleistung der endlos laufenden Bilder
        picBoxImage = listPicBoxAufeinanderfolgendeImages[1];
        picBoxImage.SetBounds(intPositionX = intBreite, constIntPositionY, intBreite, intHoehe);

    }

}
使用系统;
使用系统图;
使用System.Windows.Forms;
使用System.Collections.Generic;
/// 
///滚动背景-Bewegender Hintergrund
/// 
公共类ScrollingBackground:表单
{
/*this=fremde属性和方法,
*ohne this=特征属性和方法
*/
私人PictureBox picBoxImage;
私人PictureBox[]列表PicboxAufeinanderFolgendeimages;
私人定时器定时器;
私有常量int constantanzahlimages=2,
constinterval=1,
constinty=0;
私有int intPositionX=0,
intFeinheitDerBewegungen,
英特布雷特,
在何处;
私有字符串stringTitel,
stringBildpfad;
//克拉斯·希特格朗德酒店
/// 
///在克拉斯暗示的新实例中,初始值不适用于Ganzzahlen和Zeichenketten。
///这是一个窗户玻璃窗,威尔士人戴着一顶莫格利奇基特帽子,这是一个秘密。
/// 
///在青苔自动生长和生长之前,植物的生长和繁殖都会受到影响。
///在地衣进入自动控制系统之前,必须要有一个安全的通道
using System.Reactive.Linq;
using System.Reactive.Disposables;
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;


/// <summary>
/// Scrolling Background - Bewegender Hintergrund
/// </summary>


public class ScrollingBackground : Form
{

    /*      this = fremde Attribute und Methoden, 
     * ohne this = eigene Attribute und Methoden
     */


    private PictureBox picBoxImage;
    private PictureBox[] listPicBoxAufeinanderfolgendeImages;
    private Timer timerBewegungImage;


    private const int constIntAnzahlImages = 2,
                      constIntInterval = 1,
                      constIntPositionY = 0;

    private int intPositionX = 0,
                intFeinheitDerBewegungen,
                intBreite,
                intHoehe;

    private string stringTitel,
                   stringBildpfad;




    // Konstruktor der Klasse Hintergrund

    /// <summary>
    /// Initialisiert eine neue Instanz der Klasse Hintergrund unter Verwendung der angegebenen Ganzzahlen und Zeichenketten.
    /// Es wird ein Windows-Fenster erstellt, welches die Möglichkeit hat, ein eingefügtes Bild als bewegenden Hintergrund darzustellen.
    /// </summary>
    /// <param name="width">Gibt die Breite des Fensters an und passt den darin befindlichen Hintergrund bzgl. der Breite automatisch an.</param>
    /// <param name="height">Gibt die Höhe des Fensters an und passt den darin befindlichen Hintergrund bzgl. der Höhe automatisch an.</param>
    /// <param name="speed">Geschwindigkeit der Bilder</param>
    /// <param name="title">Titel des Fensters</param>
    /// <param name="path">Pfad des Bildes, welches als Hintergrund dient</param>

    public ScrollingBackground(int width, int height, int speed, string title, string path) 
    {

        // Klassennutzer können Werte setzen
        intBreite = width;
        intHoehe = height;
        intFeinheitDerBewegungen = speed;
        stringTitel = title;
        stringBildpfad = path;


        // Windows-Fenster wird erschaffen
        this.Text = title;
        this.Size = new Size(this.intBreite, this.intHoehe);
        this.StartPosition = FormStartPosition.CenterScreen;
        this.FormBorderStyle = FormBorderStyle.FixedSingle;
        this.MaximizeBox = false;


        // Die Bewegung des Bildes wird durch den Timer ermöglicht.
        timerBewegungImage = new Timer();
        timerBewegungImage.Tick += new EventHandler(bewegungImage_XRichtung_Tick);
        timerBewegungImage.Interval = constIntInterval;
        timerBewegungImage.Start();


        listPicBoxAufeinanderfolgendeImages = new PictureBox[2];


        imageInWinFormLadenBeginn();


        this.ShowDialog();

    }

    // Bewegungsrichtung des Bildes
    private void bewegungImage_XRichtung_Tick(object sender, EventArgs e)
    {

        for (int i = 0; i < constIntAnzahlImages; i++)
        {

            picBoxImage = listPicBoxAufeinanderfolgendeImages[i];


            // Flackerreduzierung - Minimierung des Flackerns zwischen zwei Bildern
            this.DoubleBuffered = true;


            // Bilder werden in X-Richtung bewegt
            picBoxImage.Location = new Point(picBoxImage.Location.X - intFeinheitDerBewegungen, picBoxImage.Location.Y);


            // Zusammensetzung beider gleicher Bilder, welche den Effekt haben, die Bilder ewig fortlaufend erscheinen zu lassen
            if (listPicBoxAufeinanderfolgendeImages[1].Location.X <= 0)
            {

                imageInWinFormLadenFortsetzung();

            }

        }

    }

    // zwei PictureBoxes mit jeweils zwei gleichen Bildern werden angelegt
    private void imageInWinFormLadenBeginn()
    {

        Bitmap bitmapImage = new Bitmap(stringBildpfad);


        for (int i = 0; i < constIntAnzahlImages; i++)
        {

            // Bild wird in Fenster geladen
            picBoxImage = new PictureBox();
            picBoxImage.Image = bitmapImage;


            // Bestimmung der Position und Größe des Bildes
            picBoxImage.SetBounds(intPositionX, constIntPositionY, intBreite, intHoehe);
            this.Controls.Add(picBoxImage);


            listPicBoxAufeinanderfolgendeImages[i] = picBoxImage;


            // zwei PictureBoxes mit jeweils zwei gleichen Bildern werden nebeneinander angefügt
            intPositionX += intBreite;

        }

    }

    // Wiederholte Nutzung der PictureBoxes
    private void imageInWinFormLadenFortsetzung()
    {

        // erste PictureBox mit Image wird wieder auf ihren Anfangswert "0" gesetzt - Gewährleistung der endlos laufenden Bilder
        picBoxImage = listPicBoxAufeinanderfolgendeImages[0];
        picBoxImage.SetBounds(intPositionX = 0, constIntPositionY, intBreite, intHoehe);


        // zweite PictureBox mit Image wird wieder auf ihren Anfangswert "intBreite" gesetzt - Gewährleistung der endlos laufenden Bilder
        picBoxImage = listPicBoxAufeinanderfolgendeImages[1];
        picBoxImage.SetBounds(intPositionX = intBreite, constIntPositionY, intBreite, intHoehe);

    }

}