Loading an image file in WinRT - c#

I have a WinRT project, and am getting an error when trying to preview an image. I have set capabilities to allow access to the Pictures library, and am using the following code:
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(path);
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var img = new BitmapImage();
img.SetSource(fileStream);
This error occurs on the first line:
A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll
Additional information: Error HRESULT E_FAIL has been returned from a call to a COM component.
I've tried other operations, such as folder.GetFilesAsync() with the same error. Is there another or capability that I need to allow this functionality to work correctly?
EDIT:
Based on #L.T.s answer, I tried some other capabilities. The following gives me the same error:
var folder = KnownFolders.PicturesLibrary;
var files = await folder.GetFilesAsync();
However (obviously, providing I provide the Music capability) this does not:
var testfolder = KnownFolders.MusicLibrary;
var files = await testfolder.GetFilesAsync();
I don't doubt that this it something specific to my Pictures library, but I have no idea what that could be.

If your cost is just to preview an image. You can use this
Uri uri = new Uri("ms-appx:///Assets/test.png");
BitmapImage bitmap = new BitmapImage(uri);
Image image = new Image();
image.Source = bitmap;
and add the image to a canvas. But you need to add your test.png to your projects assets first or you can change the Uri to the location of your test image.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Image Name="img"></Image>
<Button Name="btn" Click="Btn_OnClick"></Button>
</Grid>
private async void Btn_OnClick(object sender, RoutedEventArgs e)
{
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync("images.jpg");
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var image = new BitmapImage();
image.SetSource(fileStream);
img.Source = image;
}
I have windows8.1 and with vs.2013. Don't see any error. Could be something else?

//////to load an image file just do this
//put this in your app.xaml
protected override void OnActivated(IActivatedEventArgs args)
{
var root = Window.Current.Content as Frame;
var mainPage = root.Content as MainPage;
if (mainPage != null && args is FileOpenPickerContinuationEventArgs)
{
mainPage.ContinueFileOpenPicker(args as FileOpenPickerContinuationEventArgs);
}
}
//and this in your btn event
private void Button_Click(object sender, RoutedEventArgs e)
{
var openPicker = new FileOpenPicker
{
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.PicturesLibrary
};
openPicker.FileTypeFilter.Add(".jpg");
openPicker.PickSingleFileAndContinue();
}
//and this in you page
public void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs fileOpenPickerContinuationEventArgs)
{
if (fileOpenPickerContinuationEventArgs.Files != null)
{
// Do something with selected file/s
}
}

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 use TimedTextSource to view (srt) subtitle on MediaElement

