How to display images from Picture Directory ? - c#

I want to display pictures in the Pictures library. I get pictures and bind data.
StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> myPictures = await picturesFolder.GetFilesAsync();
var mydata = from file in myPictures select new { Subtitle = "subtitle", Title = "title", Image = this.getImage(file.Path) };
this.DefaultViewModel["Items"] = mydata;
This is getImage() for set BitmapImage.
private async Task<BitmapImage> getImage(string finename)
{
StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
StorageFile file = await picturesFolder.GetFileAsync(fileName);
var stream = await file.OpenReadAsync();
var bitmap = new BitmapImage();
bitmap.SetSource(stream);
return bitmap;
}
But pictures are not displayed.I think this is because of async function, but I don't know the solution. Could you help me ?

I'm not sure how do you use the data that set to DefaultViewModel, but yeah, it looks like the async method is your problem.
What you need to do is to somehow await each call to getImage(). One way to do this is to use async lambda in your select. But to do that, you need to use the method syntax.
When you do that, you will have IEnumerable<Task<a>> (where a is your anonymous type), but you need just IEnumerable<a>. To get that, use Task.WhenAll() (which will return Task<a[]>) and then await its result:
var tasks = myPictures.Select(
async file => new { Subtitle = "subtitle", Title = "title", Image = await getImage(file.Path) });
var data = await Task.WhenAll(tasks);
This will execute all getImage()s at once, which may not be the most efficient solution. If you don't want that, you would need different solution.

svick's solution seems like should work, but as he/she said - it might not be the most efficient solution. A better solution for a folder with unknown number of files is to use data virtualization with FileInformationFactory.GetVirtualizedFilesVector(). That works best with a converter.
Something I have used:
Getting a virtualized file list and binding to a ListView
private async void GetPicturesFromGalleryFolder()
{
var queryOptions = new QueryOptions();
queryOptions.FolderDepth = FolderDepth.Shallow;
queryOptions.IndexerOption = IndexerOption.UseIndexerWhenAvailable;
queryOptions.SortOrder.Clear();
var sortEntry = new SortEntry {PropertyName = "System.DateModified", AscendingOrder = false};
queryOptions.SortOrder.Add(sortEntry);
queryOptions.FileTypeFilter.Add(".png");
var fileQuery = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
var fileInformationFactory =
new FileInformationFactory(
fileQuery,
ThumbnailMode.PicturesView,
0,
ThumbnailOptions.None,
true);
MyListView.ItemsSource = fileInformationFactory.GetVirtualizedFilesVector();
}
XAML
<ListView.ItemTemplate>
<DataTemplate>
<Image
Source="{Binding Converter={StaticResource converters:IStorageItemInformationToBitmapImageConverter}"/>
</DataTemplate>
</ListView.ItemTemplate>
IStorageItemInformationToBitmapImageConverter
public class IStorageItemInformationToBitmapImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var fileInfo = value as FileInformation;
if (fileInfo != null)
{
var bi = new BitmapImage();
// The file is being opened asynchronously but we return the BitmapImage immediately.
SetSourceAsync(bi, fileInfo);
return bi;
}
return null;
}
private async void SetSourceAsync(BitmapImage bi, FileInformation fi)
{
try
{
using (var stream = await fi.OpenReadAsync())
{
await bi.SetSourceAsync(stream);
}
}
catch
{
// ignore failure
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}

Related

How can i modify my UploadFiles Task to receive all of file types?

Here is my Task
private async Task UploadFiles(InputFileChangeEventArgs inputFileChangeEventArgs)
{
_CarregaFoto = true;
var fileFormat = "image/png";
var MAXALLOWEDSIZE = 60000000;
var imageFile = await inputFileChangeEventArgs.File.RequestImageFileAsync(fileFormat, 6000, 6000);
var buffer = new byte[imageFile.Size];
await imageFile.OpenReadStream(MAXALLOWEDSIZE).ReadAsync(buffer);
AnexoDenunciaModel _novaFoto = new AnexoDenunciaModel();
_novaFoto.imagem = buffer;
_novaFoto.id_ocorre = id;
_novaFoto.nome = imageFile.Name;
_novaFoto.Denunciante = true;
await _db.InsertAnexoDenuncia(_novaFoto);
_CarregaFoto = false;
await LeTabelas2();
}
I've tried to change var fileFormat to pdf or discart this part but with no success, the task only accept png, jpg, etc. files. How can i accept other types like pdf, txt files?
Take a look here: https://learn.microsoft.com/en-us/aspnet/core/blazor/file-uploads?view=aspnetcore-6.0&pivots=server
private int maxAllowedFiles = 3;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
DO YOUR STUFF
}
}
Blazor does not limit you on the possible filetypes to upload. This has to been done by yourself. In order to tell the <InputFile /> component which files should be uploaded, you'll need to specify it for yourself.
For example:
<InputFile OnChange="OnInputFileChange" class="form-control" accept="application/pdf" />

