Динамически отображать массив PictureBoxes - проблема производительности - PullRequest
0 голосов
/ 03 мая 2020

Я хотел бы отображать coverarts для каждого альбома библиотеки MP3, немного как Itunes (на более позднем этапе, я хотел бы щелкнуть один из этих coverarts, чтобы отобразить список песен). У меня есть форма с панелью panel1, а вот l oop, который я использую:

 int i = 0;
        int perCol = 4;
        int disBetWeen = 15;
        int width = 250;
        int height = 250;
        foreach(var alb in mp2)
        {
                myPicBox.Add(new PictureBox());
                myPicBox[i].SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
                myPicBox[i].Location = new System.Drawing.Point(disBetWeen + (disBetWeen * (i % perCol) +(width * (i % perCol))), 
                    disBetWeen + (disBetWeen * (i / perCol))+ (height * (i / perCol))); 
                myPicBox[i].Name = "pictureBox" + i;
                myPicBox[i].Size = new System.Drawing.Size(width, height);
                myPicBox[i].ImageLocation = @"C:/Users/Utilisateur/Music/label.jpg";
                panel1.Controls.Add(myPicBox[i]);
                i++;
        }

Для удобства я использую одно и то же изображение для каждой фотобоксы, но я буду использовать Coverart в конечном итоге встроен в каждый mp3-файл.

Он отлично работает с рефератом библиотеки (около 50), но у меня несколько тысяч альбомов. Я попытался, и, как и ожидалось, загрузка занимает много времени, и я не могу прокрутить потом.

Есть ли способ загрузить только то, что отображается? а затем, как оценить, что отображается с полосами прокрутки.

Спасибо

1 Ответ

1 голос
/ 03 мая 2020

Winforms действительно не подходит для такого рода вещей ... Используя стандартные элементы управления, вам, вероятно, нужно либо подготовить все поля изображений заранее и загрузить изображения по мере их появления, либо управлять заполнителем переполнения для подходящей длины, чтобы полосы прокрутки работали.

Предполагая, что Winforms - единственный вариант, я бы посоветовал вам создать собственный элемент управления с полосой прокрутки и вручную запустить событие OnPaint .

Это позволило бы вам сохранить кэш изображений в памяти для рисования текущего вида [и нескольких с обеих сторон], в то же время предоставляя вам полный контроль над их загрузкой / выгрузкой [ну, как «всего» как вы можете получить на управляемом языке - вам все еще может понадобиться настроить сборщик мусора]

Чтобы получить некоторые детали ....

Создать новый элемент управления

namespace SO61574511 {
    // Let's inherit from Panel so we can take advantage of scrolling for free
    public class ImageScroller : Panel {
        // Some numbers to allow us to calculate layout
        private const int BitmapWidth = 100;
        private const int BitmapSpacing = 10;

        // imageCache will keep the images in memory. Ideally we should unload images we're not using, but that's a problem for the reader
        private Bitmap[] imageCache;

        public ImageScroller() {
            //How many images to put in the cache? If you don't know up-front, use a list instead of an array
            imageCache = new Bitmap[100];
            //Take advantage of Winforms scrolling
            this.AutoScroll = true;
            this.AutoScrollMinSize = new Size((BitmapWidth + BitmapSpacing) * imageCache.Length, this.Height);

        }

        protected override void OnPaint(PaintEventArgs e) {
            // Let Winforms paint its bits (like the scroll bar)
            base.OnPaint(e);
            // Translate whatever _we_ paint by the position of the scrollbar
            e.Graphics.TranslateTransform(this.AutoScrollPosition.X,
                           this.AutoScrollPosition.Y);

            // Use this to decide which images are out of sight and can be unloaded
            var current_scroll_position = this.HorizontalScroll.Value;

            // Loop through the images you want to show (probably not all of them, just those close to the view area)
            for (int i = 0; i < imageCache.Length; i++) {
                e.Graphics.DrawImage(GetImage(i), new PointF(i * (BitmapSpacing + BitmapWidth), 0));
            }

        }

        //You won't need a random, just for my demo colours below
        private Random rnd = new Random();

        private Bitmap GetImage(int id) {
            // This method is responsible for getting an image.
            // If it's already in the cache, use it, otherwise load it
            if (imageCache[id] == null) {
                //Do something here to load an image into the cache
                imageCache[id] = new Bitmap(100, 100);

                // For demo purposes, I'll flood fill a random colour
                using (var gfx = Graphics.FromImage(imageCache[id])) {
                    gfx.Clear(Color.FromArgb(255, rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)));
                }
            }
            return imageCache[id];
        }

    }
}

И загрузите его в свою форму, закрепляя, чтобы заполнить экран ....

    public Form1() {
        InitializeComponent();
        this.Controls.Add(new ImageScroller {
            Dock = DockStyle.Fill
        });
    }

Вы можете увидеть это в действии здесь: https://www.youtube.com/watch?v=ftr3v6pLnqA (извините за следы мыши, я захваченный площадь за окном)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...