I know that it is possible to view external subtitles with MediaElement but I do not get it that how. Can anybody explain me how to connect subtitles with MediaElement.
This is only clue I know so far. But how to tell MediaElement to use this?
TimedTextSource.CreateFromUri(new Uri("ms-appx:///TheVideoTitle.srt"), "en");
I found lot of information about this from Microsoft website:
https://learn.microsoft.com/en-us/uwp/api/windows.media.core.timedtextsource (Editied www url. By accident I pasted wrong one)
but they do not have any clear examples.
UPDATE
I created the following code. It starts to play video but still subtitle is missing. What's the problem. MediaElement has been created in XAML like this:
<MediaPlayerElement x:Name="MyVideoPlayer" Stretch="Fill"/>
This code still not work:
//Get MediaPlayerElement
MediaPlayerElement VideoPlayer = 'THE MEDIAPLAYER ELEMENT IN XAML';
//Get video file StorageFile
StorageFile VideoStorageFile = await StorageFile.GetFileFromPathAsync(GetMultimediaPlayer(ID).LocalVideoInformation.TitleFilePathOnStorage);
//Create MediaSource using VideoStorageFile as source
MediaSource VideoMediaSource = MediaSource.CreateFromStorageFile(VideoStorageFile);
//Create subtitle Uri
Uri SubtitleUri = new Uri("ms-appx:///TheTestSubtitle.srt");
//Set SubtitleUri as source of TimedTextSource
TimedTextSource SubtitleTimedTextSource = TimedTextSource.CreateFromUri(SubtitleUri);
//Add TimedTextSource (SubtitleTimedTextSource) to MediaSource (VideoMediaSource)
VideoMediaSource.ExternalTimedTextSources.Add(SubtitleTimedTextSource);
//Set MediaPlayerElement (VideoPlayer) source
VideoPlayer.Source = VideoMediaSource;
//Play
VideoPlayer.MediaPlayer.Play();
OK...Tried to copy from example code in GibHub but still the following code not work. In a void Tts-Resolved args.Error occur. (The system cannot find the file specifield.) Wondering what is the problem: All srt files etc. are exactly in same folders than in example app.
public sealed partial class MainPage : Page
{
private Dictionary<TimedTextSource, Uri> ttsMap = new Dictionary<TimedTextSource, Uri>();
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var source = MediaSource.CreateFromUri(new Uri("https://mediaplatstorage1.blob.core.windows.net/windows-universal-samples-media/elephantsdream-clip-h264_sd-aac_eng-aac_spa-aac_eng_commentary.mp4"));
var ttsEnUri = new Uri("ms-appx:///Assets/Media/ElephantsDream-Clip-SRT_en.srt");
var ttsEn = TimedTextSource.CreateFromUri(ttsEnUri);
ttsMap[ttsEn] = ttsEnUri;
var ttsPtUri = new Uri("ms-appx:///Assets/Media/ElephantsDream-Clip-SRT_pt.srt");
var ttsPt = TimedTextSource.CreateFromUri(ttsPtUri);
ttsMap[ttsPt] = ttsPtUri;
var ttsSvUri = new Uri("ms-appx:///Assets/Media/ElephantsDream-Clip-SRT_sv.srt");
var ttsSv = TimedTextSource.CreateFromUri(ttsSvUri);
ttsMap[ttsSv] = ttsSvUri;
ttsEn.Resolved += Tts_Resolved;
ttsPt.Resolved += Tts_Resolved;
ttsSv.Resolved += Tts_Resolved;
source.ExternalTimedTextSources.Add(ttsEn);
source.ExternalTimedTextSources.Add(ttsPt);
source.ExternalTimedTextSources.Add(ttsSv);
var playbackItem = new MediaPlaybackItem(source);
playbackItem.TimedMetadataTracksChanged += (item, args) =>
{
playbackItem.TimedMetadataTracks.SetPresentationMode(0, TimedMetadataTrackPresentationMode.PlatformPresented);
};
mediaPlayerElement.Source = playbackItem;
}
private void Tts_Resolved(TimedTextSource sender, TimedTextSourceResolveResultEventArgs args)
{
var ttsUri = ttsMap[sender];
if (args.Error != null)
{
return;
}
var ttsUriString = ttsUri.AbsoluteUri;
if (ttsUriString.Contains("_en"))
args.Tracks[0].Label = "English";
else if (ttsUriString.Contains("_pt"))
args.Tracks[0].Label = "Portuguese";
else if (ttsUriString.Contains("_sv"))
args.Tracks[0].Label = "Swedish";
}
}
}
You have to add the timed text source to ExternalTimedTextSources collection of the MediaSource:
var source = MediaSource.CreateFromUri(mediaUri);
var timedTextSource = TimedTextSource.CreateFromUri(timedTextUri);
source.ExternalTimedTextSources.Add(timedTextSource);
For full working example see this Windows Universal sample on GitHub.

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

Convert SurfaceImageSource to PNG