Move an object to Mainthread

I try to process a method asynchronously adn store the result in an ObservableCollection, but I always get the Error
Must create DependencySource on same Thread as the DependencyObject.
This is my Default Code that I try to use for the operation:
The method LoadServiceTasksAsync is called by a button.
public async void LoadServiceTasksAsync(object o)
{
var serviceTasks = await Task.Run(() => repository.GetServiceTasks((string)o));
var serviceTasksViewModels = serviceTasks.Select(m => new ServiceTaskViewModel()
{
OSM = m.OSM,
Priority = "" + m.Priority,
Status = m.Status
});
ServiceTasks = new ObservableCollection<ServiceTaskViewModel>(serviceTasksViewModels);
}
I also have tried to wrap it in a Dispatcher like this:
public async void LoadServiceTasksAsync(object o)
{
var serviceTasks = await Task.Run(() => repository.GetServiceTasks((string)o));
Application.Current.Dispatcher.Invoke(() =>
{
var serviceTasksViewModels = serviceTasks.Select(m => new ServiceTaskViewModel()
{
OSM = m.OSM,
Priority = "" + m.Priority,
Status = m.Status,
});
ServiceTasks = new ObservableCollection<ServiceTaskViewModel>(serviceTasksViewModels);
});
}
I know that I have to create the serviceTaskViewModels in the MainThread but I have no idea how to do that as the serviceTasks are always in another Thread.
EDIT:
var serviceTasks is an IEnumerable<Models.ServiceTask> which are downloaded with a library from a MySQL-Database. The method repository.GetServiceTasks((string)o) itself works fine.
So if I execute var serviceTasks = repository.GetServiceTasks((string)o); there are no problems, except the freezing UI.
I also have only one UI Thread.
Unfortunately I made a mistake while creating the object 'OSM' as there is a little Bitmap created depending of the state of a value.
The assignment of the Bitmap inside the 'OSM'object causes the exception.
The solution for this is to 'Freeze()' the Bitmaps:
async load BitmapImage in C#
In my case it looks like: (Shortened Version)
double? l11 = ERPStockList.getStockLevel(sapc.obj, "680");
if (l11 != null && l11>l1)
{
l1 = (double)ERPStockList.getStockLevel(sapc.obj, "680");
if (l1 >= sapc.iQuantity) { bl1 = toBitmap(File.ReadAllBytes("GreenDot.png")); }
else if (l1 > 0) { bl1 = toBitmap(File.ReadAllBytes("OrangeDot.png")); }
else { bl1 = toBitmap(File.ReadAllBytes("RedDot.png"));
}
OSM osm = new OSM()
{
LO_Acta = bl1,
LO_Windlift = bl2,
LO_Onshore = bl3
};
osm.LO_Acta.Freeze();
osm.LO_Onshore.Freeze();
osm.LO_Windlift.Freeze();
osmList.Add(osm);
The toBitmap-Method:
public BitmapImage toBitmap(Byte[] value)
{
if (value != null && value is byte[])
{
byte[] ByteArray = value as byte[];
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.StreamSource = new MemoryStream(ByteArray);
bmp.DecodePixelHeight = 10;
bmp.DecodePixelWidth = 10;
bmp.EndInit();
return bmp;
}
return null;
}

How to pass xml file inside of code instead of a folder path?

I'am trying to not create a file, and pass xml document straight to a SkiaSharp method Load. I mean, is there the way to imitate a path? So here is the code:
public IActionResult svgToPng(string itemId, string mode = "
{
var svgSrc = new XmlDocument();
svgSrc.LoadXml(/*Some xml code*/);
string svgSaveAs = "save file path";
var quality = 100;
var svg = new SkiaSharp.Extended.Svg.SKSvg();
var pict = svg.Load(svgSrc); // HERE, it needs to be a path, not XmlDocument, but i want to pass straight
var dimen = new SkiaSharp.SKSizeI
(
(int) Math.Ceiling(pict.CullRect.Width),
(int) Math.Ceiling(pict.CullRect.Height)
);
var matrix = SKMatrix.MakeScale(1, 1);
var img = SKImage.FromPicture(pict, dimen, matrix);
// Convert to PNG
var skdata = img.Encode(SkiaSharp.SKEncodedImageFormat.Png, quality);
using(var stream = System.IO.File.OpenWrite(svgSaveAs))
{
skdata.SaveTo(stream);
}
ViewData["Content"] = "PNG file was created out of SVG.";
return View();
}
The Load method seems to be this:
public SKPicture Load(
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
look at the code of that library :
https://github.com/mono/SkiaSharp.Extended/blob/master/SkiaSharp.Extended.Svg/source/SkiaSharp.Extended.Svg.Shared/SKSvg.cs
Look at the Load method, it has multiple implementations :
public SKPicture Load(string filename)
{
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
public SKPicture Load(Stream stream)
{
using (var reader = XmlReader.Create(stream, xmlReaderSettings, CreateSvgXmlContext()))
{
return Load(reader);
}
}
public SKPicture Load(XmlReader reader)
{
return Load(XDocument.Load(reader));
}
You will need to pick one of them and use it. Now, nothing stops you from getting the code and adding one extra Load for an XML string for example, but since this is a library you do not control, I'd stick to what you are given.
You could use the XmlReader version, that's probably the closest one to what you want.

Deserializing an array of values from JSON in 2048 game causes NullReferenceException

I'm making a 2048 clone for my university classes. I've got a custom class:
public class Field
{
public int value;
public bool stop;
}
And an array of those fields:
public Field[,] gameArray = new Field[4, 4];
Those are in a custom class.
Here are the methods I use to save and load this array in the item page cs file:
private async void saveAsync()
{
string jsonContents = JsonConvert.SerializeObject(currentGame.gameArray);
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile textFile = await localFolder.CreateFileAsync(saveFileName,
CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream textStream = await textFile.OpenAsync(FileAccessMode.ReadWrite))
{
using (DataWriter textWriter = new DataWriter(textStream))
{
textWriter.WriteString(jsonContents);
await textWriter.StoreAsync();
}
}
}
private async void loadAsync()
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile textFile = await localFolder.GetFileAsync(saveFileName);
using (IRandomAccessStream textStream = await textFile.OpenReadAsync())
{
using (DataReader textReader = new DataReader(textStream))
{
if ((uint)textStream.Size != 0)
{
uint textLength = (uint)textStream.Size;
await textReader.LoadAsync(textLength);
string jsonContents = textReader.ReadString(textLength);
currentGame.gameArray = JsonConvert.DeserializeObject<Field[,]>(jsonContents);
}
else currentGame.Reset();
}
update();
}
}
currentGame is an instance of the custom class. Reset(); method, well, resets all the fields, sets their value to 0. Update(); method is used to check if the player lost(not yet implemented), runs the saveAsync(); method and fills the board with lines like this(color method just changes the background color):
txt10.Text = currentGame.gameArray[0, 0].value.ToString();
color(b10, currentGame.gameArray[0, 0].value);
and the first line is what gives me a NullReferenceException. I probably messed something up with serialization and deserialization of the array, but I got literally no idea what it is and how to fix that, that's my first take on serialization and I basically copied it from the internet. Do I somehow need to pass a reference to the loadAsync(); method?

Cannot deserialize Image after sending it through the network

So, I have these two methods, which I am using to serialize and deserialize Images:
private static Image GetImageFromString(string image)
{
using (var stream = new MemoryStream(Convert.FromBase64String(image)))
{
return Image.FromStream(stream);
}
}
private static string GetImageAsString(Image image)
{
using (var stream = new MemoryStream())
{
image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
return Convert.ToBase64String(stream.GetBuffer());
}
}
If I do something Like this:
public Form1()
{
InitializeComponent();
var image = Image.FromFile(#"F:\phpide.png");
pictureBox1.Image = image;
var serialized = GetImageAsString(image);
var secondImage = GetImageFromString(serialized);
pictureBox2.Image = secondImage;
}
It works as expected
Although, If I do something like this:
//client
public void GetImage(JObject o)
{
var imageFile = o["file"].ToString();
if (!File.Exists(imageFile))
{
SendMessage("File does not exist");
return;
}
using (var image = Image.FromFile(imageFile))
{
var serialized = GetImageAsString(image);
var ob = new JObject
{
{ COMMAND, (int) Command.GetImage },
{ "content", serialized }
};
Send(ob);
ob = null;
serialized = null;
}
}
//server
public void ReceiveImage(JObject o)
{
var content = o["content"].ToString();
var image = GetImageFromString(content);
var form = new ImagePreviewForm(image);
form.Show();
}
//server
public ImagePreviewForm(Image image)
{
InitializeComponent();
pictureBox1.Image = image;
}
The image is just blank.
I have checked and the image is being received correctly, with no data loss.
What could be going wrong here? Where should I look?
This is at least one problem:
return Convert.ToBase64String(stream.GetBuffer());
You shouldn't use MemoryStream.GetBuffer here - you should use ToArray. The GetBuffer method returns the underlying buffer as-is... complete with junk data at the end of the buffer, beyond the logical current length of the stream.
Additionally, you shouldn't close the stream when you call Image.FromStream. From the docs:
You must keep the stream open for the lifetime of the Image.
So get rid of the using statement in GetImageFromString.
With the using statement you are disposing the image before the UI thread can display the image properly. Take the code out of the using block and add a Dispose() statement to the Form.Close() method.

Categories