Windows Forms: How to directly bind a Bitmap to a PictureBox? - c#

I'm just trying to build a small C# .Net 4.0 application using Windows Forms (WPF I don't know at all, Windows Forms at least a little :-) ).
Is it possible to directly bind a System.Drawing.Bitmap object to the Image property of a PictureBox? I tried to use PictureBox.DataBindings.Add(...) but this doesn't seem to work.
How can I do this?
Thanks and best regards,
Oliver

This works for me:
Bitmap bitmapFromFile = new Bitmap("C:\\temp\\test.bmp");
pictureBox1.Image = bitmapFromFile;
or, in one line:
pictureBox1.Image = new Bitmap("C:\\temp\\test.bmp");
You might be overcomplicating this - according to the MSDN documentation, you can simple assign the bitmap directly to the PictureBox.Image property.

You can use the PictureBox.DataBindings.Add(...)
The trick is to create a separate property on the object you are binding to to handle the conversion between null and and empty picture.
I did it this way.
In my form load I used
this.PictureBox.DataBindings.Add(new Binding("Visible", this.bindingSource1, "HasPhoto", false, DataSourceUpdateMode.OnPropertyChanged));
this.PictureBox.DataBindings.Add(new Binding("Image", this.bindingSource1, "MyPhoto",false, DataSourceUpdateMode.OnPropertyChanged));
In my object I have the following
[NotMapped]
public System.Drawing.Image MyPhoto
{
get
{
if (Photo == null)
{
return BlankImage;
}
else
{
if (Photo.Length == 0)
{
return BlankImage;
}
else
{
return byteArrayToImage(Photo);
}
}
}
set
{
if (value == null)
{
Photo = null;
}
else
{
if (value.Height == BlankImage.Height) // cheating
{
Photo = null;
}
else
{
Photo = imageToByteArray(value);
}
}
}
}
[NotMapped]
public Image BlankImage {
get
{
return new Bitmap(1,1);
}
}
public static byte[] imageToByteArray(Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, ImageFormat.Gif);
return ms.ToArray();
}
public static Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}

You can do:
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap("yourfile.bmp");
picturebox1.Image = bmp;

Related

How can i convert a file .png to a Bitmap? Nothing works for me. Error "'Failed to decode image. The provided image must be a Bitmap.'" Xamarin.forms

