Xaml Image.Source is not updating in C# - c#

I have some trouble with updating the source of an Image in Xaml. Im making a Windows Store App, and Im trying to set the source in my C# code. Basically, what my small program is doing is to let the user select a JPG file, and then copy this over to AppData folder. In my App, I want the picture the user have uploaded to show. Everything is working except the part where I show the image, this image does not want to change even if I provide a new source.
C# Code:
public sealed partial class MainPage : Page
{
FileOpenPicker pickerSelect;
FileSavePicker pickerSave;
StorageFolder folder;
StorageFile pic;
public MainPage()
{
this.InitializeComponent();
InitializePickers();
InitializeProfilePicture();
}
private async void InitializeProfilePicture()
{
folder = Windows.Storage.ApplicationData.Current.LocalFolder;
pic = await folder.GetFileAsync("profile.jpg");
BitmapImage uri = new BitmapImage(new Uri(pic.Path, UriKind.Absolute));
ProfilePic.Source = uri;
}
private void InitializePickers()
{
pickerSelect = new FileOpenPicker();
pickerSave = new FileSavePicker();
pickerSelect.FileTypeFilter.Add(".jpg");
}
private async void Upload_Click(object sender, RoutedEventArgs e)
{
StorageFile pictureSelect = await pickerSelect.PickSingleFileAsync();
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
await pictureSelect.CopyAsync(folder, "profile.jpg", NameCollisionOption.ReplaceExisting);
InitializeProfilePicture();
}
}
In the method "InitializeProfilePicture" I create a new BitmapImage and I set ProfilePic to this. This code is just working once, if I run the InitializeProfilePicture in the start as I do now, and the user selects a new picture and uploads to the AppData folder, the image does not change (even though the picture is indeed uploaded). If I remove the method from the start and just keep it in the Button_Click method, the new uploaded picture will be the one showing. But uploading a new picture after ProfilePic have set it's source, it will not change.
Image in Xaml is just like this
Image Width="480" Height="640" x:Name="ProfilePic" Grid.Row="1" Grid.RowSpan="2"
.. And there is also a button here to run Upload_Click method, but thats it.
Any idea why this is happening?? Should it not be updated??

You could load a BitmapImage directly from file like this:
var imageFile = await picker.PickSingleFileAsync();
var bitmap = new BitmapImage();
using (var stream = await imageFile.OpenReadAsync())
{
await bitmap.SetSourceAsync(stream);
}
ProfilePic.Source = bitmap;

Related

Get Image source from Image after uploading

