サムネイル UserControl のコードを仕上げています。その要件の 1 つは、一度に最大 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);
}
}
}
}
これがアプリケーションの画像です。一部のサムネイルのサイズが変更されていないことに注意してください。その理由はまったくわかりません。
スパム防止のため、まだ写真を投稿できませんが、ここで見つけることができます。
必要に応じて、ログ出力も提供できます。