【问题标题】:C# Where to use threading while loading multiple images into multiple PictureBoxesC# 在将多个图像加载到多个 PictureBox 时在哪里使用线程
【发布时间】:2012-07-01 01:14:18
【问题描述】:

我正在完成缩略图 UserControl 的代码,其中一项要求是能够在任何时候加载多达 3,000 张图像。可以想象,如果没有线程,这将使控件在实时环境中几乎毫无用处,因为加载它们可能需要相当长的时间,并且此时用户被冻结在任何使用此控件的程序之外。

我是线程新手,我所看到和尝试的要求我更改 GenerateThumbnails 的签名并生成错误(未包含在此代码中)。

我想知道的是,当图像加载到 PictureBoxes 中时,我将如何实现线程以允许用户交互。

此控件的代码如下(我将其全部包含在内)- 更新后实现了 BackgroundWorker。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ImageViewerPlus
{

    public partial class Filmstrip : UserControl
    {

        //private BackgroundWorker bwAsyncWorker = new BackgroundWorker();

        public Filmstrip()
        {
            InitializeComponent();
            log = log + "Initialized Filmstrip Form: \r\n";
            //bwAsyncWorker.WorkerReportsProgress = true;
            //bwAsyncWorker.WorkerSupportsCancellation = true;
            //bwAsyncWorker.ProgressChanged += new ProgressChangedEventHandler(bwAsync_ProgressChanged);
            //bwAsyncWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwAsync_RunWorkerCompleted);
            //bwAsyncWorker.DoWork += new DoWorkEventHandler(bwAsync_DoWork);

            toolStripProgressBar1.Minimum = 0;
            toolStripProgressBar1.Maximum = 100;
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            SolidBrush b = new SolidBrush(this.BackColor);
            pe.Graphics.FillRectangle(b, this.ClientRectangle);
        }

        public string log;
        private string selectedImagePath;
        private List<PictureBox> pictureBoxes;
        private List<string> imagePaths;
        private int imageCount;
        private int selectedImageIndex;
        private int pictureBoxIndex;
        private int oldSelectedImageIndex;


        public SelectedImageDelegate SelectedImageCallback;

        private static int THUMBNAIL_SIZE = 100;

        private List<PictureBox> PictureBoxes
        {
            set
            {
                pictureBoxes = value;
            }
            get
            {
                return pictureBoxes;
            }
        }

        public List<string> ImagePaths
        {
            set
            {
                imagePaths = value;
            }
            get
            {
                return imagePaths;
            }
        }

        private int ImageCount
        {
            set
            {
                imageCount = value;
            }
            get
            {
                return imageCount;
            }
        }

        public int SelectedImageIndex
        {
            set
            {
                selectedImageIndex = value;
            }
            get
            {
                return selectedImageIndex;
            }
        }

        public string SelectedImagePath
        {
            set
            {
                selectedImagePath = value;
            }
            get
            {
                return selectedImagePath;
            }
        }

        private int PictureBoxIndex
        {
            set
            {
                pictureBoxIndex = value;
            }
            get
            {
                return pictureBoxIndex;
            }
        }

        public void GenerateThumbnails(List<string> images)
        {
            log = log + "GenerateThumbnails() Entered: \r\n";
            ImagePaths = images;
            ImageCount = ImagePaths.Count;
            int pictureBoxTopInterval = THUMBNAIL_SIZE + 4;
            int pictureBoxLeftInterval = THUMBNAIL_SIZE + 4;
            int thumbnailColumns = this.panelDoubleBuffered1.Width / (THUMBNAIL_SIZE);
            int remainder;
            int thumbnailRows = Math.DivRem(ImageCount, thumbnailColumns, out remainder);
            int thumbnailBreak = 0;

            if (remainder > 0)
            {
                thumbnailRows = thumbnailRows + 1;
            }

            PictureBoxes = new List<PictureBox>();
            pictureBoxIndex = 0;

            log = log + "Generating PictureBox array: \r\n";
            for (int i = 0; i < thumbnailRows; i = i + 1)
            {
                log = log + "Row " + i.ToString() + ", Column ";
                for (int j = 0; j < thumbnailColumns; j = j + 1)
                {
                    log = log + j.ToString() + ", ";
                    if (thumbnailBreak == ImageCount)
                    {
                        break;
                    }
                    else
                    {
                        PictureBox nextPictureBox = new PictureBox();

                        PictureBoxes.Add(nextPictureBox);

                        PictureBoxes[PictureBoxIndex].ImageLocation = ImagePaths[PictureBoxIndex];
                        PictureBoxes[PictureBoxIndex].Parent = panelDoubleBuffered1;
                        PictureBoxes[PictureBoxIndex].Width = THUMBNAIL_SIZE;
                        PictureBoxes[PictureBoxIndex].Height = THUMBNAIL_SIZE;
                        PictureBoxes[PictureBoxIndex].Tag = pictureBoxes[PictureBoxIndex].ImageLocation;
                        PictureBoxes[PictureBoxIndex].Top = 3 + (i * pictureBoxTopInterval);
                        PictureBoxes[PictureBoxIndex].Left = 3 + (j * pictureBoxLeftInterval);
                        PictureBoxes[PictureBoxIndex].Show();
                        PictureBoxes[PictureBoxIndex].Click += new EventHandler(OnButtonClick);
                        PictureBoxIndex = PictureBoxIndex + 1;
                        this.Refresh();
                        thumbnailBreak = thumbnailBreak + 1;
                    }
                }
                log = log + ": Initialised\r\n";
            }

            PictureBoxIndex = 0;
            log = log + "if (bwAsyncWorker.IsBusy) - ";
            if (bwAsyncWorker.IsBusy)
            {
                log = log + "bgw is busy: \r\n";
                //toolStripStatusLabel1.Text = "Cancelling...";
                //bwAsyncWorker.CancelAsync();
            }
            else
            {
                toolStripStatusLabel1.Text = "Loading...";
                log = log + "bwAsyncWorker.RunWorkerAsync(); - ";
                bwAsyncWorker.RunWorkerAsync();
            }
        }

        private void bwAsync_DoWork(object sender, DoWorkEventArgs e)
        {
            log = log + "Executed:\r\n";
            BackgroundWorker bwAsync = sender as BackgroundWorker;

            for (int i = 0; i < ImageCount; i = i + 1)
            {
                try
                {
                    log = log + "bwAsync.ReportProgress(Convert.ToInt32(i * (100.0 / ImageCount))); - ";
                    bwAsync.ReportProgress(Convert.ToInt32(i * (100.0 / ImageCount)));
                    log = log + "Executed:\r\n";

                    log = log + "ProgressBar updated to: " + (Convert.ToInt32(i * (100.0 / ImageCount))) + "\r\n";

                    log = log + "Image " + i.ToString();
                    PictureBoxes[i].Load();
                    log = log + " Has Loaded\r\n";

                    log = log + "SetImage(PictureBoxes[i]); - ";
                    SetImage(PictureBoxes[i]);
                    log = log + "Executed:\r\n";

                    if (bwAsync.CancellationPending)
                    {
                        log = log + "e.Cancel = true; - ";
                        e.Cancel = true;
                        log = log + "Executed\r\n";

                        log = log + "return;\r\n";
                        return;
                    }

                }
                catch
                {
                }
            }
        }

        private void bwAsync_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                log = log + "RunWorkerComplete: " + e.Error.Message + "\r\n";
                MessageBox.Show(e.Error.Message);
                return;
            }

            if (e.Cancelled)
            {
                log = log + "RunWorkerComplete: Cancelled...\r\n";
                toolStripStatusLabel1.Text = "Cancelled...";
            }
            else
            {
                log = log + "RunWorkerComplete: Completed...\r\n";
                toolStripStatusLabel1.Text = "Completed...";
                toolStripProgressBar1.Value = 100;
            }
        }

        private void bwAsync_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            log = log + " + "; // shows that ProgressChanged was entered.
            toolStripProgressBar1.Value = e.ProgressPercentage;
            Application.DoEvents();
        }

        public void OnButtonClick(object sender, EventArgs e)
        {
            log = log + "Selected image changed to: " + SelectedImageIndex.ToString() + ": \r\n";
            oldSelectedImageIndex = SelectedImageIndex;
            SelectedImagePath = ((PictureBox)sender).Tag.ToString();
            SelectedImageCallback(SelectedImagePath);
            SelectedImageIndex = ImagePaths.IndexOf(SelectedImagePath);
            oldSelectedImageIndex = SelectedImageIndex;
            Refresh();

            Graphics dc = this.panelDoubleBuffered1.CreateGraphics();
            Pen Redpen = new Pen(Color.Red, 2);
            dc.DrawRectangle(Redpen, PictureBoxes[SelectedImageIndex].Left - 2, PictureBoxes[SelectedImageIndex].Top - 2, 104, 104);
        }

        public static Bitmap ConvertToRGB(Bitmap original)
        {
            Bitmap newImage = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
            newImage.SetResolution(original.HorizontalResolution, original.VerticalResolution);
            using (Graphics g = Graphics.FromImage(newImage))
            {
                g.DrawImageUnscaled(original, 0, 0);
            }
            return newImage;
        }

        //Generate new image dimensions
        public Size GenerateImageDimensions(int currW, int currH, int destW, int destH)
        {
            //double to hold the final multiplier to use when scaling the image
            double multiplier = 0;

            //string for holding layout
            string layout;

            //determine if it's Portrait or Landscape
            if (currH > currW) layout = "portrait";
            else layout = "landscape";

            switch (layout.ToLower())
            {
                case "portrait":
                    //calculate multiplier on heights
                    if (destH > destW)
                    {
                        log = log + "multiplier = (double)destW / (double)currW;";
                        multiplier = (double)destW / (double)currW;
                        log = log + "Executed\r\n";
                    }

                    else
                    {
                        log = log + "multiplier = (double)destH / (double)currH;";
                        multiplier = (double)destH / (double)currH;
                        log = log + "Executed\r\n";
                    }
                    break;
                case "landscape":
                    //calculate multiplier on widths
                    if (destH > destW)
                    {
                        log = log + "multiplier = (double)destW / (double)currW;";
                        multiplier = (double)destW / (double)currW;
                        log = log + "Executed\r\n";
                    }

                    else
                    {
                        log = log + "multiplier = (double)destH / (double)currH;";
                        multiplier = (double)destH / (double)currH;
                        log = log + "Executed\r\n";
                    }
                    break;
            }

            //return the new image dimensions
            try
            {
                return new Size((int)(currW * multiplier), (int)(currH * multiplier));
            }
            catch (System.Exception e)
            {
                log = log + "Calling from GenerateImageDimensions(): " + e.Message;
                return new Size((int)(currW * multiplier), (int)(currH * multiplier));
            }

        }

        //Resize the image
        public void SetImage(PictureBox pb)
        {
            try
            {
                //create a temp image
                log = log + "Image imgtmp = pb.Image; - ";
                Image imgtmp = pb.Image;
                log = log + "Executed\r\n";
                //calculate the size of the image
                log = log + "Size imgSize = GenerateImageDimensions(imgtmp.Width, imgtmp.Height, pb.Width, pb.Height); - ";
                Size imgSize = GenerateImageDimensions(imgtmp.Width, imgtmp.Height, pb.Width, pb.Height);
                log = log + "Executed\r\n";

                //create a new Bitmap with the proper dimensions
                log = log + "Bitmap finalImg = new Bitmap(imgtmp, imgSize.Width, imgSize.Height);";
                Bitmap finalImg = new Bitmap(imgtmp, imgSize.Width, imgSize.Height);
                log = log + "Executed\r\n";

                log = log + "Bitmap img = ConvertToRGB(finalImg);";
                Bitmap img = ConvertToRGB(finalImg);
                log = log + "Executed\r\n";

                //create a new Graphics object from the image
                log = log + "Graphics gfx = Graphics.FromImage(img);";
                Graphics gfx = Graphics.FromImage(img);
                log = log + "Executed\r\n";

                //clean up the image (take care of any image loss from resizing)
                log = log + "gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;";
                gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
                log = log + "Executed\r\n";

                //empty the PictureBox
                log = log + "pb.Image = null;";
                pb.Image = null;
                log = log + "Executed\r\n";

                //center the new image
                log = log + "pb.SizeMode = PictureBoxSizeMode.CenterImage;";
                pb.SizeMode = PictureBoxSizeMode.CenterImage;
                log = log + "Executed\r\n";

                //set the new image
                log = log + "pb.Image = finalImg;";
                pb.Image = finalImg;
                log = log + "Executed\r\n";
            }
            catch (System.Exception e)
            {
                log = log + "Calling from SetImage: - " + e.Message + "\r\n";
                MessageBox.Show(e.Message);
            }
        }
    }
}

