XAML Image source has issues displaying a deep nested path - image

This is quite vexing.
I am working on an app for image management. Part of the value is the ability to store images in sub-folders based on image properties, eg. creation date.
If I store the image source in a shallow folder (app\images\img.jpg), everything works fine.
If I store the image in KnownFolders.Pictures\source\year\month\day\img.jpg, Image does not render. (Yes, that specific path won't work, I am trying to give you a sense of how the path is constructed)...
The file is actually there. The path is correct (I can open it in a browser, e.g.). The app has access to the file.
But it does not render the bitmap.
I tried to render the bitmap manually using
new BitmapImage(new Uri("KnownFolders.Pictures\source\year\month\day\img.jpg"),UriKind.Absolute))
That does not render anything. (Again, assume the path is valid and has a file at its bottom).
What Am I Missing?
The head scratcher: for GIF anims, I am using Thomas Levesque's useful component: https://github.com/XamlAnimatedGif. That one, unfortunately, does only render gifs... and it does so even when the path is the one given above. So the Standard IMAGE control does not render correctly, but Thomas's control does... infuriating.

An UWP app can't load a BitmapImage from an absolute URL to a file in a folder structure below the Pictures Library Folder.
So this won't work:
var relativePath = #"source\year\month\day\img.jpg";
var imageFile = await KnownFolders.PicturesLibrary.GetFileAsync(relativePath);
var bitmapImage = new BitmapImage(new Uri(imageFile.Path));
However, you could do this:
var relativePath= #"source\year\month\day\img.jpg";
var imageFile = await KnownFolders.PicturesLibrary.GetFileAsync(relativePath);
var bitmapImage = new BitmapImage();
using (var stream = await imageFile.OpenAsync(FileAccessMode.Read))
{
await bitmapImage.SetSourceAsync(stream);
}

So, after way too much time spent on this...
First, link to DataContextChanged of the IMAGE element. In there, parse the DataContext out. If you are using the IMAGE outside of an ItemsControl etc, this is not required...
private async void ImageView_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
if (sender is Image)
{
Image img = (Image)sender;
if (img.DataContext is ImageView)
{
MyViewDataContext dc = (MyViewDataContext)img.DataContext;
img.Source = await dc.bitmap();
}
}
}
And here the implementation of MyViewDataContext.bitmap() which has a property called source that yields, you guessed it, absolute paths:
public async Task<BitmapImage> MyViewDataContext.bitmap()
{
if (_bitmap == null)
{
try
{
StorageFile file = await StorageFile.GetFileFromPathAsync(source);
bool r = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.CheckAccess(file);
if (r)
{
using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// create a new bitmap, coz the old one must be done for...
_bitmap = new BitmapImage();
// And get that bitmap sucked in from stream.
await _bitmap.SetSourceAsync(fileStream);
}
}
}
catch (Exception e)
{
_bitmap = null;
}
}
return _bitmap;
}
BitmapImage _bitmap;
I cache the resulting bitmap until I dispose of this MyViewDataContext.
I am now most concerned about memory. This one worries me:
How to dispose BitmapImage cache?
So, as a tech debt, I am going to address the potential mem leaks later, once this whole thing is on the test bench and I can take a look at its runtime behavior...

To access the folders and libraries represented by the properties of this class, specify the corresponding capabilities in your app manifest. For example, to access KnownFolders.PicturesLibrary, specify the Pictures Library capability in the app manifest.
Hope this will help
KnowFolders

Related

How to show all images from a folder in Xamarin Cross-Platform app?

