Im tring to load data from database and display on a ListView.
The list view contains 2 textview and 1 image view. Im using SQlite.net.
Where is my databse:
Database
public class contacts
{
[PrimaryKey, AutoIncrementAttribute, Column("id")]
public int id {get; set;}
public string name {get; set;}
public string number { get; set; }
public byte[] photo{ get; set; }
}
And where is my function to fill the listview:
private void refreshListView()
{
var db = new SQLiteConnection (MainActivity._DatabaseConnectString);
MatrixCursor mMatrixCursor = new MatrixCursor (new String[]{ "_id", "name", "number", "photo" });
SimpleCursorAdapter adap;
adap = new SimpleCursorAdapter (
this,
Resource.Layout.lv_layout,
null,
new String[]{ "name", "number", "photo" },
new int[]{ Resource.Id.tv_name, Resource.Id.tv_number, Resource.Id.iv_photo }, 0);
IEnumerable<contacts> table = db.Query<contacts> ("select * from contacts");
foreach (var contact in table) {
mMatrixCursor.AddRow (new Java.Lang.Object[] {contact.id,
contact.name, contact.number, BitmapFactory.DecodeByteArray (contact.photo, 0, contact.photo.Length)
});
}
lview.Adapter = adap;
adap.SwapCursor (mMatrixCursor);
}
The only problem is the image dont show and I have this error from the output:
[BitmapFactory] Unable to decode stream: java.io.FileNotFoundException: /android.graphics.Bitmap#42087da0: open failed: ENOENT (No such file or directory)
[System.out] resolveUri failed on bad bitmap uri: android.graphics.Bitmap#42087da0
I already done this application with java for android and I fix that problem with the this code but I dont know how to "translate" the code from java to c#.
Where is the java code below the adap:
adap.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int i) {
if (view.getId() == R.id.iv_photo) {
byte[] Blobdata = cursor.getBlob(i);
Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(
Blobdata, 0, Blobdata.length), 150, 150, false);
((ImageView) view).setImageBitmap(bitmap);
return true;
} else {
return false;
}
}
});
Somebody can help me "translate" this code to c#? or use another code.
Thanks from the help and sorry my english.
EDITED 2
Ok where is the code translated to c#
class ViewBinder : Java.Lang.Object, SimpleCursorAdapter.IViewBinder
{
public bool SetViewValue (View view, Android.Database.ICursor cursor, int i)
{
if (view.Id == Resource.Id.iv_photo) {
byte[] Blobdata = cursor.GetBlob (i);
Bitmap bitmap = Bitmap.CreateScaledBitmap (BitmapFactory.DecodeByteArray (
Blobdata, 0, Blobdata.Length), 150, 150, false);
((ImageView)view).SetImageBitmap (bitmap);
return true;
} else {
return false;
}
}
}
But now I got an error in this line:
byte[] Blobdata = cursor.GetBlob (i);
Error:
android.graphics.Bitmap cannot be cast to byte[]
I think im getting this error becouse in my database im not using Blob but byte array. Somebody know how I can retrive byte array from database or how i can use Blob data type?
You can store a byte-array in a BLOB.
This is how I fetch it:
using (SqliteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
images.Add(new Image((byte[])reader["Image"]));
}
}
I can then use my Image property ImageByteArray to convert it to a Bitmap, similar to the way you did.
Bitmap image = BitmapFactory.DecodeByteArray(data, 0, barray.Length);
The Bitmap is then ready to show in an ImageView.
imageView.SetImageBitmap(image);
As you can see, our methods are very much alike. So to me it seems cursor.GetBlob (i) returns something unexpected.
public abstract byte[] getBlob (int columnIndex)
Added in API level 1
Returns the value of the requested column as a byte array.
The result and whether this method throws an exception when the column value is null or the column type is not a blob type is implementation-defined.
Parameters:
columnIndex the zero-based index of the target column.
Returns:
the value of that column as a byte array.
(http://developer.android.com/reference/android/database/Cursor.html#getBlob(int))
According to the description from the method, it seems your column does not hold a BLOB/byte-array.
Use var Blobdata = cursor.GetBlob (i); to find out what is in it, and adjust accordingly.
Related
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.
I am using GMImagePickerController for Xamarin ios to allow users to select multiple images from their gallery. I am having trouble dealing with these images in the callback function. I am getting a null reference exception when attempting to convert the UIImages to byte arrays.
var picker = new GMImagePickerController
{
ColsInPortrait = 3,
ColsInLandscape = 5,
ModalPresentationStyle = UIModalPresentationStyle.Popover,
AllowsMultipleSelection = true,
};
PHImageManager imageManager = new PHImageManager();
picker.FinishedPickingAssets += (sender, ev) =>
{
List<Byte[]> images = new List<Byte[]>();
foreach (var asset in picker.SelectedAssets)
{
imageManager.RequestImageForAsset(asset,
new CGSize(asset.PixelWidth, asset.PixelHeight),
PHImageContentMode.Default,
null,
(UIImage image, NSDictionary info) =>
{
uiImages.Add(image);
});
}
picker.DismissModalViewController(true);
foreach(var i in uiImages)
{
Byte[] imgArray;
using (NSData imgData = i.AsJPEG())
{
imgArray = new Byte[imgData.Length];
System.Runtime.InteropServices.Marshal.Copy(imgData.Bytes, imgArray, 0, Convert.ToInt32(imgData.Length));
images.Add(imgArray);
}
}
callback(images);
};
I am getting an exception in my last foreach loop imgData remains null when I try NSData imgData = i.AsJPEG(). However I have checked and i is not null at this point. Can anyone explain to me why i.ASJPEG() would return null?
Any suggestions on how to convert my the selected assets to byte arrays to be passed to my callback function would be greatly appreciated.
Thanks!
I am new to Xamarin Andriod Development. I am trying to send a Base64 encoded image to mySQL server database and then retrieving the Base64 encoded image in the database when starting the app and displaying it. I have been able to get all the other information in the database to display, the only thing missing is the images when I open the app.
My code looks like this:
Sending the Image to the server
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (resultCode == Result.Ok)
{
int contactID = mContacts[(int)mSelectedPic.Tag].ID;
Stream stream = ContentResolver.OpenInputStream(data.Data);
mSelectedPic.SetImageBitmap(DecodeBitmapFromStream(data.Data, 150, 150));
Bitmap bitmap = BitmapFactory.DecodeStream (stream);
MemoryStream memStream = new MemoryStream ();
bitmap.Compress (Bitmap.CompressFormat.Webp, 100, memStream);
byte[] picData = memStream.ToArray ();
WebClient client = new WebClient ();
Uri uri = new Uri ("MYWESBITE/UpdateContact.php");
NameValueCollection parameters = new NameValueCollection ();
parameters.Add ("Image", Convert.ToBase64String(picData));
parameters.Add ("ContactID", contactID.ToString());
client.UploadValuesAsync (uri, parameters);
client.UploadValuesCompleted += Client_UploadValuesCompleted;
}
}
PHP code to handle the image and store it into the database as a VARBINARY
$imgData = base64_encode($mImage);
$sql = "UPDATE Contact SET ImageBase64 = '$imgData' WHERE ID = '$mContactID'";
$result = mysql_query($sql, $link);
if (!$result) {
echo "DB Error, could not query the database\n";
echo 'MySQL Error: ' . mysql_error();
exit;
}
Then when the app opens it calls this PHP function
$sql = "SELECT * FROM Contact";
$result = mysql_query($sql, $link);
if (!$result) {
echo "DB Error, could not query the database\n";
echo 'MySQL Error: ' . mysql_error();
exit;
}
//create an array
$contact_array = array();
while($row =mysql_fetch_assoc($result))
{
$contact_array[] = array("ID" => $row["ID"],
"Name" => $row["Name"],
"Number" => $row["Number"],
"ImageBase64" => base64_encode($row["ImageBase64"])
);
}
echo json_encode($contact_array);
This shows how I turn the base64_encoded string to a byte array
class Contact
{
public int ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
public string ImageBase64 { private get; set; }
public byte [] Image
{
get
{
if (ImageBase64 != "" && ImageBase64 != null)
{
byte[] image = Convert.FromBase64String (ImageBase64);
return image;
}
return null;
}
}
}
MainActivity.cs
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
mListView = FindViewById<ListView>(Resource.Id.listView);
mProgressBar = FindViewById<ProgressBar> (Resource.Id.progressBar);
mClient = new WebClient ();
mUrl = new Uri ("MYWEBSITE/GetContacts.php");
//Call the Client
mClient.DownloadDataAsync (mUrl);
mClient.DownloadDataCompleted += MClient_DownloadDataCompleted;
}
Then finally gets converted to image like this
ImageView pic = row.FindViewById<ImageView>(Resource.Id.imgPic);
if (mContacts[position].Image != null)
{
pic.SetImageBitmap(BitmapFactory.DecodeByteArray(mContacts[position].Image, 0, mContacts[position].Image.Length));
}
Thank you for your time everyone, I hope you can help me figure this out!
A few questions might help to answer this: At what point does it not work? Are you getting an error? or does the image not display?
Right off the bat, however, it looks like you are trying to interact from the UI from a background thread. Can you try wrapping the code in your MCClient_Downloaded method in RunOnUIThread?
I got it to work now. I changed my database type to be a Medium BLOB instead of a VARBINARY, and then got rid of the base64_encode() on the way into the database and then got rid of the base64_encode() on the way out of the database in the JSON creation.
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();
}
}
iv'e got a repeater bonded to a list of entities
BL_Engine engine = (BL_Engine)Application["engine"];
List<AppProduct> products = engine.Get_Products();
Repeater1.DataSource = products;
Repeater1.DataBind();
iv'e also got a user control i use to represent these product entities , i do this by overriding the Databind() in the user control :
public override void DataBind()
{
AppProduct product = (Page.GetDataItem() as AppProduct);
lbl_title.Text = product.Title;
lbl_short.Text = product.Short;
lbl_date.Text = product.Date.ToShortDateString();
Session["current_img"] = product.Image1;
base.DataBind();
}
in my HttpHanlder object kept in a .ashx file i write the image to the response
the response happens only once so only the last picture is written to (ALL) the user controls.
public void ProcessRequest(HttpContext context)
{
byte [] image = (byte[])context.Session["current_img"];
context.Response.ContentType = "image/bmp";
context.Response.OutputStream.Write(image, 0, image.Length);
}
any idea how i could write the binary data for each individual control
thanks in advance
eran.
Let me suggest a different approach.
Declare a regular html image element in your control and set the "runat=server" property as so:
<img src="" runat="server" id="img_product" />
Then change your DataBind() method to do this instead:
public override void DataBind()
{
AppProduct product = (Page.GetDataItem() as AppProduct);
lbl_title.Text = product.Title;
lbl_short.Text = product.Short;
lbl_date.Text = product.Date.ToShortDateString();
img_product.src= "\"data:image/jpg;base64,"+System.Convert.ToBase64String(product.Image1)+"\"";
base.DataBind();
}
And get rid of the HTTPHandler. You don't need it for this.
Change your handler so that it takes the id of the current product as a query string parameter. In the handler load the correct image data based on the parameter and write that instead.
well to conclude , for this case i think the best practice would be #icarus's answer ,
not the disregard #kmcc049's answer i just hadn't dug into it since it seemed like a more complicated architecture for my app.
in this case DROP THE HTTPHANDLER .
i saved the image's type , and data from the post file.
public enum ImageType : byte {jpg,jpeg,png,gif}
private byte[] Get_Image(HttpPostedFile file)
{
ImageType type = GetType(file.ContentType);
if (file.InputStream.Length <= 1)
return null;
byte[] imageData = new byte[file.InputStream.Length + 1 + 1];
file.InputStream.Read(imageData, 1, imageData.Length+1);
imageData[0] =(byte)type;
return imageData;
}
private ImageType GetType(string _type)
{
ImageType t = default(ImageType);
string s = _type.Substring(_type.IndexOf('/')+1).ToLower() ;
switch (s)
{
case "jpg": t = ImageType.jpg;
break;
case "jpeg": t = ImageType.jpeg;
break;
case "png": t = ImageType.png;
break;
case "gif": t = ImageType.gif;
break;
}
return t;
}
then i extracted and added it to the user control in my DataBind override(in my user control) :
public override void DataBind()
{
AppProduct product = (Page.GetDataItem() as AppProduct);
img_main.Attributes.Add("src",Build_Img_Data(product.Image1));
base.DataBind();
}
private string Build_Img_Data(byte[] imageData)
{
ImageType type = (ImageType)imageData[0] ;
byte[] new_imageData = new byte[imageData.Length - 1];
Array.ConstrainedCopy(imageData, 1, new_imageData, 0, new_imageData.Length);
MemoryStream ms = new MemoryStream(new_imageData);
string base64String = string.Format("data:image/{0};base64,{1}",type.ToString(),Convert.ToBase64String(ms.ToArray()));
return base64String;
}