I can successfully load the following Bitmap like this and display it within an Image control on the view.
var bitmapImage = new BitmapImage
{
UriSource =
new Uri("../Images/Test.JPG", UriKind.Relative)
};
However as soon as I add this line to create a WriteableBitmap out of the bitmap,
var w = new WriteableBitmap(bitmapImage);
I get a Runtime error at the line above: "Object reference not set to an instance of an object."
It seems the BitmapImage creation is delayed, could that be? How should I fix this?
Update:
I am now trying this but the openImage seems never to be hit. (even without trying to make it synchronous, it still fails) What is wrong here?
var image = new BitmapImage();
image.ImageOpened += (sender, args) => resetEventBitmap.Set();
image.ImageFailed += (o, eventArgs) =>
{
resetEventBitmap.Set();
throw eventArgs.ErrorException;
};
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = uri;
resetEventBitmap.WaitOne();
Thanks,
Reference:
http://www.blog.ingenuitynow.net/Silverlight+Creating+A+WriteableBitmap+From+A+Uri+Source.aspx
Basically, bitmap image has a dependency property "CreateOptions" which, by default, is set to "DelayCreation". This causes the bitmap to be delayed for rendering until after it's needed. Hence, this causes our "object reference not set to an instance of an object" error. To fix this, we have to break the bitmap creation out of the writeablebitmap constructor, change this option, and then put it back in. In vb.net this looks like:
Dim tmpUri As New Uri(yourpath.ToString)
Dim bmp As New BitmapImage
bmp.CreateOptions = BitmapCreateOptions.None
bmp.UriSource = tmpUri
Dim wb As New WriteableBitmap(bmp)
BitmapImage _classField;
void LoadImageFunction()
{
_classField = new BitmapImage();
_classField.ImageOpened += new EventHandler<RoutedEventArgs>(bi_ImageOpened);
_classField.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(bi_ImageFailed);
//sorry.. totally forgot about order :)
_classField.UriSource = new Uri("../some/uri", UriKind.Relative);
}
void bi_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
//something has happend
throw e.ErrorException;
}
void bi_ImageOpened(object sender, RoutedEventArgs e)
{
//image is loaded.. now we can work with it..
var w = new WriteableBitmap(_classField);
}
img1 = new BitmapImage(new Uri("/PrjName;component/Images/image01.jpg", UriKind.RelativeOrAbsolute));
img2 = new BitmapImage(new Uri("/PrjName;component/Images/image02.jpg", UriKind.RelativeOrAbsolute));
img1.CreateOptions = BitmapCreateOptions.None;
img2.CreateOptions = BitmapCreateOptions.None;
img1.ImageOpened += new EventHandler<RoutedEventArgs>(img1_ImageOpened);
img2.ImageOpened += new EventHandler<RoutedEventArgs>(img2_ImageOpened);
void img2_ImageOpened(object sender, RoutedEventArgs e)
{
load2 = true;
}
void img1_ImageOpened(object sender, RoutedEventArgs e)
{
load1 = true;
}
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
while (!load1 && !load2)
{ }
WriteableBitmap x = new WriteableBitmap(img1);
WriteableBitmap y = new WriteableBitmap(img2);
}
This should work. it did for me..! It makes it a lil' complicated, but that's how it works!
Related
Made a new test project just for this, with a button and an image. MyImage.png is placed in the project's debug working directory, and NOT included in the project.
This works fine, with an absolute path specified:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
Uri fileUri = new Uri("C:/blah/blah/blah/MyImage.png");
BitmapImage myimage = new BitmapImage(fileUri);
Imagebox.Source = myimage;
}
This does NOT show any image, with a relative path:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
Uri fileUri = new Uri("MyImage.png", UriKind.Relative);
BitmapImage myimage = new BitmapImage(fileUri);
Imagebox.Source = myimage;
}
However, I noticed that the relative path DOES work if I set a breakpoint at the last line, examine myimage's properties (it holds data indicating MyImage.png was successfully found), then continue execution. At which point the image shows up. I'm using Visual Studio 2019 Community.
I'm very confused why this is happening.
That looks like a bug. A workaround would be to set BitmapCacheOption.OnLoad:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
var fileUri = new Uri("MyImage.png", UriKind.Relative);
var myImage = new BitmapImage();
myImage.BeginInit();
myImage.UriSource = fileUri;
myImage.CacheOption = BitmapCacheOption.OnLoad;
myImage.EndInit();
Imagebox.Source = myImage;
}
Or always use an absolute URI:
private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)
{
var fileUri = new Uri(Path.Combine(Environment.CurrentDirectory, "MyImage.jpg"));
var myImage = new BitmapImage(fileUri);
Imagebox.Source = myImage;
}
Here's my problem.
I'm loading a few BitmapImages in a BlockingCollection
public void blockingProducer(BitmapImage imgBSource)
{
if (!collection.IsAddingCompleted)
collection.Add(imgBSource);
}
the loading happens in a backgroungwork thread.
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
String filepath; int imgCount = 0;
for (int i = 1; i < 10; i++)
{
imgCount++;
filepath = "Snap";
filepath += imgCount;
filepath += ".bmp";
this.Dispatcher.BeginInvoke(new Action(() =>
{
label1.Content = "Snap" + imgCount + " loaded.";
}), DispatcherPriority.Normal);
BitmapImage imgSource = new BitmapImage();
imgSource.BeginInit();
imgSource.UriSource = new Uri(filepath, UriKind.Relative);
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
blockingProducer(imgSource);
}
}
debugging this part of the code everything looks okay, the problem comes now ...
after finishing loading the images I want to show them in UI one by one. I'm using a dispatcher to do so but I always get the message telling me that the called Thread can not access the object because it belongs to a different Thread.
public void display(BlockingCollection<BitmapImage> results)
{
foreach (BitmapImage item in collection.GetConsumingEnumerable())
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
this.dstSource.Source = item;
Thread.Sleep(250);
}), DispatcherPriority.Background);
}
}
debug accuses that the error is here
this.dstSource.Source = item;
I'm trying everything but cant find out what’s wrong. Anyone has any idea?
You have to call Freeze after loading the images in order to make them accessible to other threads:
BitmapImage imgSource = new BitmapImage();
imgSource.BeginInit();
imgSource.UriSource = new Uri(filepath, UriKind.Relative);
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
imgSource.Freeze(); // here
As far as I have understood the BitmapCacheOption.OnLoad flag, it is only effective when a BitmapImage is loaded from a stream. The Remarks section in BitmapCacheOption says:
Set the CacheOption to BitmapCacheOption.OnLoad if you wish to close a
stream used to create the BitmapImage. The default OnDemand cache
option retains access to the stream until the image is needed, and
cleanup is handled by the garbage collector.
A BitmapImage created from a Uri may be loaded asynchronously (see the IsDownloading property). Consequently, Freeze may not be callable on such a BitmapImage, as downloading may still be in progress after EndInit. I guess it nevertheless works in your case because you are loading BitmapImages from file Uris, which seems to be done immediately.
To avoid this potential problem you may just create the BitmapImage from a FileStream:
var imgSource = new BitmapImage();
using (var stream = new FileStream(filepath, FileMode.Open))
{
imgSource.BeginInit();
imgSource.StreamSource = stream;
imgSource.CacheOption = BitmapCacheOption.OnLoad;
imgSource.EndInit();
imgSource.Freeze();
}
For the further future readers, here is the code I used to fix my problem.
public void display(BlockingCollection<BitmapImage> collection)
{
if (collection.IsCompleted || collection.Count != 0)
{
BitmapImage item = collection.Take();
this.Dispatcher.BeginInvoke(new Action(() =>
{
this.dstSource.Source = item;
}), DispatcherPriority.Normal);
}
else
{
dispatcherTimer.Stop();
}
}
public void dispatcherTimer_Tick(object sender, EventArgs e)
{
display(collection);
}
public void configureDispatcherTimer()
{
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimeSpan interval = TimeSpan.FromMilliseconds(150);
dispatcherTimer.Interval = interval;
}
I've saved an image in my DataBase in a parameter type image and I've done this with:
private void btnAceptar_Click(object sender, RoutedEventArgs e)
{
UsuariosBLL bll = new UsuariosBLL();
UsuariosBO user = new UsuariosBO();
PerfilBO perfil = cmbperf.SelectedItem as PerfilBO;
//........
user.Imagen = ConvertImageToByteArray(ruta);
bll.InsertarFilaUsuarios(user);
MessageBox.Show("Se insertó");
//.......
}
where the method ConvertToByteArray convert the image selected in a Byte Array
public byte[] ConvertImageToByteArray(string path)
{
byte[] ImageByte=null;
try
{
FileStream fs = new FileStream(path,FileMode.Open,FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
ImageByte = br.ReadBytes((int)fs.Length);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return ImageByte;
}
and now I just wanna retrieve the image selecting a different user in my combobox.
I've tried like this:
private void cmbUsuarios_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UsuariosBO user = e.AddedItems[0] as UsuariosBO;
//.....
MemoryStream ms = new MemoryStream(user.Imagen);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
imgFoto = img;
usuario = user;
}
But a error born:
Can not implicitly convert type 'System.Drawing.Image' to 'System.Windows.Controls.Image'
I understand what it means but I don't know how to fix it...
thanks !!
Instead of creating a System.Drawing.Image from the byte stream, you should create a WPF ImageSource and assign that to the Source property of your ImageControl.
One way to do this is by creating a BitmapImage:
using (var ms = new MemoryStream(user.Imagen))
{
var img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.StreamSource = ms;
img.EndInit();
imgFoto.Source = img;
}
Or alternatively a BitmapFrame:
using (var ms = new MemoryStream(user.Imagen))
{
imgFoto.Source = BitmapFrame.Create(ms,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
My Solution Explorer image :
My code in PopupWinHelper :
var img = new Image
{
BitmapEffect = new DropShadowBitmapEffect(),
Stretch = Stretch.Fill,
Source = new BitmapImage(new Uri("/WpfApplication1;component/images/PopupImage.png"))
};
My code in Mainwindow :
private void button1_Click(object sender, RoutedEventArgs e)
{
cuswin.PopupWinHelper.ShowPopUp(150,300,"asdad", new Thickness(20));
}
Why does this error occur?
Then I'm use :
var img = new Image
{
BitmapEffect = new DropShadowBitmapEffect(),
Stretch = Stretch.Fill,
Source = new BitmapImage(new Uri("/images/PopupImage.png", UriKind.Relative))
};
No error. But the picture is not shown in image.
Source = new BitmapImage(new Uri("/WpfApplication1;component/images/PopupImage.png", UriKind.Relative))
should work, assumed PopupImage.png is a Resource.
Regards
The following code will only show a size for my bitmap (needed for processing) if I uncomment the commented lines. This doesn't seem the right to go about things but it's all I've come up with so far that works. I don't want to display my bitmap as an image in a UI element, I just want to process it.
BitmapImage bmpi;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
bmpi = new BitmapImage(new Uri("multicolor.png", UriKind.Relative));
//Image img = new Image();
//img.Source = bmpi;
//LayoutRoot.Children.Add(img);
//LayoutRoot.Children.Clear();
MessageBox.Show(bmpi.PixelWidth.ToString());
}
To load the image upfront, you need to set CreateOptions to None from its default value, DelayCreation.
Then you can get the width in the ImageOpened event.
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
bmpi = new BitmapImage();
bmpi.CreateOptions = BitmapCreateOptions.None;
bmpi.ImageOpened += new EventHandler<RoutedEventArgs>(bmpi_ImageOpened);
bmpi.UriSource = new Uri("multicolor.png", UriKind.RelativeOrAbsolute);
}
void bmpi_ImageOpened(object sender, RoutedEventArgs e)
{
MessageBox.Show(bmpi.PixelWidth.ToString());
}