I am using VS 2017 to create a cross platform (UWP, Android, iOS) Xamarin app. I am trying to show all images from a folder on device as thumbnails (similar to gallery app, sample screenshot attached).
I have looked into WrapLayout sample code provided on Xamarin website (Link), but it's loading all images from internet using JSON
protected override async void OnAppearing()
{
base.OnAppearing();
var images = await GetImageListAsync();
foreach (var photo in images.Photos)
{
var image = new Image
{
Source = ImageSource.FromUri(new Uri(photo + string.Format("?width={0}&height={0}&mode=max", Device.OnPlatform(240, 240, 120))))
};
wrapLayout.Children.Add(image);
}
}
async Task<ImageList> GetImageListAsync()
{
var requestUri = "https://docs.xamarin.com/demo/stock.json";
using (var client = new HttpClient())
{
var result = await client.GetStringAsync(requestUri);
return JsonConvert.DeserializeObject<ImageList>(result);
}
}
I have also looked into Xamarin Media Plugin (Link), but it shows only one image at a time. Sample code -
await CrossMedia.Current.Initialize();
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
return;
MainImage.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
But I am unable to find a way to implement these two (or any other methods) in such a way that I can create my own gallery section in my app.
You need to create an Activity in your specific platform. This activity will be launched as an intent throught your PCL project using, for instance, Dependency Services.
In this custom Activity you should have a GridView which fills its source from the current directory if the file fits your restrictions, such a specific extension, size, etc.
Finally, to get the selected image you just send the image path or whatever you need to the PCL project with DependencyService.

Is it possible to drag and drop a UWP StorageFile to desktop, created using CreateStreamedFileFromUriAsyc

