I have this code for downloading a file which runs correct:
var base64EncodedBytes = System.Convert.FromBase64String(item.FileDataAsBase64String);
var downloadDirectory = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
var filePath = Path.Combine(downloadDirectory, "test.pdf");
var streamWriter = File.Create(filePath);
streamWriter.Close();
File.WriteAllBytes(filePath, base64EncodedBytes);
I'm able to locate the file I downloaded in the Downloads folder, but also I want to show a notification in the notification bar that the file has been downloaded and with a click in the notifications the user to be able to open the downloaded file.
Is that possible?
You can use Plugin.LocalNotification to show the notification once the file is downloaded.
try
{
var base64EncodedBytes = System.Convert.FromBase64String(item.FileDataAsBase64String);
var downloadDirectory = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
var filePath = Path.Combine(downloadDirectory, "test.pdf");
var streamWriter = File.Create(filePath);
streamWriter.Close();
File.WriteAllBytes(filePath, base64EncodedBytes);
DisplayNotification("test.pdf downloaded successfully", filePath);
}
catch(System.Exception e)
{
System.Console.WriteLine(e.ToString());
DisplayNotification("Download Failed",string.Empty);
}
public void DisplayNotification(string message, string filePath)
{
var notification = new NotificationRequest
{
NotificationId = 100,
Title = "Your App name",
Description = message,
ReturningData = filePath, // Returning data when tapped on notification.
NotifyTime = DateTime.Now.AddSeconds(30) // Used for Scheduling local notification, if not specified notification will show immediately.
};
NotificationCenter.Current.Show(notification);
}
Note: Make sure to initialize the plugin setup in both the project iOS and Android.
Solved: The answer was to update all of the nuget packages and target a newer version of Android. Now images loads as expected. I'm not happy with this as I was using exactly the code that Xamarin provided and targeting newer versions has deprecated some of the items the code relys on. Initial version was Xamarin.Forms v23 and I updated to V25
I have a brand new Xamarin forms project with a simple view in which I'm trying to display an image. I've tried several ways of getting an image to display and I am not having any luck at all.
I'm using <image> and I have also tried FFImageLoader control as well.
<StackLayout Orientation="Vertical">
<ff:CachedImage Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" WidthRequest="100" HeightRequest="100" />
<Button x:Name="btn" Text="Image" Clicked="Button_Clicked" />
<Frame OutlineColor="Red">
<Image x:Name="StupidImage" Source="{Binding Thumbnail}" Aspect="Fill" HeightRequest="100" WidthRequest="100" />
</Frame>
</StackLayout>
This is the current view. I've also set the Source directly to a value with no result.
I'm able to get a stream for the image. I'm able to read all of the bytes from the stream. I built a debug visualizer to display the bytes as an image. Getting the image from a source is not a problem. Getting the image control(s) to display the image is a problem.
I tried binding with a view model. When that failed, I tried that directly setting the source
StupidImage.Source = ImageSource.FromStream(() => result.Stream);
I also made a copy of the bytes and tried
StupidImage.Source = ImageSource.FromStream(() => new MemoryStream(imageBytes));
I've tried ImageSource.FromFile() and .FromUri. I tried adding an image to the project as a resource. Each try was the same, the resource was read and the bytes were available, but the image control just doesn't display it.
I thought maybe it was a size problem, so I set the size of the control. Nothing. I thought maybe it was a resolution problem, so I used a smaller image. I tried several different images of varying quality.
Then I gave up on the image control and I got the FFImageLoading nuget package and gave it a direct url to an image. Same example that FFImageLoading examples used. Still no image.
I tried the emulator and I tried 2 different physical devices. Same result.
I also tried setting an image on a button using btn.Image = "whatever.jpg" with the same result.
This is the result every time. I'm lost. How do I get images to display?
EDIT:
I did get this to work, but only on the emulator
<Image x:Name="StupidImage" Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" />
and same for
StupidImage.Source = ImageSource.FromUri(new Uri("https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg"));
EDIT 2 - Clarification
My goal is to allow the user to select a photo from the device and then display a preview of it.
If you want to use images in you app you can load them into your Shared Project, like
Make sure you change the Build Action to Embedded resource
Then in your code
image.Source = ImageSource.FromResource("App5.Images.useravatar.png");
Note the Resource name.
And XAML
<ContentPage.Content>
<StackLayout>
<Image x:Name="image" WidthRequest="50"/>
</StackLayout>
</ContentPage.Content>
Just a few things you can take off the list:
[x] Adding a image from Visual studio :
Right click on the correct folder
select Add >> New File ...
NB: you have to add it with visual studio and not just throw it in the folder. Visual studio needs to know about it
[x] When Adding the image is it in the correct place :
For android: it has to be in
ProjectName.Driod.Resources.drawable folder
For ios: it has to be in
ProjectName.iOS.Resources folder
[x] Naming Convention
Its always best to use .png , all lowercase , no spaces or special char on both android and ios
with ios you normally get 3 images of the same image with the following namting convention
theman.png
theman#2x.png
theman#3x.png
They are all the same image just different sizes
[x] Showing it in xaml :
<StackLayout>
<Image Source="thedog.png" HeightRequest="100" WidthRequest="100" />
</StackLayout>
In your example you used a frame , how about a stacklayout ? a frame has more requirements.
for MVVM you can change Source with the following , dont forget that twoway :)
Source="{Binding Thumbnail, Mode=TwoWay}"
NB This is VERY basic explanations
You can try implementing the CrossMedia Plugin.
Then in your button clicked code section, put the following:
Button_Clicked.Clicked += async (sender, args) =>
{
if ( !CrossMedia.Current.IsPickPhotoSupported )
{
DisplayAlert("Error message here", "More message", "OK");
return;
}
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
});
if (file == null)
return;
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
};
Once the button is clicked, the gallery/directory will be displayed. You can choose the photo you want. Once you hit OK the image will be displayed in the Image control/tag. I'm not sure if this is the solution you are looking for. Hopes it gets you on the right direction.
This may or may not help I'll add some code, one of the surprising things about Xamarin forms and Android and using a memory stream.. is that the device density multiplier is still applied even if you aren't using a resource(If I am remembering correctly) so I would imagine if you are looking at the ADB interface you will see memory issues which is why you cant display an image... I solved this previously via sampling
The way I solved it was creating a new Image subclass -ResourceImage,
public class ResourceImage :Image
{
public enum SourceTypes{
Database,
File,
Function,
}
private bool _LoadAct = false;
public bool LoadAct { get{
return _LoadAct;
}
set{ _LoadAct = value; OnPropertyChanged ("LoadAct");
}
}
public Func<Stream> Func{ get; set; }
public SourceTypes SourceType{ get; set;}
public string ResName{ get; set;}
public ResourceImage ()
{
}
public ResourceImage (string name)
{
ResName = name;
}
public ResourceImage(Func<Stream> func){
SourceType = SourceTypes.Function;
Func = func;
}
}
then in the Android Renderer : I did the following
public class ResourceImageRenderer : ImageRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Image> e)
{
base.OnElementChanged (e);
if (e.OldElement == null)
{
var el = (ResourceImage)Element;
if (el.SourceType == ResourceImage.SourceTypes.Database) {
//Ignore for now
} else if (el.SourceType == ResourceImage.SourceTypes.File) {
using (global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ()) {
options.InJustDecodeBounds = false;
options.InSampleSize = 1;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);
var gd = Context.Resources.GetIdentifier (el.ResName.Split (new char[]{ '.' }) [0], "drawable", Context.PackageName);
using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {
var bitmap = global::Android.Graphics.BitmapFactory.DecodeResource (Context.Resources, gd, options);//DecodeStream (ms, rt, options);
bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;
Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));
}
}
} else if (el.SourceType == ResourceImage.SourceTypes.Function) {
new Task (() => {
var ms = el.Func();
if(ms == null)return;
global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ();
options.InJustDecodeBounds = false;
options.InSampleSize = 2;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);
ms.Position = 0;
Device.BeginInvokeOnMainThread(()=>{
using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {
try{
var bitmap = global::Android.Graphics.BitmapFactory.DecodeStream (ms, rt, options);
bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;
Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));
}catch(Exception eee){
}
}
});
}).Start();
}
}
}
Looking back at the code(haven't touched it in years.) there are plenty of places for improvement, I had to add the sampling to solve the same issue , users were selecting images to display in a messaging app and it worked perfectly on iOS just never displayed on Android
This is how I allow a user to select an image and then display it on a page.
I call my image service Select Image method passing in a callback method
await _imageService.SelectImage(ImageSelected);
This is my SelectImage method. There is some permission checking at the start. It uses the Media Plugin to display the gallery and allow the user to select an image.
public async Task SelectImage(Action<MediaFile> imageAction)
{
var allowed = await _permissionService.CheckOrRequestStoragePermission();
if (!allowed) return;
if (!_media.IsPickPhotoSupported)
{
throw new GalleryUnavailableException("Gallery unavailable");
}
var file = await _media.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Small,
CompressionQuality = 92
});
imageAction(file);
}
It returns a MediaFile
Here is the Image Selected callback method
private void ImageSelected(MediaFile image)
{
if (image == null)
{
return;
}
ChosenImage = new IncidentImage
{
ImageBytes = image.ToByteArray()
};
}
ChosenImage is a Property in my view model
public IncidentImage ChosenImage {get; set;}
I use PropertyChanged.Fody to trigger property changed notifications but you can also use INotifyPropertyChanged.
And IncidentImage is a class I use to both store and display images
public class IncidentImage
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int IncidentDetailsId { get; set; }
public byte[] ImageBytes { get; set; }
[Ignore]
public ImageSource ImageSource
{
get
{
ImageSource retval = null;
try
{
if (ImageBytes != null)
{
retval = ImageSource.FromStream(() => new MemoryStream(ImageBytes));
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return retval;
}
}
}
And here is the XAML
<Image Source="{Binding ChosenImage.ImageSource}"
Aspect="AspectFit"/>
assume we have the following text :
Contact us on 015546889 or email#hotmail.com
How I can display the above text in same label in xamarin.forms and handle click on email by send email and handle phone call by click on the number.
I can use the the following code to make clickable label
Label label = new Label;
label.GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => {
//do some function here
})
});
How to hyperlink same as messaging app or Whatsapp application
After a lot of search i found the perfect solution Here :
https://theconfuzedsourcecode.wordpress.com/tag/xamarin-hyperlink-label/
Hope this will help others :)
Check out the Label element in Forms9Patch. It has a HtmlText property that allows simple markup.
using System;
using Xamarin.Forms;
namespace Forms9PatchDemo
{
public class LabelLink : ContentPage
{
public LabelLink()
{
var label = new Forms9Patch.Label
{
HtmlText = "Contact us on <a id=\"phone\" href=\"tel:+353015546889\">015546889</a> or <a id=\"email\" href=\"mailto:email#hotmail.com\">email#hotmail.com</a>"
};
label.ActionTagTapped += (object sender, Forms9Patch.ActionTagEventArgs e) =>
{
var id = e.Id;
var href = e.Href;
var uri = new Uri(e.Href);
Device.OpenUri(uri);
};
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
new Label { Text = "Forms9Patch.Label.HtmlText <a> example" },
new BoxView { BackgroundColor = Color.Black, HeightRequest = 1 },
label
}
};
}
}
}
Note that the above example won't work on iOS emulators because the tel: and mailto: schemes are not supported. It does work on actual iOS devices.
I am picking a photo from photo library and i get the following
AlbumPath:
assets-library://asset/asset.JPG?id=106E99A1-4F6A-45A2-B320-B0AD4A8E8473&ext=JPG
Path:
/Users/myname/Library/Developer/CoreSimulator/Devices/21CB035B-A738-4F74-B121-2DB2A6B5372A/data/Containers/Data/Application/3081A323-98AF-4EF6-95B9-29D4C2CD8425/Documents/temp/IMG_20170408_111143.jpg
How do i assign this image to a button ? I tried the following.
var file = await CrossMedia.Current.PickPhotoAsync(
new Plugin.Media.Abstractions.PickMediaOptions
{
});
Button btn = new Button();
btn.Image = (Xamarin.Forms.FileImageSource)ImageSource.FromFile(file.Path);
No image is displayed on the button. Any help help is appreciated.
Thank you.
Once you deploy the application the app will not have access to your mac. For the application to use the picture it will need to be a bundled resource within the iOS application. Below is a simple example of using images within an iOS app. You'll definitely want to consider just adding a gesture listener to your image instead of making it a button though. If you try to just use the image on a button you'll have to do some styling adjustments to get it to look clean.
Image Setup
Put image in iOS Resource folder
Make sure bundledresource is selected from image properties.
Image Button
public class ImageButton : ContentPage
{
private Button _imageBtn = new Button { Image = "icon.png", Text = "Sample button" };
public ImageButton()
{
Content = new StackLayout
{
Children =
{
_imageBtn
}
};
}
}
Image with TapGesture
public class ImageButton : ContentPage
{
private Image _imageBtn = new Image { Source = "icon.png" };
private TapGestureRecognizer _imageTap = new TapGestureRecognizer();
public ImageButton()
{
_imageBtn.GestureRecognizers.Add(_imageTap);
Content = new StackLayout
{
Children =
{
_imageBtn
}
};
_imageTap.Tapped += (s, e) =>
{
// handle the tap
};
}
}
I'm using the IMediaPicker lines to open gallery and pick an image:
using XLabs.Platform.Services.Media;
using XLabs.Platform.Device;
using XLabs.Ioc;
using Xamarin.Forms;
private async Task<string> pickImage(){
var device = Resolver.Resolve<IDevice>();
IMediaPicker mediaPicker = DependencyService.Get<IMediaPicker>() ?? device.MediaPicker;
if (mediaPicker == null)
throw new NullReferenceException("Media picker initialize error");
string ImageSource = null;
try
{
if (mediaPicker.IsPhotosSupported)
{
var mediaFile = await mediaPicker.SelectPhotoAsync(new CameraMediaStorageOptions
{
MaxPixelDimension = 400
});
ImageSource = mediaFile.Path;
}
}
catch (System.Exception)
{
}
return ImageSource;
}
However when I trigger the function in iPad it look like this:
The gallery only shows up as the size of an iPhone4 and there seems to be no where to change the frame or size. All I want is to display a full screen for it.
It worked well in android tablets.
Is there a work around?