I'm building a simple application that shows an image bitmap using the Image class in UWP.
When i change something on the image, the program won't refresh the image displayed.
I've tried to change source using another temp image but the problem is still the same.
This is the object in the XAML file
<Image x:Name="image" HorizontalAlignment="Left" Height="128" VerticalAlignment="Top" Width="128" AutomationProperties.AccessibilityView="Raw" ManipulationMode="All"/>
This is the code that changes the source of the image
private void ChangeImage_BTN_Click(object sender, RoutedEventArgs e)
{
readImage();
}
private void readImage()
{
switch (nimg)
{
case 1:
image.Source = new BitmapImage(new Uri("ms-appx:///Assets/France.bmp"));
nimg = 3;
break;
case 2:
//image.Source = new BitmapImage(new Uri("ms-appx:///Assets/Inghilterra.bmp"));
//nimg = 3;
break;
case 3:
image.Source = new BitmapImage(new Uri("ms-appx:///Assets/Area24_128x128.bmp"));
nimg = 1;
break;
}
}
When i switch the image Area_128x128.bmp to France.bmp, I modify the bitmap file adding some draw, switching again to Area_128x128.bmp the image has the old things.
How can i see the image properly?
The "ms-appx:///" prefix in the URI refers to the app's install directory. It turns out to be not supported update edits of files at runtime.
For your user case, there are some workarounds you can have a try.
You can use ApplicationData.RoamingFolder like this:
Windows.Storage.StorageFolder roamingFolder = Windows.Storage.ApplicationData.Current.RoamingFolder;
var file = await roamingFolder.GetFileAsync("France.bmp");
using (var fileStream = (await file.OpenAsync(Windows.Storage.FileAccessMode.Read)))
{
var bitImg = new BitmapImage();
bitImg.SetSource(fileStream);
image.Source = bitImg;
}
You can use KnownFolders.PicturesLibrary like this:
var file = await KnownFolders.PicturesLibrary.GetFileAsync("France.bmp");
using (var fileStream = (await file.OpenAsync(Windows.Storage.FileAccessMode.Read)))
{
var bitImg = new BitmapImage();
bitImg.SetSource(fileStream);
image.Source = bitImg;
}
In general, the path of KnownFolders.PicturesLibrary is C:\Users\[YOUR USER NAME]\Pictures.
For more information, you can reference Store and retrieve settings and File access permissions.
Related
Here is the problem.
I have a view, that should display an image and some controls.
User add new images, changes some options and click "finish".
Images are large and very large (400-1500 MB Tiff)
User should see the preview of image, but it is ok if it loading for 10-15 sec or even more, he have a job for this time.
Image is binding through MVVM pattern like simple string (file will be always in local folder)
<Image Name="ImagePreview" Source="{Binding SFilePathForPreview,
FallbackValue={StaticResource DefaultImage},
TargetNullValue={StaticResource DefaultImage}}"
HorizontalAlignment="Center" Width="200" Height="200"
VerticalAlignment="Center" />
Problem is that all is hangs when user try to add a file for loading time.
I understand that this case should be solved through multithreading - but have no idea how to implement this.
I tryed to update image from view in different thread like this:
Thread newThread = new Thread(LazyLoad);
newThread.Name = "LazyLoad";
newThread.Start(SFilePathForPreview);
public void LazyLoad(object SFilePath)
{
try
{
string path = (string)SFilePath;
BitmapImage t_source = new BitmapImage();
t_source.BeginInit();
t_source.UriSource = new Uri(path);
t_source.DecodePixelWidth = 200;
t_source.EndInit();
t_source.Freeze();
this.Dispatcher.Invoke(new Action(delegate
{
ImagePreview.Source = t_source;
}));
}
catch
{
//...
}
}
But anyway at point
ImagePreview.Source = t_source;
everything hangs up until image fully loaded.
Is there a way to load a preview in the background and show it without those terrible hangs?
The probably most simple way of asynchronously loading an image is via an asynchronous Binding. You would not have to deal with Threads or Tasks at all.
<Image Source="{Binding Image, IsAsync=True}"/>
A possible view model could look like shown below, where you must make sure that the Image property getter can be called from a background thread.
public class ViewModel : ViewModelBase
{
private string imagePath;
private BitmapImage image;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
image = null;
OnPropertyChanged(nameof(ImagePath));
OnPropertyChanged(nameof(Image));
}
}
public BitmapImage Image
{
get
{
lock (this)
{
if (image == null &&
!string.IsNullOrEmpty(imagePath) &&
File.Exists(imagePath))
{
using (var stream = File.OpenRead(imagePath))
{
image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.DecodePixelWidth = 200;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
}
}
}
return image;
}
}
}
As you already mentioned, you are blocking the UI thread with the image load. You can use a WriteableBitmap class instance as the source for your Image. This will let you load the image on a background thread or async task. Here is a quick guide (not mine) on the issue.
https://www.i-programmer.info/programming/wpf-workings/527-writeablebitmap.html
Another option would be using priortybinding with the highest priorty to the full image and a lower priority to the faster-loading preview image. MS has documented priority binding here:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-prioritybinding
I have a GridView in my app that basically contains images as following:
<GridView x:Name="ImageGridView">
<Image x:Name="Image_1"/>
<Image x:Name="Image_2"/>
<Image x:Name="Image_3"/>
</GridView>
with this in codebehind for the images to Load:
public void SetImages()
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Image_1.Source= new BitmapImage(new Uri(localFolder.Path + "/Image_1.png"));
Image_1.Source= new BitmapImage(new Uri(localFolder.Path + "/Image_2.png"));
Image_1.Source= new BitmapImage(new Uri(localFolder.Path + "/Image_3.png"));
}
The GridView shows the images just fine. However, I also have a Class that changes the colors of those Pictures based on existing values as fallowing:
This means that the Images sources don't change, and the paths to those images also remain as it is. The only thing changing are the colors in those images as demonstrated in the pictures.
However, when the colors-changing task is finished and the images are updated in the App's LocalState folder, they are not updated in the GridView images.
I have also tried creating an observable collection with the images in question. Binded the GridView to this collection, and at each color update I would include this:
GridView.ItemsSource = null;
GridView.ItemsSource = ImagesCollection;
It would work by changing some Images but not others. this happens randomly. If there it a way to await the first before executing the second, this might work out!
The question is: how to I ask the GridView to reload its content? Update its content? clear cache and re-fetch the Images?
Put this code into a method:
public void SetImages()
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Image_1.Source= new BitmapImage(new Uri(localFolder.Path + "/Image_1.png"));
Image_2.Source= new BitmapImage(new Uri(localFolder.Path + "/Image_2.png"));
Image_3.Source= new BitmapImage(new Uri(localFolder.Path + "/Image_3.png"));
}
Call this method once at the beginning and when you modified the images.
Don't be afraid to use MVVM. In your xaml bind the image sources to properties in your viewmodel.
XAML:
<GridView>
<Image Source="{Binding ImageSource1}"/>
<Image Source="{Binding ImageSource2}"/>
</GridView>
ViewModel:
public string ImageSource1 => Windows.Storage.ApplicationData.Current.LocalFolder + "/Image_1.png";
public string ImageSource2 => Windows.Storage.ApplicationData.Current.LocalFolder + "/Image_2.png";
// invoke the NotifyPropertyChanged event to enforce an update.
public void UpdateImages() {
InvokePropertyChanged(nameof(ImageSource1));
InvokePropertyChanged(nameof(ImageSource2));
}
Well I found an indirect dirty way to do it. to update the images in the GridView.
I started with clearing the ObservableCollection that the GridView binded to:
ThumnailsCollection.Clear();
Navigate to the folder where the thumbnails are saved to. each type of thumbnails are grouped in one folder. each time the color is changed where the thumbnails for it does not exist, new ones are generated. otherwise, it recalls the ones already on hand:
StorageFolder localFolder =
Windows.Storage.ApplicationData.Current.LocalFolder;
//Check if the main folder where the images are saved exist
IStorageItem storageItem = await localFolder.TryGetItemAsync("Thumnails");
if (storageItem != null)
{
//If it does, look into the subfolders. each image group of the same
//type and different colors are saved to tier own folder
StorageFolder ImagesFolder =
await localFolder.GetFolderAsync("Thumnails");
var queryOption =
new QueryOptions { FolderDepth = FolderDepth.Deep };
var flatWallpaperSubFolders =
await ImagesFolder .CreateFolderQueryWithOptions(queryOption).GetFoldersAsync();
foreach (StorageFolder subFolder in subFolders)
{
IStorageItem thumnail =
await subFolder.TryGetItemAsync($"{AlphaColorID} - {BetaColorID}.png");
if (thumnail != null)
{
//in each of the folders, look if the needed image
//exist or not
//If it does, add it to the ObservableCollection
BitmapImage icon =
new BitmapImage(new Uri(subFolder.Path + $"/{AlphaColorID} - {BetaColorID}.png"));
ThumnailsCollection.Add(new ThumnailsBitmap(icon));
}
else
{
//If the Image with the requested colors does not exist
//Render a new image with the needed colors
//the rendered image is added to the same subfolder with the other
// similar images
await ApplyColor(new Uri($"ms-appx:///Images/{subFolder.Name}-Thumnail.png"), $"{AlphaColor} - {BetaColor}.png", $"{subFolder.Name}");
BitmapImage icon =
new BitmapImage(new Uri(flatWallpaperSubFolder.Path + $"/{AlphaColor} - {BetaColor}.png"));
ThumnailsCollection.Add(new ThumnailsBitmap(icon));
}
}
}
else
{
//when run for the first time, all folders and images are missing
//through ApplyColor task, all Images are rendered and folders created
await ApplyColor(new Uri("ms-appx:///Images/Number-1-Thumnail.png"), $"{AlphaColor} - {BetaColor}.png", "Number-1");
await ApplyColor(new Uri("ms-appx:///ModernWallpaperGrayscale/Number-2-Thumnail.png"), $"{AlphaColor} - {BetaColor}.png", "Number-2");
//All needed to do is to all of those Images to the
//ObservableCollection
StorageFolder ImagesFolder = await localFolder.GetFolderAsync("Thumnails");
var queryOption =
new QueryOptions { FolderDepth = FolderDepth.Deep };
var flatWallpaperSubFolders =
await ImagesFolder.CreateFolderQueryWithOptions(queryOption).GetFoldersAsync();
foreach (StorageFolder subFolder in subFolders)
{
BitmapImage icon =
new BitmapImage(new Uri(subFolder.Path + $"/{AlphaColor} - {BetaColor}.png"));
ThumnailsCollection.Add(new ThumnailsBitmap(icon));
}
}
I want to display an image retrieved from a scanner in a picturebox. So far I have been able to do it saving the image to a temporary file:
private void btnScan_Click(object sender, EventArgs e)
{
var fn = System.IO.Path.GetTempFileName();
System.IO.File.Delete(fn);
var image = Scanner.Scan();
image.SaveFile(fn);
_ScannedImage = Bitmap.FromFile(fn);
pictureBoxMain.Image = _ScannedImage;
}
Is there a way to convert the image data in the ImageFile class avoiding to save it to disk?
As per #C. Colin comment, theres' a way to get the bitbitmap from the ImageFile using a memory stream:
var imageBytes = (byte[])image.FileData.get_BinaryData();
var ms = new MemoryStream(imageBytes);
var img = Image.FromStream(ms);
Load a picturebox from a WIA ImageFile?
You can convert the ImageFile to Image, and after that create a Bitmap object:
var myImage = (Image)imageFile;
Bitmap bmp = new Bitmap(myImage);
Therefore you don't have to save (as you requested) in path.
Please tell me if this works for you.
See this post about converting from ImageFile to Image:
Load a picturebox from a WIA ImageFile?
I'm developing my first Windows 10 UWP app. I have an image. This is its XAML code:
<Image x:Name="image"
HorizontalAlignment="Left"
Height="50"
Margin="280,0,0,25"
VerticalAlignment="Bottom"
Width="50"
Source="Assets/Five.png"/>
And im trying to change image.source with this code:
private void slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
BitmapImage One = new BitmapImage(new Uri(#"Assets/One.png"));
BitmapImage Two = new BitmapImage(new Uri(#"Assets/Two.png"));
BitmapImage Three = new BitmapImage(new Uri(#"Assets/Three.png"));
BitmapImage Four = new BitmapImage(new Uri(#"Assets/Four.png"));
BitmapImage Five = new BitmapImage(new Uri(#"Assets/Five.png"));
if (slider.Value == 1)
{
image.Source = One;
}
else if (slider.Value == 2)
{
image.Source = Two;
}
else if (slider.Value == 3)
{
image.Source = Three;
}
else if (slider.Value == 4)
{
image.Source = Four;
}
else if (slider.Value == 5)
{
image.Source = Five;
}
}
But when I compile the code I get this error pointing to the variable declaration:
UriFormatException was unhandled by user code
Windows Runtime APIs do not support URIs of type UriKind.Relative, so you typically use the signature that infers the UriKind and make sure you've specified a valid absolute URI including the scheme and authority.
To access files stored inside the application package, but from code where there is no inferred root authority, specify the ms-appx: scheme like following:
BitmapImage One = new BitmapImage(new Uri("ms-appx:///Assets/One.png"));
For more info, please see How to load file resources (XAML) and URI schemes.
You need to specify the additional UriKind parameter for each of your URI objects, so that they are defined as Relative e.g.
new Uri("Assets/One.png", UriKind.Relative)
I have an image control which is showing the preview image. If the user delete the image (which resides in folder) it should show the newly taken image.
but the image control shows the deleted image instead of showing new image.
// clear the image source before deleting the image.
// save image in the directory
public string globalFilePath;
int imageCount = Directory.GetFiles(imgDir, "*", SearchOption.TopDirectoryOnly).Length;
string filePath = Path.Combine(imgDir, "IMAGE_" + ++imageCount + ".jpeg");
globalFilePath = filePath;
// setting image control source
var strUri = new Uri(WebCamControl.globalFilePath, UriKind.Relative);
previewImage.Source = BitmapFromUri(strUri);
//Method
public static ImageSource BitmapFromUri(Uri source)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = source;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
return bitmap;
}
// delete image
previewImage.Source = null;
if (System.IO.File.Exists(WebCamControl.globalFilePath))
{
System.IO.File.Delete(WebCamControl.globalFilePath);
}
else
{
MessageBox.Show("File Not Exists");
}
After deleting the image in the directory file the image image control should show the new image, but my Image control shows the deleted image. please give me the solution.
In WPF, we generally don't need to use actual BitMapImage objects to display an Image. It's far easier to let the .NET Framework convert our string file paths to the images to the actual Image elements.
Also, it is far better to data bind the Image.Source to a property that implements the INotifyPropertyChanged interface (or a DepenedencyProperty):
<Image Source="{Binding YourImageFilePath}" ... />
Then, whenever a file path of a new image is set to the YourImageFilePath property, your displayed image will update immediately.
YourImageFilePath = filePath;
Try This one:
string path = ((BitmapImage)img.Source).UriSource.LocalPath;
img.SetValue(System.Windows.Controls.Image.SourceProperty, null);
File.Delete(path);
OR
string path = ((BitmapImage)img.Source).UriSource.LocalPath;
img.Source = null;
File.Delete(path)