这是应用程序的图像,请注意一些缩略图没有调整大小,我完全不明白为什么。

由于防垃圾邮件,我还不能发布图片,但你可以找到一个here

如果需要,我也可以提供日志输出。

【问题讨论】:

    标签: c#


    【解决方案1】:

    使用 BackgroundWorker 线程。

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
    
        for (int i = 1; (i <= 10); i++)
        {
            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
                break;
            }
            else
            {
                // **Perform your uploads here** and report progress.
                System.Threading.Thread.Sleep(500);
                worker.ReportProgress((i * 10));
            }
        }
    }
    

    【讨论】:

    • 为此干杯,我花了一段时间来实现它,但我做到了,我现在只是有一些小问题。我不得不将图像加载到它自己的 for 循环中,与 PictureBoxes 初始化分开以避免线程冲突。我现在遇到的问题是,当图像加载时,屏幕上有很多图片框,刷新和用户输入在加载之前被冻结,所以如果有 12 个可见,则在所有 12 个都加载之前没有交互。
    • 您使用的是什么版本的框架?如果您使用的是 4.0,您可以创建任务并使用 4.0 的并行功能一次上传更多。
    • 我使用的是 VS2012 RC2 atm,所以 4.5。否则我只有 VSE,所以我会在它仍然处于活动状态时充分利用它 ;) - 我将不得不研究并行功能,您可以推荐任何资源吗?
    • 我更新了问题中的代码,它现在包括我对 BackgrondWorker 的实现以及缩略图大小调整例程。我现在遇到一些图像在它们各自的 PictureBox 可见时无法调整大小的问题。调整应用程序的大小以使缩略图控件不可见可以解决此问题,但是,如果图像仍在加载并且我展开应用程序,则那些图像加载将再次出现此问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 2012-11-15
    相关资源
    最近更新 更多