Ну, я не знаю, насколько это вам поможет, но оно того стоит. Я не совсем уверен, что WebClient
можно распараллелить подобным образом, но аналогичный пример появляется на странице 187 Pro .NET Performance: Optimize Your C# Applications
, поэтому я собираюсь сделать прыжок и сказать, что вы можете использовать WebClient
из нескольких потоков. Я добавил новый метод, который вы можете просто вызвать из своего пользовательского интерфейса, хотя он делает проверку, чтобы убедиться, что его не нужно вызывать. Этот метод считывает номер страницы, а затем запускает фоновый процесс, который загружает и изменяет размеры изображений, а затем, после того, как он все это сделает, он создаст элементы управления в потоке пользовательского интерфейса.
private void GetPageAndLoadControls()
{
if (MPageDropDown.InvokeRequired)
{
MPageDropDown.Invoke((MethodInvoker)(() => GetPageAndLoadControls();));
return;
}
var page = MPageDropDown.SelectedItem != null ?
int.Parse(MPageDropDown.SelectedItem.ToString()) - 1 : 0;
Task.Run(() => LoadMoviesControls(page));
}
private void LoadMoviesControls(int page)
{
var dict = new ConcurrentDictionary<string, Bitmap>();
var movies = MovieList.Skip(page * Max).Take(Max).ToList();
using (var client = new WebClient())
{
Parallel.ForEach(movies, (m) =>
{
Stream stream = null;
Bitmap bmp = null;
try
{
if (!string.IsNullOrWhitespace(m.movie_Banner)
{
stream = client.OpenRead(s);
bmp = new Bitmap(stream);
// Note: I am guessing on a key here, that maybe there is a title
// use whatever key is going to be best for your class
dict.TryAdd(m.movie_Title, new Bitmap(bmp, 139, 208));
}
else dict.TryAdd(m.movie_Title, Properties.Resources.no_image_available);
}
finally
{
bmp?.Dispose();
stream?.Dispose();
}
});
}
// Here we have to invoke because the controls have to be created on
// the UI thread. All of the other work has already been done in background
// threads using the thread pool.
MoviesFlowPanel.Invoke((MethodInvoker)() =>
{
foreach(var movie in movies)
{
Bitmap image = null;
dict.TryGetValue(movie.movie_Title, out image);
MoviesFlowPanel.Controls.Add(
new MovieControl(movie, image, ShowMError, this);
}
});
}
// Changed the constructor to now take an image as well, so you can pass in
// the already resized image
public MovieControl(Movies M1, Bitmap image, bool show, MainForm current)
{
InitializeComponent();
Current = current;
bunifuImageButton1.Image = image ?? Properties.Resources.no_image_available; // Sanity check
bunifuImageButton1.Tag = M1;
}
Редактировать
Что-то, что пришло мне в голову после того, как я написал это и подумал об этом, вы не опубликовали код для ClearMovies()
, но из опубликованного кода я предполагаю, что вы избавляетесь от предыдущих 180 элементов управления и создаете 180 новых. Что было бы лучше, так это использовать тот же подход, который я показал в приведенном выше коде, и затем вместо «создания» новых элементов управления вы просто обновляете существующие. Вы можете добавить метод обновления в свой пользовательский элемент управления, который просто изменяет изображение и элемент «Фильм», который хранится в элементе управления. Это позволит сэкономить на создании новых элементов управления каждый раз, что должно повысить производительность. Вам может потребоваться вызвать Invalidate
в конце метода Update
. Не уверен, и так как я не могу все это проверить, просто хотел упомянуть об этом. Кроме того, вместо использования свойства Tag
для хранения объекта Movie
, я бы просто добавил переменную-член в ваш UserControl. Это добавит безопасность типов и прояснит, что и где хранится.
public class MovieControl : Control
{
public Movies Movie { get; protected set; }
// Changed the constructor to now take an image as well, so you can
// pass in the already resized image
public MovieControl(Movies M1, Bitmap image, bool show, MainForm current)
{
InitializeComponent();
Current = current;
bunifuImageButton1.Image = image ?? Properties.Resources.no_image_available; // Sanity check
Movie = M1;
}
public void UpdateMovieAndImage(Movies movie, Image image)
{
// Only dispose if it isn't null and isn't the "No Image" image
if (bunifuImageButton1.Image != null
&& bunifuImageButton1.Image != Properties.Resources.no_image_available)
{
binifuImageButton1.Image.Dispose();
}
bunifuImageButton1.Image = image;
Movie = movie;
}
}