Displaying each image from a directory in an Image Control - c#

I'd like to display the contents(.jpg files) from a local directory in an Image control. The images has to be replaced with a 5 sec delay.
DirectoryInfo dir = new DirectoryInfo(#"D:\somedir");
FileInfo[] files = dir.GetFiles();
foreach (var item in files)
{
imgBox.Source = (ImageSource)new ImageSourceConverter().ConvertFromString(item.FullName);
}

Load images into a memory, then user a background thread to rotate them with the defined delay. Dispatcher call is required to access the UI control from a background thread.
List<Image> images;
void GetImagesIntoAList()
{
List<Image> images = new List<Image>();
DirectoryInfo dir = new DirectoryInfo(#"D:\somedir");
FileInfo[] files = dir.GetFiles();
foreach (var item in files)
{
FileStream stream = new FileStream(item.FullName, FileMode.Open, FileAccess.Read);
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.StreamSource = stream;
src.EndInit();
i.Source = src;
images.Add(i);
}
Thread rotator = new Thread(rotate);
rotator.Start();
}
void rotate()
{
foreach(var img in images)
{
Dispatcher.BeginInvoke( () =>
{
nameOfImageControlOnAWindow.Source = img;
}
);
Thread.Sleep(5000);
}
}

Related

How can I delete an image while it is open in listview?

I am trying to athis listview that shows images in a folder
public void LoadData()
{
var imgList = Directory.GetFiles(directoryPath, "*.jpg",
SearchOption.AllDirectories);
myListView.ItemsSource = imgList;
}
The ListView ItemTemplate is an Image and a button to delete the Image
The click event of the button is handled as follows
private void DeleteImg(object sender, RoutedEventArgs e)
{
string delImg = (string)(sender as Button).DataContext;
File.Delete(delImg);
LoadData();
}
I get the following error when I try to delete an image
System.IO.IOException: 'The process cannot access the file 'xyz' because it is being used by another process.'
xyz is the file name obviously
I think it's because the image is open in ListView
Any ideas about how I can close the file before deleting it
What I have tried:
I tried
myListView.ItemsSource = null;
myListView.Items.Clear();
before delete
also:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
I found this and it works;
public void LoadData()
{
var imgList = Directory.GetFiles(directoryPath, "*.jpg", SearchOption.AllDirectories);
myListView.ItemsSource = imgList;
foreach (string f in imgList)
{
var bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(f);
imgLister.Add(bi);
bi.EndInit();
bi.Freeze();
}
}
Freeze() methode Frees up memory, and allows file to be deleted

How to fix The process cannot access the file because it is being used by another process

I wrote a small code to make a slideshow program on a different screen. Everything is running fine also the delete and copy part, unless I do it right after the first picture is shown. If I do it later the debugger steps in.
private void timer1_Tick(object sender, EventArgs e)
{
string[] images = Directory.GetFiles(#"D:\reflexscherm\root\Logo-teams2", "*.*");
counter++;
var maxcount = images.Count();
textBox1.Text = maxcount.ToString();
if (counter > maxcount - 1)
{
counter = 0;
maxcount = images.Count();
}
//pb1.Image.Dispose();
pb1.Image = Image.FromFile(images[counter]);
//Image oldImage = pb1.Image;
//pb1.Image.Dispose();
//oldImage.Dispose();
//pb1.Image = Image.FromFile(images[counter]);
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
Image oldImage = pb1.Image;
pb1.Image = Image.FromFile(#"D:\reflexscherm\root\sponsor1\x. Groot-logo-REFLEX.jpg");
pb1.Image.Dispose();
oldImage.Dispose();
string[] files = System.IO.Directory.GetFiles(sourcepath);
string[] delfiles = Directory.GetFiles(targetpath);
this.Hide();
foreach (string d in delfiles)
{
Image oldI = pb1.Image;
pb1.Image = Image.FromFile(#"D:\reflexscherm\root\sponsor1\x. Groot-logo-REFLEX.jpg");
//pb1.Image.Dispose();
oldI.Dispose();
File.Delete(d);
}
foreach (string s in files)
{
string fname = s.Substring(sourcepath.Length + 1);
File.Copy(Path.Combine(sourcepath, fname), Path.Combine(targetpath, fname), true);
this.Show();
timer1.Start();
}
What I am looking for is some help to adjust the code, so when I change files in sourcefolder then program copies the files from the sourcefolder to the targetfolder. I know how to use filewatcher. I am using a button to test the code.
Method 1: Preserve a copy and assign a Control's property
Use this method when a Bitmap object is handled in more than one place, so we need to preserve it for further elaborations/assignments and save to disc after.
Assign, store and dispose of the source Bitmap immediately, deleting the Image file:
Bitmap bitmap = null;
//---------------------------------------------
string imagePath = #"[Path of the Image]";
bitmap?.Dispose();
pictureBox1.Image?.Dispose();
using (Bitmap tempImage = new Bitmap(imagePath, true))
{
bitmap = new Bitmap(tempImage);
pictureBox1.Image = bitmap;
}
File.Delete(imagePath);
Method 2: Assign the Bitmap and dispose of it immediately
This method can be used when you need to assign a Bitmap to a Control and then move/delete the Image file. The Image is disposed of immediately, so it's only available through a Control's property: if we ask to have it back, sometimes what we get is not exactly what we gave.
string imagePath = #"[Path of the Image]";
using (Image image = Image.FromFile(imagePath, true))
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = new Bitmap(image);
}
File.Delete(imagePath);
Note that the old image assigned to the Control, if any, is disposed of before assigning a new one.
Also note I'm always specifying to preserve the internal ICM informations, if any, specifying true as the second parameter of both new Bitmap(imagePath, true) and Image.FromFile(imagePath, true).
Some images won't look as the original if we don't.
You should use a readonly File Access if you don't want it to be locked:
using( FileStream stream = new FileStream( path, FileMode.Open, FileAccess.Read ) )
{
image = Image.FromStream( stream );
}
Hope it helps...
I had the same problem before, i did the following:
this.photo.Dispose();
this.photo.Refresh();
this.photo.Image.Dispose();
this.photo.Image = null;
this.photo.ImageLocation = null;
and it worths it.

UWP how to upload a list of images from different folders to different pivotItems

I'm creating an UWP App image gallery and I want to upload a list of images
with a single method changing folder location to multipes PivotItems with a single method
public async void precargar()
{
List<StackPanel> spanel = new List<StackPanel>();
IReadOnlyList<StorageFile> files = await Imagefolder.GetFilesAsync();
foreach (var item in files)
{
StackPanel stack = new StackPanel();
StorageItemThumbnail thumbnail = null;
try { thumbnail = await item.GetThumbnailAsync(ThumbnailMode.PicturesView); }
catch (Exception) { System.Diagnostics.Debug.WriteLine("esto es un error lo sentimos"); }
BitmapImage bi;
if (thumbnail == null)
{
bi = new BitmapImage(new Uri("ms-appx:///wallpaper/2.png"));
}
else
{
Stream stream = thumbnail.AsStream();
bi = new BitmapImage();
await bi.SetSourceAsync(stream.AsRandomAccessStream());
}
Image image = new Image() { Width = 300 };
image.Source = bi;
stack.Children.Add(image);
spanel.Add(stack);
}
Viewtiles.ItemsSource = spanel;
}
the above code works correctly
in this way:
and I use it in this way to load them to the interface
public async void CargarFolders()
{
Imagefolder = await appInstalledFolder.GetFolderAsync(carpetas[0]);
precargar();
}
Now I want to use that code to load other lists of images using the same code in the following way:
public async void Naturaleza()
{
Imagefolder = await appInstalledFolder.GetFolderAsync(carpetas[1]);
precargar();
Naturals.ItemsSource = spanel;
}
but it does not work. How can I do it?
I think you should use FlipView control rather than pivot control for displaying image. Because FlipView control has move forward and backward option with touch friendly. You can set large sets of images to FlipView control.
Please go through https://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/flipview

Images in Background Workers in WPF Application

I have the following code snippet that I use to create a List to add to a scrollviewer as a binding in a WPF application:
private void LoadThumbs(object sender, DoWorkEventArgs e)
{
//ClearScreen();
int max = (int)e.Argument;
int current = 0;
foreach (string filename in filenames)
{
Image thumbnail = new Image();
Uri image_path = new Uri(filename);
BitmapImage image = new BitmapImage(image_path);
Thickness thumb_margin = thumbnail.Margin;
thumb_margin.Bottom = 2.5;
thumb_margin.Top = 2.5;
thumb_margin.Left = 2.5;
thumb_margin.Right = 2.5;
thumbnail.Margin = thumb_margin;
thumbnail.Width = 100;
image.DecodePixelWidth = 200;
thumbnail.Source = image;
thumbnail.Tag = filename;
thumbnail.MouseDown += image_Click;
thumbnail.MouseEnter += hand_Over;
thumbnail.MouseLeave += normal_Out;
images.Add(thumbnail);
thumbnail = null;
}
}
This worked fine until I added a BackgroundWorker to process this. Now, when execution gets to
Image thumbnail = new Image();
I get the following exception:
System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.'
Two questions:
(1) How can I process this code to allow the background worker to work on Image, or, (2) is there a better way to do what I am doing to allow for the BackgroundWorker to work?
I have zero experience working in a multi-threaded environment. I want it to work this way because the largest record I process has 180 images and creates about a 10-15 second hang.
Do not create Image elements in code behind. Instead, use an ItemControl with an appropriate ItemTemplate:
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Bind it to a view model like shown below, which is capable of asynchronously loading the image files. It is important that the BitmapImages are frozen to make them cross-thread accessible.
public class ViewModel
{
public ObservableCollection<ImageSource> Images { get; }
= new ObservableCollection<ImageSource>();
public async Task LoadImagesAsync(IEnumerable<string> filenames)
{
foreach (var filename in filenames)
{
Images.Add(await Task.Run(() => LoadImage(filename)));
}
}
public ImageSource LoadImage(string filename)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.DecodePixelWidth = 200;
bitmap.UriSource = new Uri(filename);
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
}
which is initialized like this:
private ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
...
await viewModel.LoadImagesAsync(..., "*.jpg"));
}
An alternative view model method could load the BitmapImages directly from a FileStream instead of an Uri:
public ImageSource LoadImage(string filename)
{
using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 200;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
}

disposing generic list of bitmapimages

How can i dispose all the images in a generic list in WPF?
Here is my try:
//piclist is a global variable pointing to a folder on harddrive
foreach (string s in this.piclist)
{
this.picsToDisplay.Add(this.BitmapFromUri(new Uri(s)));
}
private BitmapImage LoadImage(string myImageFile)
{
BitmapImage myRetVal = null;
if (myImageFile != null)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead(myImageFile))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
myRetVal = image;
}
return myRetVal;
}
and this is how i clear and dispose everything:
if (this.picsToDisplay.Count > 0)
{
foreach (BitmapImage b in this.picsToDisplay)
b.StreamSource.Dispose();
this.picsToDisplay.Clear();
}
Array.ForEach(Directory.GetFiles(this.tempPics),
delegate(string path) { File.Delete(path); });
It crashes first at b.StreamSource.Dispose();
Saying that streamsource is null 'reference not set to an instance of an object'
If i comment out that line, it crashes at this part: File.Delete(path);
With the message that the picture i'm trying to delete is in use.
The pictures getting displayed in an image control in WPF.
Its source is already set to NULL before i proceed to dispose etc.
What am i missing guys?
============
EDIT
private void btnLoadPictures_Click(object sender, EventArgs e)
{
this.ClearPicturesFromList();
DirectoryInfo Dir = new DirectoryInfo(this.pictures);
List<string> stringList = new List<string>();
FileInfo[] picList = Dir.GetFiles();
stringList = (from FI in picList
where FI.LastWriteTime.Date.ToString("dd-MM-yyyy") ==
this.dpPictureDate.SelectedDate.Value.ToString("dd-MM-yyyy")
select (FI.FullName)).ToList();
try
{
if (Directory.Exists(this.tempPics))
{
if (stringList.Count > 0)
{
foreach (string s in stringList)
{
string destFolder = System.IO.Path.Combine(this.tempPics, System.IO.Path.GetFileName(s));
File.Copy(s, destFolder, true);
}
DirectoryInfo Dir2 = new DirectoryInfo(this.tempPics);
FileInfo[] pics = Dir2.GetFiles();
this.picsInTempFolder = (from FI in pics
select (FI.FullName)).ToList();
foreach (string s in this.picsInTempFolder)
{
this.picsToDisplay.Add(this.LoadImage(s));
}
this.indexCounter = 0;
this.imgBox.Source = (BitmapImage)this.picsToDisplay[this.indexCounter];
this.tbxPictureName.Text = System.IO.Path.GetFileName(this.picsInTempFolder[this.indexCounter]);
stringList.Clear();
pics = null;
}
}
}
catch (Exception exp)
{
this.WriteToRichTextBox(exp.ToString());
}
}
pressing the clearimages button for the first time, i get the exception about the file being in use. doing it the second time, actually works fine.
putting a thread.sleep between clearing all lists and stuff than delete the files in the temp directory doenst work. but somehow it needs a sort of delay.
try this method for loading images
private BitmapImage LoadImage(string myImageFile)
{
BitmapImage myRetVal = null;
if (myImageFile != null)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead(myImageFile))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
myRetVal = image;
}
return myRetVal;
}

Categories

Resources