I have an image SurfaceImageSource and I would convert it to PNG.
I tried to use this project:
link
I tried to use SharpDX library, but I did not succeed.
private void initialize()
{
StorageFolder folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("folder", CreationCollisionOption.OpenIfExists);
StorageFile imagePng = await folder.CreateFileAsync("file.png", CreationCollisionOption.ReplaceExisting);
if (imagePng != null)
{
//surfaceImageSource to PNG method
surfaceToPng(surfaceImage,imagePng);
}
}
private void surfaceToPng(SurfaceImageSource surface,StorageFile imagePng){
IRandomAccessStream stream = await imagePng.OpenAsync(FileAccessMode.ReadWrite);
//.....//
}
The sample you linked is about "How to save SurfaceImageSource target as an image in Universal app" which is just what you want. It create a C++ Windows Runtime Component named "MyImageSourceComponent" and provide a sealed class named "MyImageSource" which contains the method public void SaveSurfaceImageToFile(IRandomAccessStream randomAccessStream); You can call this method to save the SurfaceImageSource to png.
uint imageWidth;
uint imageHeight;
MyImageSource myImageSource;
public MainPage()
{
this.InitializeComponent();
imageWidth = (uint)this.MyImage.Width;
imageHeight = (uint)this.MyImage.Height;
myImageSource = new MyImageSource(imageWidth, imageHeight, true);
this.MyImage.Source = myImageSource;
}
private async void btnSave_Click(object sender, RoutedEventArgs e)
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.FileTypeChoices.Add("Png file", new List<string>() { ".png" });
savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
StorageFile file = await savePicker.PickSaveFileAsync();
if (file != null)
{
IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite);
myImageSource.SaveSurfaceImageToFile(stream);
}
}
Although this sample is for Windows 8.1, it also should be able work with uwp app. I helped convert the sample into uwp app here you can reference. I created a new uwp app with a windows runtime component and reference the code of the "MyImageSouceComponent" in the sample. Then add the runtime component as reference to the uwp project. At last use the above code call the SaveSurfaceImageToFile method.

Error in async method

In WinRT app I have one FlipView myFlipView with some pictures and one Image myImage. On myFlipView's event SelectionChanged there is the following method:
async private void myFlipView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (myFlipView == null) return;
Uri newUri = new Uri("ms-appx://" + (((BitmapImage)(((Image)(((ContentControl)(myFlipView.SelectedItem)).Content)).Source)).UriSource.AbsolutePath));
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(newUri);
WriteableBitmap wb = new WriteableBitmap(1, 1);
if (file != null)
{
using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
await wb.SetSourceAsync(fileStream);
}
}
wb = ModifyPicture(wb);
myImage.Source = wb;
}
To sum up it finds uri of current image in myFlipView and set that image in myImage but with some modifications defined in ModifyPicture. It works perfectly on tablets but on computers with mouses there is one error. When I click arrows attached to FlipView very fast then myImage shows wrong picture. For example if in myFlipView I have 10 pictures (p1, p2, ..., p10) and currently p1 is chosen, when I change to p2 on myImage also p2 appears. But when I click very fast sometimes in FlipView I have for example p9 and in myImage p8. I suppose it is connected with fact that method is called many times but I don't know how to fix it. Thank you in advance for help :)
You should probably save the Task/IAsyncOperation that's already running and cancel it if the event handler is called again before it completes.
See this article on how to cancel running tasks.
Pseudo-code (as I don't know C#):
Task loadAndSetImage(uri) {
return new Task...
}
flipView_SelectionChanged {
if (myFlipView == null) return;
if (this.runningTask && !this.runningTask.IsCanceled) {
this.runningTask.Cancel();
}
Uri newUri = new Uri("ms-appx://" + (((BitmapImage)(((Image)(((ContentControl)(myFlipView.SelectedItem)).Content)).Source)).UriSource.AbsolutePath));
this.runningTask = loadAndSetImage(newUri);
this.runningTask.ContinueWith( (t) => this.runningTask = null; );
}
In addition to or instead of cancelling internal tasks as ma_il mentions - you could break/cancel your async method execution if you detect that it should be canceled. E.g.
private int myFlipView_SelectionChangedCallId;
async private void myFlipView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (myFlipView == null) return;
var callId = ++myFlipView_SelectionChangedCallId;
Uri newUri = new Uri("ms-appx://" + (((BitmapImage)(((Image)(((ContentControl)(myFlipView.SelectedItem)).Content)).Source)).UriSource.AbsolutePath));
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(newUri);
if (callId != myFlipView_SelectionChangedCallId) return;
WriteableBitmap wb = new WriteableBitmap(1, 1);
if (file != null)
{
using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
await wb.SetSourceAsync(fileStream);
if (callId != myFlipView_SelectionChangedCallId) return;
}
}
wb = ModifyPicture(wb);
myImage.Source = wb;
}
Also if your ModifyPicture method does any heavy pixel processing - you would want to run it on a background thread and await it.

Categories

Resources