Silverlight URI loading Image - silverlight

I am trying to load an Image in Silverlight, but i keep getting a null pointer exception when i assign it to this.Image1.Source. I've looked over similiar stack questions, but none of them solve the problem. My image file is in a folder right next to the xap file.
public MainPage()
{
Uri uri = new Uri("Content/button.png", UriKind.Relative);
var image = new BitmapImage { UriSource = new Uri("Content/button.png", UriKind.Relative) };
try
{
BitmapImage img = new BitmapImage(uri);
this.image1.Source = image;
}
catch (Exception e)
{
string problem = e.Message;
}
InitializeComponent();
}

Ok, i've found the problem, i was trying to assign image to image1.Source, but image1 was not yet created - it is created in the InitilizeComponent() function.

Related

Alternative to BitmapImage to load a PNG

On a WPF project, I need to set a ImageSource property from a local Resource.
I am trying to use the following code:
var bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri("pack://application:,,,/Resources/Identicons/no_user.jpg", UriKind.Absolute);
bmp.EndInit();
Avatar = bmp;
Where Avatar is defined before as:
private ImageSource myAvatar;
The problem is that BitmapImage does not support MetaData information, that the source image have (it was created with Paint.net) so it throws a error. The error is the following:
Metadata = 'bmp.Metadata' threw an exception of type
'System.NotSupportedException'
So I believe that I need alternatives to BitmapImage to properly load the desired image.
As a end note, using the same image inside xaml, directly in a "source" property it works fine.
Thank you
Unlike BitmapImage, BitmapFrame supports the Metadata property:
So you may replace
Avatar = new BitmapImage(new Uri(...));
by
Avatar = BitmapFrame.Create(new Uri(...));
From MSDN:
BitmapFrame provides additional functionality not defined by
BitmapSource ... BitmapFrame also supports the writing of metadata
information by using the Metadata property or the
CreateInPlaceBitmapMetadataWriter method.
As you have the path to the image it can be done by below using Image Control,
XAML
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding ImagePath}"></BitmapImage>
</Image.Source>
</Image>
VM
public ViewerViewModel()// Constructor
{
ImagePath = #"..\Images\Untitled.jpg";// Change the image path based on your input.
}
private string _ImagePath = string.Empty;
public string ImagePath {
get { return _ImagePath; }
set { _ImagePath = value; NotifyPropertyChanged(); }
}
Problem solved.
This was inside a Class with the property:
private ImageSource myAvatar;
....
public ImageSource Avatar
{
set { myAvatar = Avatar; }
get { return myAvatar; }
}
And I was trying to change the Avatar (that sets the myAvatar through the set).
I do not understand why, but changing directly the myAvatar works. For example doing:
BitmapSource image = BitmapFrame.Create(new Uri("pack://application:,,,/Resources/Identicons/no_user.jpg", UriKind.Absolute));
myAvatar = image;
Is ok, but:
BitmapSource image = BitmapFrame.Create(new Uri("pack://application:,,,/Resources/Identicons/no_user.jpg", UriKind.Absolute));
Avatar = image;
Always sets Avatar as null.
This is a method inside a class. I would appreciate to understand why as it is not clear to me. Thank you.

Image control gets stuck after internet disconnection