I'm working with Xamarin in the part of Xamarin.Forms i need to convert a file ("image.png") to a Bitmap because when project run its enter in "break mode" and show me this message "Java.Lang.IllegalArgumentException: 'Failed to decode image. The provided image must be a Bitmap.'". So i tried to convert the file in many ways like:
1_ Using methods from System.Drawing.Bitmap but it's show this exception "This operation is not supported in this platform"
2_ Cannot use Android.Graphics because i'm working in xamarin.forms not in xamarin.android.
3_ I tried to convert the "image.png" into a base64 or byte[] array and then into a bitmap but the problem is the same like in first problem because to convert from byte[] array or base64 to a Bitmap i have use methods from System.Drawing.Bitmap.
4_ I tried using the library SkiaSharp but i don't have success because i don't found so much information about how to convert .png to SKBitmap and then convert SKBitmap to Bitmap (even i don't know if this is possible).
5_ Finally i converted "image.png" to a "image.bmp" with an app and use the file .bmp in my project but it doesn't work too.
the "image.png" i have to save in a string variable, well that's the idea.
If you have a solution with SkiaSharp o whatever i will glad
EDIT
here is a part of my code, i just save in a variable the image
Icon = "pin_blue.png";
//i can't use a path because in xamarin you have many size from the same
//image for the different size of the screen
EDIT 2 This is my method to show the pins in google maps
private void ShowPins(List<PointsOfInterest> resultPointsOfInterest)
{
if (resultPointsOfInterest != null && resultPointsOfInterest.Any())
{
var location = Geolocation.GetLastKnownLocationAsync();
PointsOfInterest position = new PointsOfInterest();
if (location != null)
{
position.ccsm0166latitud = location.Result.Latitude;
position.ccsm0166longitud = location.Result.Longitude;
}
else {
position = resultPointsOfInterest.First();
}
//Distance = Distance.FromKilometers(new Random().Next(23,35));
Distance = Distance.FromKilometers(3);
Position = new Position(position.ccsm0166latitud, position.ccsm0166longitud);
PinsFiltered= Pins = new List<PinCustom>(resultPointsOfInterest.Select(
x => new PinCustom()
{
Position =
new Position(x.ccsm0166latitud, x.ccsm0166longitud),
Address = x.ccsm0166direccion,
Label = x.ccsm0166nombre,
Type = PinType.Place,
TypePointOfInterest = x.ccsm0166tipositio,
IconBM = Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.branch ? PinCustom.ConvertToBitmap("pin_blue.png") :
Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.branch ? "pin_blue.png" :
Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.branchWithExtendedHours ? "pin_black.png" :
Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.branchWithExtendedHours2 ? "pin_black.png" :
Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.branchWithExtendedHours3 ? "pin_black.png" :
Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.selfServiceTerminal ? "pin_green.png" :
Int32.Parse(x.ccsm0166tipositio) == (int)PointOfInterestType.atmServBox ? "pin_yellow.png" : string.Empty
}).ToList());
}
else
{
Pins = new List<PinCustom>();
}
}
This is the class Pin where i save the image
public class PinCustom : Pin
{
public string TypePointOfInterest { get; set; }
public string Icon { get; set; }
public Bitmap { get; set; }
//Here i create this variable to save a bitmap but i don't know if i do the things well
}
this is the icon .png i want to show in googlemaps
Pin Blue Image
To open your file and convert it to byte array:
string directory = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
// or your image directory, just replace it
var stream = File.OpenWrite(Path.Combine(directory, "image.png"));
byte[] buff = ConvertStreamToByteArray(stream);
stream.Close();
public static byte[] ConvertStreamToByteArray(Stream stream)
{
byte[] byteArray = new byte[16 * 1024];
using (MemoryStream mStream = new MemoryStream())
{
int bit;
while ((bit = stream.Read(byteArray, 0, byteArray.Length)) > 0)
{
mStream.Write(byteArray, 0, bit);
}
return mStream.ToArray();
}
}
then, to pass this byte array to SKBitmap:
SKBitmap bmp = SKBitmap.Decode(buff);
EDIT:
If you want to try without ConvertStreamToByteArray():
byte[] buff = null;
stream.Write(buff, 0, buff.Length);
This Write will depend of the type of your stream. In this example, I'm using FileStream.
EDIT 2:
string resourceID = "image.png";
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream(resourceID))
{
resourceBitmap = SKBitmap.Decode(stream);
SKBitmap bmp = SKImage.FromBitmap(resourceBitmap);
}
and then you can use this bitmap to draw and fill your SKCanvas:
canvas.DrawBitmap(bmp, 0, 0);
Use ffmpeg
command for this: ffmpeg -i image.png image.bmp
i have 5 types of pins (pins are the image .png) when i put the pins
in format .png
if you want to custom the pins in your map, you can simply use Custom Renderers to achieve this.
The icon used to represent a marker can be customized by calling the MarkerOptions.SetIcon method. This can be accomplished by overriding the CreateMarker method, which is invoked for each Pin that's added to the map:
protected override MarkerOptions CreateMarker(Pin pin)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
return marker;
}
If you want to display different icons, you can refer to the following code:
protected override MarkerOptions CreateMarker(Pin pin)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
var customPin = (Element as CustomMap).CustomPins.FirstOrDefault(p => p.Position == pin.Position);
if (customPin != null)
{
if (customPin.IconType == "corporate")
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.corporate));
}
else if (customPin.IconType == "pickup")
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.bus));
}
}
return marker;
}
For more, check:Customizing the Marker.

Azure Database and Photos

