Change Listbox ItemsSource with different Thread - c#

I want to change ListBox ItemsSource without UI freezing. For this challenge, I wrote the code that you see below;
When User Click The Button
Thread th = new Thread(DisplayFilesAsync);
th.Start(new object[] { FolderPath, fdv.FileType });
Async Method
private void DisplayFilesAsync(object param)
{
object[] args = param as object[];
string searchText = Convert.ToString(args[0]);
FileType type = (FileType)args[1];
IEnumerable<FileListItem> list = uDirectoryHelper.GetFileFromFolder(searchText, type);
Dispatcher.BeginInvoke(new Action<IEnumerable<FileListItem>>(DisplayFiles), DispatcherPriority.Background, new object[] { list });
}
Change The ItemsSource
private void DisplayFiles(IEnumerable<FileListItem> fileList)
{
lstFiles.ItemsSource = fileList;
}
In the last method, if I change directly ItemSource of ListBox, program isn't break up but when program pass to closed curly brackets of the method, is break up and throw an Must create DependencySource on same Thread as the DependencyObject exception.
if I changed that; it's break up on closed curly brackets again, not over the ADD method and throw an same exception.
private void DisplayFiles(IEnumerable<FileListItem> fileList)
{
foreach (FileListItem item in fileList)
{
lstFiles.Items.Add(item);
}
}
But if I change the code like that, it's work perfectly and add items to my listbox.
foreach (FileListItem item in fileList)
{
lstFiles.Items.Add("EXAMPLE");
}
I'm really don't understand to my missing. Why I can add to some string but I can't add to my FileListItem class. What am I missing? I've tried a lot of different code but it's always break on closed curly braces NOT OVER THE ASSIGMENT LINE.
I'm really need to help. Thanks for answers. Have good day, good works.

Your FileListItem class has a property of type ImageSource
public ImageSource ItemImage { get; set; }
Although you haven't show that in your question, you certainly assign a value to that property in your GetFileFromFolder method, e.g. like
fileList.ItemImage = new BitmapImage(new Uri(...));
Unless the BitmapImage is not frozen in the background thread, it can not be accessed by the UI thread, hence you get that exception.
Make sure you call the Freeze() method on the BitmapImage after it has loaded a bitmap. Note that you can't do asynchronous bitmap loading (e.g. by a remote URI) in this situation. If your bitmap is loaded from a local file, do something like this:
var bitmap = new BitmapImage();
using (var stream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
}
fileList.ItemImage = bitmap;
Or, instead of BitmapImage, use the BitmapFrame.Create() method, which already returns a frozen BitmapFrame (another subclass of ImageSource).
using (var stream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read))
{
fileList.ItemImage = BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}

Related

Is there a solution to convert an ImageSource to an Android Bitmap and vice-versa in Xamarin.Forms

I'm currently working on an application that modifies a picture that is currently located in the PCL project. And I'm currently having trouble with converting the DataTypes.
I'm currently trying to figure out how I can convert an ImageSource to a bitmap. I've read some answers on the internet but they didn't seem to work for me.
I call the platform-specific code with a DependencyService and pass the ImageSource as a parameter.
The function signature looks like this:
public ImageSource BlurImage(ImageSource ImageSource)
{
return null;
}
This function should Create a bitmap from the ImageSource first and once all the logic has been done it should convert back to an ImageSource.
Can someone explain to me how I should convert ImageSource to bitmap and vice-versa?
Thanks in advance,
Tom
You can use the below function to convert ImageSource to Bitmap:
private async Task<Bitmap> GetImageFromImageSource(ImageSource imageSource, Context context)
{
IImageSourceHandler handler;
if (imageSource is FileImageSource)
{
handler = new FileImageSourceHandler();
}
else if (imageSource is StreamImageSource)
{
handler = new StreamImagesourceHandler(); // sic
}
else if (imageSource is UriImageSource)
{
handler = new ImageLoaderSourceHandler(); // sic
}
else
{
throw new NotImplementedException();
}
var originalBitmap = await handler.LoadImageAsync(imageSource, context);
return originalBitmap;
}
And the Next one for Bitmap to ImageSource:
public async Task<ImageSource> GetBytesFromImage(Bitmap bitmap)
{
ImageSource imgSource;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream); // You can change the compression asper your understanding
imgSource=ImageSource.FromStream(stream);
}
return imgSource;
}