I have a image control in WPF window and its xmls is as follows:
<Image Source="{Binding Path=ImageUrl,Converter={StaticResource ImageSourceConverter}}"
Height="150" HorizontalAlignment="Left" Margin="100,15,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="200" />
At run time I change the source of Image by changing the value of bounded property. This code works fine in normal scenario. Issue appears only after internet is disconnected. If there is disconnection while downloading the image then image is not shown. Thats fine. But when internet comes back and image source is changed to another image url, image doesn't get downloaded. ImageConverter is not even called. After this point there is no way to display image in the control. Image control gets stuck.
public class ImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string url;
try
{
if (value is string)
url = (string)value;
else
url = ((Uri)value).AbsolutePath;
}
catch (Exception)
{
url = string.Empty;
return value;
}
BitmapImage src = new BitmapImage();
if (targetType == typeof(ImageSource))
{
if (value is string)
{
string str = (string)value;
src.BeginInit();
src.CacheOption = BitmapCacheOption.OnLoad;
src.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
src.UriSource = new Uri(str, UriKind.RelativeOrAbsolute);
src.EndInit();
return src;
}
else if (value is Uri)
{
Uri uri = (Uri)value;
return new BitmapImage(uri);
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Any help will be highly appreciated.
In article about memory leaks discusses leak caused by use of downloaded BitmapImage as Image Source[7]:
Quote: "This leak is triggered because WPF does not remove internal reference to certain objects (such as LateBoundBitmapDecoder, BitmapFrameDecode, etc) which are used during web download and causes the leak.
This leak only happens when you download an image from the internet. (E.g. it does not appear when you load images from your local machine)"
...
The Fix/Workaround
The workaround is to consider downloading the BitmapImage first in other means to a temporary folder or to memory and then use the local BitmapImage . (See WebClient.DownloadFile & WebClient.DownloadData APIs)
Maybe it you will approach.
I fixed issue by downloading the image first and if it succeeds then only assign it to Image control. Posting it here in case anyone needs it.
private bool ValidImage(string url, out BitmapImage image)
{
try
{
System.Net.WebRequest request = System.Net.WebRequest.Create(url);
System.Net.WebResponse response = request.GetResponse();
System.IO.Stream responseStream = response.GetResponseStream();
Bitmap bitmap = new Bitmap();
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
BitmapImage result = new BitmapImage();
result.BeginInit();
// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
// Force the bitmap to load right now so we can dispose the stream.
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
image = result;
}
}
catch (Exception ex)
{
logger.Error(ex, "error downlading image");
image = null;
return false;
}
return true;
}

Loading Images in background while not locking the files

My application loads a lot of images in a BackgroundWorker to stay usable. My Image control is bound to a property named "ImageSource". If this is null it's loaded in the background and raised again.
public ImageSource ImageSource
{
get
{
if (imageSource != null)
{
return imageSource;
}
if (!backgroundImageLoadWorker.IsBusy)
{
backgroundImageLoadWorker.DoWork += new DoWorkEventHandler(bw_DoWork);
backgroundImageLoadWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
backgroundImageLoadWorker.RunWorkerAsync();
}
return imageSource;
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
bitmap = new BitmapImage();
bitmap.BeginInit();
try
{
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.DecodePixelWidth = 300;
MemoryStream memoryStream = new MemoryStream();
byte[] fileContent = File.ReadAllBytes(imagePath);
memoryStream.Write(fileContent, 0, fileContent.Length);
memoryStream.Position = 0;
bitmap.StreamSource = memoryStream;
}
finally
{
bitmap.EndInit();
}
bitmap.Freeze();
e.Result = bitmap;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BitmapSource bitmap = e.Result as BitmapSource;
if (bitmap != null)
{
Dispatcher.CurrentDispatcher.BeginInvoke(
(ThreadStart)delegate()
{
imageSource = bitmap;
RaisePropertyChanged("ImageSource");
}, DispatcherPriority.Normal);
}
}
This is all well so far but my users can change the images in question. They choose a new image in an OpenDialog, the old image file is overwritten with the new and ImageSource is raised again which loads the new image with the same filename again:
public string ImagePath
{
get { return imagePath; }
set
{
imagePath= value;
imageSource = null;
RaisePropertyChanged("ImageSource");
}
}
On some systems the overwriting of the old file results in an exception:
"a generic error occured in GDI+" and "The process cannot access the file..."
I tried a lot of things like loading with BitmapCreateOptions.IgnoreImageCache and BitmapCacheOption.OnLoad. This raised Exceptions when loading them:
Key cannot be null.
Parameter name: key
If I try this without the BackgroundWorker on the UI thread it works fine. Am I doing something wrong? Isn't it possible to load the images in the background while keeping the files unlocked?
Well, it seems all of the above works. I simplified the example for the question and somehow on the way lost the problem.
The only difference I could see in my code is that the loading of the image itself was delegated to a specific image loader class which somehow created the problem. When I removed this dependency the errors disappeared.

WPF: Get specify image from touch

I was added picture as a children to layer called "canvas". By the following code:
if (addChild)
{
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri(path, UriKind.Absolute);
src.EndInit();
i.Source = src;
i.Width = 200;
i.IsManipulationEnabled = true;
double rotAngle = Rand.GetRandomDouble(-3.14/4, 3.14/4);
i.RenderTransform = new MatrixTransform(Math.Cos(rotAngle), -Math.Sin(rotAngle),
Math.Sin(rotAngle), Math.Cos(rotAngle), Rand.GetRandomDouble(0, this.Width - i.Width), Rand.GetRandomDouble(0, this.Height - i.Width));
canvasImages.Add(i);
canvas.Children.Add(i);
Canvas.SetZIndex(i, canvas.Children.Count-1);
addedFiles.Add(path);
maxZ++;
}
Here is the problem. I'm trying to make an event called "canvas_TouchDown" which can detect the specify picture when I touched it so that it will get the center of that image object.
List<Image> canvasImages = new List<Image>();
private void canvas_TouchDown(object sender, TouchEventArgs e)
{
foreach (Image canvasImage in canvasImages)
{
if (canvasImage.AreAnyTouchesCaptured == true)
{
System.Diagnostics.Debug.WriteLine("I found image that you touch");
}
}
}
However, there is nothing happened. I also try to use PersistId property but it doesn't work. Have any suggestion?
Regard,
C.Porawat
If you are adding the image to the canvas, touching it and expecting the canvas to receive the touch you will be disappointed. You should either listen to "touch down" on the image or "preview touch down" on the canvas.

WPF loading serialized image

In an app I need to serialize an image through a binarywriter, and to get it in an other app to display it.
Here is a part of my "serialization" code :
FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write);
BinaryWriter bin = new BinaryWriter(fs);
bin.Write((short)this.Animations.Count);
for (int i = 0; i < this.Animations.Count; i++)
{
MemoryStream stream = new MemoryStream();
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(Animations[i].Image));
encoder.Save(stream);
stream.Seek(0, SeekOrigin.Begin);
bin.Write((int)stream.GetBuffer().Length);
bin.Write(stream.GetBuffer());
stream.Close();
}
bin.Close();
And here is the part of my deserialization that load the image :
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader bin = new BinaryReader(fs);
int animCount = bin.ReadInt16();
int imageBytesLenght;
byte[] imageBytes;
BitmapSource img;
for (int i = 0; i < animCount; i++)
{
imageBytesLenght = bin.ReadInt32();
imageBytes = bin.ReadBytes(imageBytesLenght);
img = new BitmapImage();
MemoryStream stream = new MemoryStream(imageBytes);
BitmapDecoder dec = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
img = dec.Frames[0];
stream.Close();
}
bin.Close();
When I use this method, I load the image (it seems to be stored in the "img" object) but it can't be displayed.
Has somedy an idea?
Thanks
KiTe
UPD :
I already do this : updating my binding, or even trying to affect it directly through the window code behing. None of these approaches work :s
However, when I add this :
private void CreateFile(byte[] bytes)
{
FileStream fs = new FileStream(Environment.CurrentDirectory + "/" + "testeuh.png", FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
}
at the end of you function, it perfectly create the file, which can be read without any problems ... So I don't know where the problem can be.
UPD 2 :
A weird things happens.
Here is the binding I use :
<Image x:Name="imgSelectedAnim" Width="150" Height="150" Source="{Binding ElementName=lstAnims, Path=SelectedItem.Image}" Stretch="Uniform" />
(the list is itself binded on an observableCollection).
When I create manually the animation through the app, it works (the image is displayed).
But when I load it, it is not displayed (I look at my code, there are not any "new" which could break up my binding, since there are others properties binded the same way and they does not fail).
Moreover, I've put a breakpoint on the getter/setter of the properties Image of my animation.
When it is created, no problems, it has the good informations.
But when it is retreived through the getter, it return a good image the first time, and then and image with pixelWidth, pixelHeight, width, height of only 1 but without going through the setter anymore !
Any idea?
UPD3
tried what you said like this :
Added a property TEST in my viewModel :
private BitmapSource test;
public BitmapSource TEST { get { return test; } set { test = value; RaisePropertyChanged("TEST"); } }
then did it :
img = getBmpSrcFromBytes(bin.ReadBytes(imageBytesLenght));
TEST = img;
(in the code showed and modified before)
and my binding :
<Image x:Name="imgSelectedAnim" Width="150" Height="150" Source="{Binding Path=TEST}" Stretch="Uniform" />
(datacontext is set to my ViewModel)
And it still doesn't work and does the weird image modification (pixW, pixH, W and H set to 1)
FINAL UPD:
It seems I finally solved the problem. Here is simply what I did :
byte[] bytes = bin.ReadBytes(imageBytesLenght);
MemoryStream mem = new MemoryStream(bytes);
img = new BitmapImage();
img.BeginInit();
img.StreamSource = mem;
img.EndInit();
the strange thing is that it didn't work the first time, maybe it is due to an architectural modification within my spriteanimation class, but I don't think it is.
Well, thank you a lot to Eugene Cheverda for his help
At the first, I think you should store your images in a list of BitmapSource and provide reverse encoding of image for using stream as BitmapImage.StreamSource:
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader bin = new BinaryReader(fs);
int animCount = bin.ReadInt16();
int imageBytesLenght;
byte[] imageBytes;
List<BitmapSource> bitmaps = new List<BitmapSource>();
for (int i = 0; i < animCount; i++)
{
imageBytesLenght = bin.ReadInt32();
imageBytes = bin.ReadBytes(imageBytesLenght);
bitmaps.Add(getBmpSrcFromBytes(imageBytes));
}
bin.Close();
UPD
In code above use func written below:
private BitmapSource getBmpSrcFromBytes(byte[] bytes)
{
using (var srcStream = new MemoryStream(bytes))
{
var dec = new PngBitmapDecoder(srcStream, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(dec.Frames[0]);
BitmapImage bitmapImage = null;
bool isCreated;
try
{
using (var ms = new MemoryStream())
{
encoder.Save(ms);
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.EndInit();
isCreated = true;
}
}
catch
{
isCreated = false;
}
return isCreated ? bitmapImage : null;
}
}
Hope this helps.
UPD #2
Your binding is incorrect. You may be should bind selected item to for example CurrentImageSource. And then this CurrentImageSource bind to Image control.
Code:
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<BitmapSource> ImagesCollection { get; set; }
private BitmapSource _currentImage;
public BitmapSource CurrentImage
{
get { return _currentImage; }
set
{
_currentImage = value;
raiseOnPropertyChanged("CurrentImage");
}
}
private void raiseOnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then:
<ListBox ItemsSource="{Binding ImagesCollection}" SelectedItem="{Binding CurrentImage}"/>
<Image Source="{Binding CurrentImage}"/>
ADDED
Here is the sample project in which implemented technique as I described above.
It creates animations collection and represent them in listbox, selected animation is displayed in Image control using its Image property.
What I want to say is that if you cannot obtain image as it should be, you need to search problems in serialization/deserialization.

Resources