The goal:
What I want to do is drag a GridViewItem from a GridView in a UWP app outside of the app to the desktop explorer. During the drop event, I want to download a file from the internet and create a StorageFile that will be used to populate the DataPackage. I want this StorageFile to be copied to the desktop. Unfortunately, when a deferral is used for UWP Drag&Drop (using SetDataProvider), as soon as you leave the app window, the request is activated and you have to populate the DataPackage with the object to be transferred. So, it seemed to me that I would need to use a deferred type of StorageFile created with CreateStreamedFileFromUriAsyc.
I do not want to pre-download the data every time I start to do a drag and drop operation. I only want to download the data when I'm actually dropping it somewhere legitimate to copy.
I know how to drag and drop a pre-existing StorageFile from UWP to Explorer (desktop) using a deferred request.
I also know how to create a StorageFile using CreateStreamedFileFromUriAsyc that will gives you a StorageFile that downloads data only when the data is requested.
When I try to combine these two ideas, windows Explorer gives me the error 'interface is not supported.'.
If I use the exact same code, but just get the file contents by calling something like GetBasicPropertiesAsync during the deferred drag handler, it works only if I hold the drag over the desktop until the file is downloaded. I can see it finishing when the drag icon changes from the 'prohibited' icon to 'copy'. If I let go of the mouse button before it is done, no copy will occur and no errors are raised.
Obviously, I would like the drag and drop to download without having to manually start it on the deferred handler. Any ideas? Is this possible?
(Yes, I realize the code to create the correct file extension is wrong/incomplete, but that's irrelevant here...)
//DragStarted Handler in constructor
DragItemsStartedCommand = ReactiveCommand.Create<DragItemsStartingEventArgs>((e) =>
{
_dragItems = e.Items.Cast<ItemViewModel>();
e.Data.Properties.Title = "Transfer file";
e.Data.Properties.Description = "desscription of transfering a file";
e.Data.Properties.FileTypes.Add(StandardDataFormats.StorageItems);
e.Data.SetDataProvider(Windows.ApplicationModel.DataTransfer.StandardDataFormats.StorageItems, OnDeferredStorageFileRequestedHandler);
e.Data.RequestedOperation = DataPackageOperation.Copy;
});
//Deferred request handler
async void OnDeferredStorageFileRequestedHandler(DataProviderRequest request)
{
DataProviderDeferral deferral = request.GetDeferral();
try
{
Task<IEnumerable<StorageFile>> task = null;
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
task = DownloadStorageFiles();
});
var result = await task;
request.SetData(result);
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
deferral.Complete();
Debug.WriteLine("deferral complete!!!");
}
}
//Create StorageFile with deferred loading Task
async Task<IEnumerable<StorageFile>> DownloadStorageFiles()
{
List<StorageFile> storageItems = new List<StorageFile>();
foreach (var item in _dragItems)
{
var request = new RestSharp.RestRequest();
var defaultItemType = ItemType.MSWord;
switch (item.MimeTypeTranslated)
{
case ItemType.GoogleDocument:
case ItemType.GoogleSpreadsheet:
case ItemType.GooglePresentation:
case ItemType.GoogleDrawing:
case ItemType.GoogleScript:
request.Resource = $"files/{item.File.id}/export";
request.AddParameter("mimeType", Statics.ItemTypeDictionary.First(x => x.Value == defaultItemType).Key);
break;
default:
request.Resource = $"files/{item.File.id}";
request.AddParameter("alt", "media");
break;
}
string fileName = "";
if (item.File.name.EndsWith($".{Statics.ItemExtensionDictionary[defaultItemType]}"))
fileName = $"{item.File.name}";
else
fileName = $"{item.File.name}.{Statics.ItemExtensionDictionary[defaultItemType]}";
var uri = account.Client.GetAuthorizedUriForDownload(request);
var thumbnail = RandomAccessStreamReference.CreateFromFile(await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///StoreLogo.png")));
var storageFileDeferred = await StorageFile.CreateStreamedFileFromUriAsync(fileName, uri , thumbnail);
//var props = await storageFileDeferred.GetBasicPropertiesAsync();
storageItems.Add(file);
}
return storageItems;
}
GitHub repro of this problem:
https://github.com/limefrogyank/DragDeferredFileToDesktop
First page is a regular drag to desktop that works because StorageFile (+ underlying data) is already in assets folder
Second page shows the error generated when using a StorageFile that is created with CreateStreamedFileFromUriAsync.
Third page uses the same type of StorageFile, but with a hack to force the retrieval of data synchronously. Desktop freezes for a second until data is ready.
As of the recent Windows updates the drag n' drop of storage files on UWP apps is automatically handled by the system.
example gif.

WP7: Get image's name which images are stored in Libary picture?

I have some images that are stored in Media Libary, view here. Now I need to get the name of an image after I choose the image by using photo chooser task. I used photo chooser task to select an image and then got the path of the image. My pupose is get the name from the path:
private void button1_Click(object sender, RoutedEventArgs e)
{
PhotoChooserTask objPhotoChooser = new PhotoChooserTask();
objPhotoChooser.Completed += new EventHandler<PhotoResult>(PhotoChooseCall);
objPhotoChooser.Show();
}
void PhotoChooseCall(object sender, PhotoResult e)
{
switch (e.TaskResult)
{
case TaskResult.OK:
BinaryReader objReader = new BinaryReader(e.ChosenPhoto);
image1.Source = new BitmapImage(new Uri(e.OriginalFileName));
MessageBox.Show("Photo's name: " + e.OriginalFileName.ToString());
break;
case TaskResult.Cancel:
MessageBox.Show("Cancelled");
break;
case TaskResult.None:
MessageBox.Show("Nothing Entered");
break;
}
}
Output:
Photo's name: \Applications\Data\C80566AB-E17E-495C-81A1-3FCAE34D3DEDE\Data\PlatformData\PhotoChooser-a8208960-3597-40fc-9b4f-869afcf822b6.jpg
After I choose the same image. The name of it will change (PhotoChooser-a8208960-3597-40fc-9b4f-869afcf822b6.jpg will change). I think it's not the name of the photo.
So:
Can we get the name of the image?
And how do we do?
You can get the actual file names. Use the MediaLibrary for that:
MediaLibrary lib = new MediaLibrary();
var collection = lib.RootPictureAlbum;
foreach (var p in collection.Albums[0].Pictures)
Debug.WriteLine(p.Name);
Notice that I am specifying the album index inside the root picture album. 0 will be for sample pictures, and so on. If you need to grab the contents of the image, just use p.GetImage(); to get the readable stream.
No. You cannot get the name of the file as it is in the pictures hub. You are given a copy of the stream and that has a different, temporary, name.

DataBound controls loading images and avoiding image cache in WP7

I want to load images into a Pivot header to substitute the lack of a Gallery control in WP7. I'm trying to populate them from a URL, and want to make sure that the image is not kept in the cache (by setting UriSource = null) to make sure that they don't take too much resources.
There's no way to do this in the XAML itself, can someone give me sample code to handle this from code-behind. my attempts have been unsuccessful. what am I doing wrong here?
public class PhotoGalleryVM
{
public ObservableCollection<BitmapImage> Images
{
get
{
ObservableCollection<BitmapImage> list = new ObservableCollection<BitmapImage>();
foreach (RoomImage r in App.appData.currentChoices.roomImages)
{
BitmapImage img = new BitmapImage(new Uri(Uri.UnescapeDataString(r.largeUri)));
img.UriSource = null;
list.Add(img);
}
return list;
}
}
}
There is an option that enables to ignore image cache:
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
Read more at msdn

How can I insert an image with iTextSharp in an existing PDF?

I have an existing PDF and I can use FdFWriter to input to text boxes. It works well. Now I have an image. I have read the documentation and looked at many examples but they all create new documents and insert an image. I want to take an existing PDF and insert an image into either an image field or as the icon image of a button. I have tried but it corrupts the document.
I need to be able to take an existing document and put an image on it. I do not want to open, read, replace, and delete the original. This original changes and the name "original" only means the source file in this context. There are many PDF files like this that need an image.
Thank you for any help.
Edit - I am very thankful for the code below. It works great, but the problem for me is that the existing PDF has digital signatures on it. When the document is copied like this (into result.pdf) those signatures, while still present, have a different byte count or other item that is corrupted. This means the signatures, while they show up on result.pdf, have an icon next to them that state "invalid signature."
In case it matters I am using a Topaz signature pad to create my signatures, which has it's own security. Merely copying the PDF will not corrupt it but the process below will.
I am trying to put the image on the existing document, not a copy of it, which in this case matters.
Also, by signature, I mean handwritten, not pin numbers.
Thank you again.
EDIT - Can PdfSignatureAppearance be used for this?
EDIT - I seem to be able to do it with:
var stamper = new PdfStamper(reader, outputPdfStream,'1',true);
If you want to change the contents of an existing PDF file and add extra content such as watermarks, pagenumbers, extra headers, PdfStamper is the object you need. I have successfully used the following code to insert an image into an existing pdf file to a given absolute position:
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
class Program
{
static void Main(string[] args)
{
using (Stream inputPdfStream = new FileStream("input.pdf", FileMode.Open, FileAccess.Read, FileShare.Read))
using (Stream inputImageStream = new FileStream("some_image.jpg", FileMode.Open, FileAccess.Read, FileShare.Read))
using (Stream outputPdfStream = new FileStream("result.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
{
var reader = new PdfReader(inputPdfStream);
var stamper = new PdfStamper(reader, outputPdfStream);
var pdfContentByte = stamper.GetOverContent(1);
Image image = Image.GetInstance(inputImageStream);
image.SetAbsolutePosition(100, 100);
pdfContentByte.AddImage(image);
stamper.Close();
}
}
}
When you insert the image you have the possibility to resize it. You can take a look at transformation matrix in the iTextSharp documentation.
Here is a similar example whichi inserts an image on the page using the stamper:
Gmane iTex Mailing List Post
I could solve my problem by simply adding following lines to my signing code to add image
var image = iTextSharp.text.Image.GetInstance(#"C:\Users\sushil\Documents\sansign.jpg");
appearance.Acro6Layers = true;
appearance.SignatureGraphic = image;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
As I was signing document with visible digital signature , now I can have both image and digital signature properties side by side
in the .net core6 that uses DDD try this declare class in Infrastructure Layer
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
public async Task<string> SignatureToPdf(string pathPdfFile, string
pathSignatureImage, string pathOutputName)
{
var webRootPath = hostingEnvironment.ContentRootPath;
if (!File.Exists(Path.Combine(webRootPath, pathPdfFile))) return
null;
await using Stream inputPdfStream =
new FileStream(Path.Combine(webRootPath, pathPdfFile),
FileMode.Open, FileAccess.Read, FileShare.Read);
await using Stream inputImageStream =
new FileStream(Path.Combine(webRootPath, pathSignatureImage), FileMode.Open, FileAccess.Read, FileShare.Read);
await using Stream outputPdfStream =
new FileStream(Path.Combine(webRootPath, pathOutputName),
FileMode.Create, FileAccess.Write, FileShare.None);
var reader = new PdfReader(inputPdfStream);
var stamper = new PdfStamper(reader, outputPdfStream);
var pdfContentByte = stamper.GetOverContent(1);
var image = Image.GetInstance(inputImageStream);
image.SetAbsolutePosition(100, 100);
pdfContentByte.AddImage(image);
stamper.Close();
return "ok";
}
pdftk can do this. It's not a library but you can easily call it from your code as a .exe.
See stamp and background commands:
http://www.pdflabs.com/docs/pdftk-man-page/
ref: How to do mail merge on top of a PDF?

Resources