Is it some way to do async code for write to Stream?

I write some WPF editor, wich show come text.
But text stores at Stream.
So, here XAML code:
<TextBox Text="{Binding ObjectViewModel.Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
I set UpdateSourceTrigger to PropertyChanged, because user can read and write some text at this redactor;
Here code where read and write text (as stream):
public String Text
{
get
{
var sr = new StreamReader(StreamObject.text, Encoding.UTF8, true);
return sr.ReadToEnd();
}
set
{
lock (_lockObject)
{
var str = value;
var textStream = new MemoryStream();
using (var writer = new BinaryWriter(textStream, Encoding.UTF8, true))
{
writer.Write(Encoding.UTF8.GetBytes(str));
}
textStream.Position = 0;
StreamObject.text = null;
StreamObject.text = textStream;
textStream.Close();
}
}
}
So, when user type new word- code write to stream new data (clear old stream and create new).
But it get slow perfomance.
Should i use async (somehow) or other method to make perfomace better?
I know,that can use LostFocus property, but at that way user should to lost focus of TextBox.
Should i use async (somehow) or other method to make perfomace better?
There is indeed an async version of the ReadToEnd method called ReadToEndAsync but it won't actually make your code any faster and you cannot call it from a property since properties cannot be marked as async.
It makes no sense to create a new Stream in the getter of the source property and in the setter of the same each time the property is changed.
You should read from the stream once, for example in the constructor of the view model or the Execute method of a command, and then set the value of the string property. Then you convert the string back to a stream once by the time you actually need to save it to the database, e.g:
class ViewModel
{
public ViewModel()
{
using (var sr = new StreamReader(StreamObject.text, Encoding.UTF8, true))
Text = sr.ReadToEnd();
}
//this method is called when your "Save" button or similar is clicked
public void SaveToDb()
{
lock (_lockObject)
{
var str = value;
using (textStream = new MemoryStream())
{
using (var writer = new BinaryWriter(textStream, Encoding.UTF8, true))
{
writer.Write(Encoding.UTF8.GetBytes(str));
}
textStream.Position = 0;
StreamObject.text = null;
StreamObject.text = textStream;
}
}
}
public String Text
{
get;set;
}
}
The source property itself should not know anything about the stream.

C# problems with FileInfo List(add Image)

How can I insert Image object into FileInfo list?
I have a list with images of this type
public List<FileInfo> _imagesList = new List<FileInfo>();
But I need to add image to this list using a method like this one:
public void AddImage(Image img)
{
}
I tried using this method
public void AddImage(string pathToImage)
{
try
{
FileInfo file = new FileInfo(pathToImage);
_imagesList.Add(file);
}
catch (Exception e)
{
MessageBox.Show("Не удалось загрузить изображение. Ошибка : " + e);
}
}
Let me explain why you can't do that. Image class does not have any references to file which was used to create that image. When you create Image instance with
Image.FromFile(filename)
File name is not stored anywhere. All you will have inside Image instance is an array of bytes which is initialized this way:
Stream dataStream = File.OpenRead(filename);
image.rawData = new byte[(int) dataStream.Length];
dataStream.Read(image.rawData, 0, (int) dataStream.Length);
So, the point is - you can't get file name from which Image was created. Actually image can be create not from file - from any stream (network stream, memory stream). Image is not related to any file, thus you can't insert it to list of FileInfo objects.
Well, as a workaround, you can save Image to file. And then insert that file to list.
you can either use the method you gave, adding new FileInfo(pathToImage) or change your list so it'll contain Image items
you can use
public List<Image> _imagesList = new List<Image>();
for example. if you share more information i could help you more. this looks like an XY problem so maybe you should tell us what's the real problem
You cannot do like this, FileInfo accept one arguments in its constructor: fileName.
You should have something like this:
public List<FileInfo> _imagesList = new List<FileInfo>();
public void Main()
{
AddImage("YourImagePath1");
AddImage("YourImagePath2");
// ...
}
public void AddImage(string path)
{
_imagesList.Add(new FileInfo(path));
//var img = Image.FromFile(path); // do this if you need to load your image
}
UPDATE:
You changed your question and your code now looks like mine, what was the issue in the code you used to have?