I am currently following walk through on how to upload an image from a phone's photo gallery link here to allow users to upload an image to my application which they can set as their profile picture. I am able to get an image from the phone's gallery however when I try and save, I cannot retrieve the image source from the xaml.
Below is the xaml for the image and the button that the user clicks on to upload an image.
<Button Text="Pick a Profile Image"
Clicked="OnPictureButton_Clicked"
Grid.Column="0"></Button>
<Image Source="{Binding Employee.ProfilePicture}"
Grid.Column="1"
x:Name="profilePicture"
Grid.RowSpan="2"
WidthRequest="200"></Image>
Here is the corresponding c# code:
private async void OnPictureButton_Clicked(object sender, EventArgs e)
{
(sender as Button).IsEnabled = false;
// _stream is a private global variable
// Allow the user to view images on the phone
_stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamASync();
// If they select an image, set it as the source for profilePicture
if (_stream != null)
{
profilePicture.Source = ImageSource.FromStream(() => _stream);
}
(sender as Button).IsEnabled = true;
}
private async void Clicked_EmployeeSaved(object sender, EventArgs e)
{
var data = (EmployeeFull)BindingContext;
var uploadedPicture = profilePicture.Source; // Should be uploaded image
// Testing how to get the source for the image (Can disregard for question)
Bitmap bitmap = BitmapFactory.DecodeStream(_stream);
MemoryStream ms = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, ms);
byte[] byteArray;
byteArray = ms.ToArray();
}
Now, I know that once the user selects an image from the gallery on their device, the stream closes and so I will be unable to access it later on in the code, like I have tried in the second function shown.
However, I am unable to retrieve the name of the image that I have selected to upload. On the screen, the user is able to see this image as I have set the selected image as the source for the profilePicture tag but when I try and get that source when the user clicks 'save', it shows an ImageSource` object, not a string which I expected.
Is there another way I can get the uploaded image's name?
If you are sure the ImageSource is a stream, you can use a cast ImageSource to StreamImageSource, and then get the stream from that.
Example:
var imageStreamSource = (StreamImageSource)profilePicture.Source;
Stream actualStream = await imageStreamSource.Stream(new CancellationToken());

UI freezes at the moment Image control loads BitmapImage [duplicate]

This question already has an answer here:
Load a large BitmapImage asynchronously
(1 answer)
Closed 2 years ago.
My WPF MVVM application loads an image from the given URL asynchronously, through Webclient.DownloadFileAsync(url, fileLocation). That process goes fine and smooth, no freezes at all when downloading a picture. But the problem occurs when I present the image file to the user - an application becomes unresponsive.
After file is downloaded, I assign the image file to the BitmapImage:
public async void LoadFileToBitmapImage(string filePath)
{
_isDownloading = false;
await FileToBitmapImage(filePath);
}
public Task FileToBitmapImage(string filePath)
{
return Task.Run(() =>
{
var executableLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var imageLocation = Path.Combine(executableLocation, filePath);
var bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(imageLocation);
bi.EndInit();
bi.Freeze();
Image = bi;
});
}
Image.cs:
private BitmapImage _image;
public BitmapImage Image
{
get => _image;
set
{
_image = value;
NotifyOfPropertyChange("Image");
}
}
XAML Image Binding:
<Image Source="{Binding Image, IsAsync=True}" Margin="3"/>
The problem occurs when the image is downloaded and presenting it to the user. The bigger an image, the more time it takes to present an image to the user and the more time an application is unresponsive.
I tried clicking pause at that very time when the application freezes to check threads and get the following info and unfortunately it doesn't provide me with any information.
Any help will be much appreciated!
Edit
Worth noting that application becomes unresponsive after PropertyChanged event is raised, not before. Maybe it's something to do with rendering an image to the UI?
first, if you save the image, change the binding to a string/uri directly, no BitmapImage, no nned to create it, Wpf handle that for you
public BitmapImage Image ===> public Uri Image
and remove FileToBitmapImage.
I spent a few days to find a simple solution to this problem. I needed to display over a hundred images in high quality without UI freezing.
I tried various modifications of the binding and so on in the end only the creation of the Image control through the code and set of the Source property worked before Image appeared in the tree of interface elements.
In XAML only empty ContentControl:
<ContentControl x:Name="ImageContent"/>
In code:
static readonly ConcurrentExclusiveSchedulerPair _pair = new ConcurrentExclusiveSchedulerPair();
// Works for very big images
public void LoadImage(Uri imageUri)
{
var image = new System.Windows.Controls.Image(); // On UI thread
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
Task.Factory.StartNew(() =>
{
var source = new BitmapImage(imageUri); // load ImageSource
Dispatcher.RunOnMainThread(() =>
{
image.Source = source; // Set source while not in UI
// Add image in UI tree
ImageContent.Content = image; // ImageContent is empty ContentControl
ImageContent.InvalidateVisual();
});
}, default, TaskCreationOptions.LongRunning, _pair.ExclusiveScheduler);
}
Works better with loading image with CacheOption OnLoad.
public static ImageSource BitmapFromUri(Uri source)
{
if (source == null)
return new BitmapImage(source);
using (var fs = new FileStream(source.LocalPath, FileMode.Open))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = fs;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
}

How to get dragover image from webView to Bitmap (UWP C#)

I want to drag&drop image on website to Canvas on Xaml.
But it is not acceptable, because dragged data is not image data but html data.
So I try to get dragover thumbnail image.
But I don't know how to access dragover thumbnail image.
How can I access dragover thumbnail image. I want to know those code. C#.
--Xaml--
<WebView x:Name="WebView" Source="https://google.com" ScriptNotify="Notify" NavigationCompleted="Completed" ></WebView>
<Canvas x:Name="Board" AllowDrop="True" DragOver="dragOver" Drop="drop" Background="White"></Canvas>
--C#--
private void dragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
e.DragUIOverride.IsContentVisible = true; // I want to get this content data. To bitmap.
}
You can use the following code to get the BitmapImage from DragEventArgs
if (e.DataView.Contains(StandardDataFormats.Bitmap))
{
try
{
var a = await e.DataView.GetBitmapAsync();
var c = await a.OpenReadAsync();
BitmapImage b = new BitmapImage();
b.SetSource(c);
MyCanvasImage.Source = b;
}
catch (Exception) { }
}

Windows phone 8.1 image loading on UI

We are developing an app which have to attach an image at the runtime and display the same to the UI. We are using following set of code file for it:
private void Btn_Attach_File_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.FileTypeFilter.Add(".jpeg");
fileOpenPicker.FileTypeFilter.Add(".png");
fileOpenPicker.ContinuationData["Operate"] = "OpenImage";
fileOpenPicker.PickSingleFileAndContinue();
}
Also, to continue the app after picking file from storage, here is the code :
public async void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args)
{
if ((args.Files != null && args.Files.Count > 0))
{
IReadOnlyList<StorageFile> files = args.Files;
Image myImage = new Image();
foreach (StorageFile file in files)
{
if (args.ContinuationData["Operate"] as string == "OpenImage" && args.Files != null && args.Files.Count > 0)
{
IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
System.Diagnostics.Debug.WriteLine(file.Name);
System.Diagnostics.Debug.WriteLine(file.Path);
using (StorageItemThumbnail thumbnail = await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 190)) //, ThumbnailOptions.UseCurrentScale);
{
if (thumbnail != null && thumbnail.Type == ThumbnailType.Image)
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(thumbnail);
myImage.Source = bitmapImage;
}
}
}
}
}
}
Above code is in xaml.cs file.
But the problem is, even though I'm loading the file, there is no attachment on UI.
Do we need to make any changes to the corresponding xaml file? we've searched a lot on it but couldn't find any solution.
Thanks in advance,
First, make an image control in xaml:
<Image Name="img"/>
In codebehind after the loop:
this.img.Source = myImage.Source;
//For better perf according to the docs, specify this
//this.img.Source.DecodePixelWidth = 200
Certainly it's possible to make a binding instead of using the Name property, but this is the easiest way.
Read the docs: MSDN
One other thing, the loop foreach (StorageFile file in files) isn't that useful if you have one file, and if you have more than one file it will take the last file.
Feel free to ask for more info!
Happy coding :)!

Display loading while Caching image from URL

I have a code in WPF C# that i use to load images from the web as such:
if (myImgURL != "")
{
var imgBitmap = new BitmapImage();
imgBitmap.BeginInit();
imgBitmap.UriSource = new Uri(myImgURL, UriKind.RelativeOrAbsolute);
imgBitmap.CacheOption = BitmapCacheOption.OnLoad;
imgBitmap.EndInit();
myImgControl.Source = imgBitmap;
}
It works perfectly but sometimes it takes a while before the images get displayed (if internet is slow and all). How can I have a ProgressRing (from the Mahapps.Metro toolkit) display and be enabled while the image loads and then disappear when the image is displayed?
I am not aware of any event trigger for when an image is being downloaded and when it is fully loaded.
Take a look at the following events in class BitmapSource (the base class of BitmapImage):
DownloadProgress
DownloadCompleted
DownloadFailed
And just a note. You are creating a BitmapImage from an Uri and showing it immediately. Hence there is no need to set BitmapCacheOption.OnLoad (which afaik is necessary only if you load from a stream that should be closed immediately after EndInit). So you could shorten your code like this:
if (!string.IsNullOrEmpty(myImgURL))
{
var imgBitmap = new BitmapImage(new Uri(myImgURL));
myImgControl.Source = imgBitmap;
if (imgBitmap.IsDownloading)
{
// start download animation here
imgBitmap.DownloadCompleted += (o, e) =>
{
// stop download animation here
};
imgBitmap.DownloadFailed += (o, e) =>
{
// stop download animation here
};
}

Categories