I have a database set up in Azure, and along with some other data types, it will house some pictures. The column to hold the photos is an Image data type. My insert looks like this (converts the pic to a binary file)
public void Insert()
{
string filepath = "C:\\PictureFolder\\MyPhoto.jpg";
byte[] Pic = File.ReadAllBytes(filepath);
DataContex oDc = new DataContex();
tblShip otblShip = new tblShip;
otblShip.Id = Guid.NewGuid();
otblShip.Name = "Enterprise";
otblShip.Picture = Pic;
oDc.tblShips.InsertOnSubmit(oMyTable);
oDc.SubmitChanges();
}
The insert works, when I check my table in Azure, a binary value is inserted in the Picture column. How do I get it back, and how do I display an actual photo in my WPF interface?
I agree with #CSharpRocks that, since you're already using Azure, it'll be very convenient for you to do some research and store your pictures on a BlobStorage storage account. Storage accounts have many advantages when compared to traditional database storage.
You can find more about storage accounts and how to get started with it here.
But this question is more about how do you retrieve the image and show it in your WPF application, as per you own question. So let's see:
You already have you image stored on your database, so you can fetch the related tblShip entity, using the primary key value (or any other query):
Assuming you have this somewhere in your xaml view:
<Image Name="imgControl" ... />
You could show your image like this:
private void DisplayImageFromDb(Guid id)
{
using (var context = new DataContext())
{
var item = context
.tblShips
.AsNoTracking()
.FirstOrDefault(x => x.Id == id);
if (item == null)
throw new Exception("Image could not be found!");
//Convert the byte[] to a BitmapImage
BitmapImage img = new BitmapImage();
MemoryStream ms = new MemoryStream(item.Picture);
img.BeginInit();
img.StreamSource = ms;
img.EndInit();
//assign the image to the Source property of the Image Control.
imgControl.Source = img;
}
}
This will work ok, but It'll be better if you use a more WPF (MVVM) oriented way. So let's say you have you xaml view, but your are using MVVM. The Image control is therefore bound to a viewmodel property:
<Image Grid.Row="1"
Source="{Binding ImageSource, Converter={StaticResource ByteArrayToImageConverter}}"/>
In your view's resources you declare the converter:
<Windows.Resources>
<local:ByteArrayToImageConverter x:Key="ByteArrayToImageConverter" />
</Window.Resources>
Here is the implementation for the converter:
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace WpfApp1
{
public class ByteArrayToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return null;
//Convert the byte[] to a BitmapImage
BitmapImage img = new BitmapImage();
MemoryStream ms = new MemoryStream((byte[])value);
img.BeginInit();
img.StreamSource = ms;
img.EndInit();
return img;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Now you have a clean separation of concerns so your UI is who actually is showing the image and your viewmodel should only load it from the database and manipulate the domain entity:
Your viewmodel must declare ImageSource property used for binding:
private byte[] imageSource;
public byte[] ImageSource
{
get { return imageSource; }
set
{
imageSource = value;
//must implement property changed notifications
OnPropertyChanged(new PropertyChangedEventArgs("ImageSource"));
}
}
After all this refactoring, the method in the viewmodel, that loads the image can be implemented like this:
private void DisplayImageFromDb(int id)
{
using (var context = new DataContext())
{
var item = context
.tblShips
.AsNoTracking()
.FirstOrDefault(x => x.Id == id);
if (item == null)
throw new Exception("Image could not be found!");
//Assign the property and let the binding do the work
ImageSource = item.Picture;
}
}
You'll certainly get the same result, but you now have a very well separated app that will allow to be more easily maintained as it evolves.
Hope this helps!
Everything's hard coded here, but here is how I move a 100 x 100 pixel image
to and from the Db. I do understand there are better ways to store images, but for my purposes, this works great!
public void InsertPhotoToDb()
{
string filepath = "C:\\TFS\\Enterprise.jpg";
byte[] Pic = File.ReadAllBytes(filepath);
ArmadaDataContext oDc = new ArmadaDataContext();
tblPictureTest otblPictureTest = new tblPictureTest();
otblPictureTest.Id = Guid.NewGuid();
otblPictureTest.FileName = "Enterprise";
otblPictureTest.Photo = Pic;
oDc.tblPictureTests.InsertOnSubmit(otblPictureTest);
oDc.SubmitChanges();
oDc = null;
}
private void DisplayImageFromDb()
{
using (var oDc = new ArmadaDataContext())
{
var item = oDc
.tblPictureTests
.FirstOrDefault(x => x.FileName == "Enterprise"); // Retrieves using the filename
// If retrieving using the GUID, use the line below instead.
// .FirstOrDefault(x => x.Id == Guid.Parse("58b44a51-0627-43fe-9563-983aebdcda3a"));
if (item == null)
throw new Exception("Image could not be found!");
//Convert the byte[] to a BitmapImage
BitmapImage img = new BitmapImage();
MemoryStream ms = new MemoryStream(item.Photo.ToArray());
img.BeginInit();
img.StreamSource = ms;
img.EndInit();
//assign the image to the Source property of the Image box in the UI.
imgPhoto.Source = img;
}
}

QRCode Extraction With ZXing

Hi I'm trying to read QRCode from scanned images, but I'm getting a low index of extraction (19 extracted from 500 images) the code of extraction:
class QrExtractor
{
public String extractFrom(Bitmap image)
{
using (image)
{
LuminanceSource source;
source = new BitmapLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result = new QRCodeReader().decode(bitmap);
if (result != null)
{
return result.Text;
}
return "Couldn't Extract";
}
}
}
Is there any improvements that I can apply to this?
Thanks

Passing in a HttpPostedFileWrapper variable to a function breaks another function. Image validation and conversion in C#

So basically this part of the program is editing/uploading a new profile picture to a user's account. Previously, it worked fine. Then I decided to add in some picture validations (picture has to have certain dimensions, etc). So I made a separate Helper class for that that takes in the HttpPostedFileWrapper variable initialized in the controller.
So, in this controller function, I initialize a new instance of the ValidateImage class which holds two functions (DoValidation and Resize).
The Resize function was working fine until I added the DoValidation function and I feel like it has something to do with the memory stream.
I now get an "Invalid Parameter" error in the ResizeImage function (see below), even though I never changed that code and was working fine previously. Does it have something to do with the filestream not being closed properly or something?
Here is the code:
//Controller.cs
public virtual ActionResult EditMyProfilePicture(bool? ignore)
{
var loggedInEmployee = this.EmployeeRepos.GetEmployeeByUserName(User.Identity.Name);
int tgtWidth = 250, tgtHeight = 250;
try
{
// get a reference to the posted file
var file = Request.Files["FileContent"] as HttpPostedFileWrapper;
ValidateImage img = new ValidateImage();
if (file != null && file.ContentLength > 0)
{
// isolate the filename - IE returns full local path, other browsers: just the file name.
int index = file.FileName.LastIndexOf("\\");
// if not IE, index will be -1, but -1 + 1 = 0 so we are okay.
string fileName = file.FileName.Substring(index + 1);
// Validate the image
img.DoValidation(file, tgtWidth, tgtHeight);
if (!img.IsValidated)
{
throw new ArgumentException(img.Message);
}
else
{
byte[] resizedImg = img.Resize(file, tgtWidth, tgtHeight);
this.EmployeeRepos.SaveProfileImage(loggedInEmployee.EmployeeCode, resizedImg);
}
return RedirectToAction(MVC.Employees.EditMyProfile());
}
else
{
throw new ArgumentException("Please select a file to upload.");
}
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, ex.Message);
}
return View(Views.EditMyProfilePicture, loggedInEmployee);
}
// ValidateImage.cs
public class ValidateImage
{
public string Message { get; private set; }
public bool IsValidated { get; private set; }
public void DoValidation(HttpPostedFileWrapper file, int tgtWidth, int tgtHeight)
{
try
{
Image img = Image.FromStream(file.InputStream);
int curHeight = img.Height, curWidth = img.Width;
// check for image too small
if (curHeight < tgtHeight || curWidth < tgtWidth)
{
Message = "image is too small. please upload a picture at least 250x250.";
IsValidated = false;
return;
}
// check for image is square
else if (curHeight != curWidth)
{
Message = "image is not a square.";
IsValidated = false;
return;
}
else
{
IsValidated = true;
}
}
catch
{
}
}
public byte[] Resize(HttpPostedFileWrapper file, int tgtWidth, int tgtHeight)
{
byte[] bytes = new byte[file.ContentLength];
file.InputStream.Read(bytes, 0, file.ContentLength);
file.InputStream.Close(); // close the file stream.
// Down-sample if needed from current byte array to max 250x250 Jpeg
byte[] resized = Helpers.ImageResizer.ResizeImage(bytes, tgtWidth, tgtHeight, ResizeOptions.MaxWidthAndHeight, ImageFormat.Jpeg);
return resized;
}
}
// Resize Image function
public static byte[] ResizeImage(byte[] bytes, int width, int height, ResizeOptions resizeOptions, ImageFormat imageFormat)
{
using (MemoryStream ms = new MemoryStream(bytes))
{
Image img = Image.FromStream(ms);
Bitmap bmp = new Bitmap(img);
bmp = ResizeImage(bmp, width, height, resizeOptions);
bmp.SetResolution(72, 72);
bmp.Save(ms, imageFormat);
return ms.ToArray();
}
}