Cannot serialize member 'vehicleImage' of type 'Windows.UI.Xaml.Media.Imaging.BitmapImage'

When I add the type BitmapImage to my class for my Windows 8 application (below), I get the:
Cannot serialize member vehicleImage' of type 'Windows.UI.Xaml.Media.Imaging.BitmapImage', see inner exception for more details.
Inner Exception Details:
System.Runtime.InteropServices.WindowsRuntime.RuntimeClass is inaccessible due to its protection level. Only public types can be processed.
Code:
public BitmapImage vehicleImage
{
get
{
return m_vehicleImage;
}
set
{
Uri _baseUri = new Uri("ms-appx:///");
BitmapImage imageBitmap = new BitmapImage(new Uri(_baseUri, ImagePath));
m_vehicleImage = imageBitmap;
OnPropertyChanged("vehicleImage");
}
}
private async void SetImage()
{
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(ImagePath);
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var img = new BitmapImage();
img.SetSource(fileStream);
vehicleImage = img;
}
I'm serializing the object into XML. When I remove this bit of code everything works. I'd like to have the ability to have an image that the user selects from their computer (which is why I'm trying to use the BitmapImage type).
Any help is appreciated.
Use the System.Xml.Serialization namespace to access [XMLIgnore()] for these sort of properties.
You should want to store the string of where the image is, correct? Then you serialize the string, and [XMLIgnore()] the BitMapImage.
This allows the BitMap Image to still be accessed while serialization can occur.

Feedback on code to Serialize, Deserialize and Save Image

Here is my code to Serialize, Deserialize and Save an image to the file system. I have looked at many examples of serialization/deserialization and I just want to get some feedback as I am sure my code could be improved. Any feedback would be greatly appreciated. I know this is a common problem so hopefully this question will be a good resource for others in the future.
This is the revised code using recommendations:
private void Form1_Load(object sender, EventArgs e)
{
RunTest();
}
private void RunTest()
{
byte[] jpgba = ConvertFileToByteArray("D:\\Images\\Image01.jpg");
using (Image jpgimg = ConvertByteArrayToImage(jpgba))
{
SaveImageToFileSystem(jpgimg, "D:\\Images\\Image01_Copy.jpg");
}
byte[] pngba = ConvertFileToByteArray("D:\\Images\\Image02.png");
using (Image pngimg = ConvertByteArrayToImage(pngba))
{
SaveImageToFileSystem(pngimg, "D:\\Images\\Image02_Copy.png");
}
byte[] gifba = ConvertFileToByteArray("D:\\Images\\Image03.gif");
using (Image gifimg = ConvertByteArrayToImage(gifba))
{
SaveImageToFileSystem(gifimg, "D:\\Images\\Image03_Copy.gif");
}
MessageBox.Show("Test Complete");
this.Close();
}
private static byte[] ConvertFileToByteArray(String FilePath)
{
return File.ReadAllBytes(FilePath);
}
private static Image ConvertByteArrayToImage(byte[] ImageByteArray)
{
using (MemoryStream ms = new MemoryStream(ImageByteArray))
{
return Image.FromStream(ms);
}
}
private static void SaveImageToFileSystem(Image ImageObject, string FilePath)
{
// ImageObject.Save(FilePath, ImageObject.RawFormat);
// This method only works with .png files.
// This method works with .jpg, .png and .gif
// Need to copy image before saving.
using (Image img = new Bitmap(ImageObject.Width, ImageObject.Height))
{
using (Graphics tg = Graphics.FromImage(img))
{
tg.DrawImage(ImageObject, 0, 0);
}
img.Save(FilePath, img.RawFormat);
}
return;
}
What I have see from quick look:
Streams should be wrapped in using(...) pattern, in your case if exception occurs during processing, then Dispose() won't be called.
using (FileStream fs = new FileStream(FilePath, FileMode.Open))
{
// Another small optimization, removed unnecessary variable
byte[] iba = new byte[(int)fs.Length];
fs.Read(iba, 0, iba.Length);
}
You should catch only exceptions you expect. For example in SerializeImage this will be IOException. Catching all exceptions is very bad practice.
}
catch (IOException ex)
{
Image.FromStream method depends on stream, so if you close underlying stream and return Image you can receive unpredictable behavior (well, in most cases this will work, but sometimes error occurs). So you need to create image copy and return it.
using (MemoryStream ms = new MemoryStream(ImageByteArray))
{
using (Image img = Image.FromStream(ms))
{
return new Bitmap(img);
}
}
You are not disposed tg graphics object and img object in SaveImage method (but disposed ImageObject, see next paragraph). And in general I do not see necessity in such logic, simply call ImageObject.Save(..., ImageFormat.Png) if you want to save image preserving quality.
In the same method (SaveImage) you are disposed ImageObject parameter. This is also bad practice in most cases, consider disposing this image outside worker method by using using(...) pattern.
Here's a bit more:
private void RunTest()
{
// byte array that can be stored in DB
byte[] iba;
// image object to display in picturebox or used to save to file system.
iba = ReadImage("D:\\Images\\Image01.jpg");
using (Image img = DeserializeImage(iba))
{
SaveImage(img, "D:\\Images\\Image01_Copy.jpg");
}
iba = ReadImage("D:\\Images\\Image02.png");
using (Image img1 = DeserializeImage(iba))
{
SaveImage(img1, "D:\\Images\\Image02_Copy.png");
}
iba = ReadImage("D:\\Images\\Image03.gif");
using (var img2 = DeserializeImage(iba))
{
SaveImage(img2, "D:\\Images\\Image03_Copy.gif");
}
MessageBox.Show("Test Complete");
}
private static byte[] ReadImage(String filePath)
{
// This seems to be the easiest way to serialize an image file
// however it would be good to take a image object as an argument
// in this method.
using (var fs = new FileStream(filePath, FileMode.Open))
{
Int32 fslength = Convert.ToInt32(fs.Length);
var iba = new byte[fslength];
fs.Read(iba, 0, fslength);
return iba;
}
}
private static Image DeserializeImage(byte[] imageByteArray)
{
using (var ms = new MemoryStream(imageByteArray))
{
return Image.FromStream(ms);
}
}
private static void SaveImage(Image imageObject, string filePath)
{
// I could only get this method to work for .png files.
// imageObject.Save(filePath, imageObject.RawFormat);
// This method works with .jpg, .png and .gif
// Need to copy image before saving.
using (Image img = new Bitmap(imageObject.Width, imageObject.Height))
{
using (Graphics tg = Graphics.FromImage(img))
{
tg.DrawImage(imageObject, 0, 0);
}
img.Save(filePath, img.RawFormat);
}
return;
}
Note what you called Serializing is just reading the bytes in. Serializing is more what you're doing when you Save.
I got rid of all the try/catch blocks. The best they were doing for you is telling you whether the problem happened in Reading, Saving or Deserializing. You can determine that from the stack trace, which you were destroying by only displaying ex.Message.
You were also returning null on a serious exception, propagating failure.
Beside that I agree with everything arbiter said.
As John Saunder says, serializing and deserializing are more than just reading the raw data from a file. See Wiki on Serialization
For images in .net, you don't need to use anything more than the provided framework methods (most of the time)
So Loading an Image (De-Serialization) in .net is.
using System.Drawing.Image;
Image test;
test = Image.FromFile(#"C:\myfile.jpg")
test = Image.FromStream(myStream); // or you can load from an existing stream
Likewise, Saving the image (Serialization) is:
test.Save(#"C:\anotherFile.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
These are the basics of loading and saving an image in .net. If you have a more specific scenario, ask another question.

Categories