How to serve PNG to HtmlImageLoadEventArgs callback as byte[]?

I am using HTML Renderer to create a PDF. I implemented an image provider to server images (PNG). Its HandleImageLoad is properly called but the provided imgae is not shown in the PDF but something that looks like a clock(?) image.
This is the setup:
ImageProvider imgProvider = new ImageProvider();
PdfSharp.Pdf.PdfDocument pdf = TheArtOfDev.HtmlRenderer.PdfSharp.PdfGenerator.GeneratePdf(
HTML, PdfSharp.PageSize.A4, 20,
null, null, imgProvider.HandleImageLoad);
public void HandleImageLoad(object sender, HtmlImageLoadEventArgs args)
{
var img = LoadFromSource(args.Src);
if(img == null)
{
args.Handled = false;
args.Callback();
Log.Error("Missing image: {0}", args.Src);
}
else
{
args.Handled = true;
args.Callback(img.Data);
}
}
The variable img has stored a PNG as byte[] in the member named Data. Do I need any transformation or intermediary object to render the image propery as image?
Ok, I found it out. The image type must be of type PdfSharp.Drawing.XImage. The easiest way is to load the PNG fila as a System.Drawing.Image.
using (var mem = new MemoryStream(img.Data))
{
System.Drawing.Image sysImg = System.Drawing.Image.FromStream(mem, false, true);
var ximg = PdfSharp.Drawing.XImage.FromGdiPlusImage(sysImg);
args.Callback(ximg);
}

Categories

Resources