Image doesn't appear in custom view renderer - xamarin

I've this cusom view in shared project:
public class CustomAdView : View
{
}
then I add it in the main page:
var adView = new CustomAdView();
Grid.SetRowSpan(adView, 4);
_mainGrid.Children.Add(adView);
// here if I add an Image instead, it shows without problem.
Then in the android project, I've a custom renderer for that CustomAdView like this:
[assembly: ExportRenderer(typeof(CustomAdView), typeof(CustomAdViewRenderer))]
namespace MyApp.Droid.Renderers
{
public class CustomAdViewRenderer : ViewRenderer<CustomAdView, Android.Views.View>
{
public CustomAdViewRenderer(Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomAdView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control == null)
SetNativeControl(CreateAdView());
}
// ...
private Android.Views.View CreateAdView()
{
try
{
var url = RequestImageUrlFromServer();
var imageView = new ImageView(Context);
imageView.Click += (o, e) =>
{
Device.OpenUri(new Uri("http://www.google.com"));
};
imageView.SetImageURI(Android.Net.Uri.Parse(url));
imageView.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.FillParent, LayoutParams.FillParent);
return imageView;
}
catch (Exception)
{
return null;
}
}
}
}
CreateAdView() returns imageView correctly. If I click on the screen, it responds to click event and opens the browser, but the problem is that it doesn't show the image!
I guess I set ImageView source incorrectly. Image url is something like this:
https://addeals.s3.amazonaws.com/campaigns/pub8/testing/768x1024_hangman_test.jpg
Any help is much appreciated.

You can refer the following code
var iamgeview = new ImageView(Context);
URL url = new URL("xxx");
Bitmap pngBP = BitmapFactory.DecodeStream(url.OpenStream());
iamgeview.SetImageBitmap(pngBP);

Related

Get and Display path of Chosen photo from gallery in Xamarin forms

I am using a Dependency service to pick a photo from the gallery. and I want to show the path when the user selects an image from their phone in a Label.
I have read too many logs but not getting the proper results.
I want it like this:
Now the selected image is displayed properly but what I don't get is how to display the path of the selected image.
Please suggest me how to do it for both android and ios.
Note: I'm using Dependency service for it so I don't want third-party plugins.
I hope I will get a better solution for this.
Thanks in advance.
Creating the interface in forms
namespace xxx
{
public interface IPhotoPickerService
{
Task<Dictionary<string,Stream>> GetImageStreamAsync();
}
}
in iOS
[assembly: Dependency (typeof (PhotoPickerService))]
namespace xxx.iOS
{
public class PhotoPickerService : IPhotoPickerService
{
TaskCompletionSource<Dictionary<string, Stream>> taskCompletionSource;
UIImagePickerController imagePicker;
Task<Dictionary<string, Stream>> IPhotoPickerService.GetImageStreamAsync()
{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
};
// Set event handlers
imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;
// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);
// Return Task object
taskCompletionSource = new TaskCompletionSource<Dictionary<string, Stream>>();
return taskCompletionSource.Task;
}
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data;
if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
{
data = image.AsPNG();
}
else
{
data = image.AsJPEG(1);
}
Stream stream = data.AsStream();
UnregisterEventHandlers();
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(args.ImageUrl.ToString(), stream);
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(dic);
}
else
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
void OnImagePickerCancelled(object sender, EventArgs args)
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
void UnregisterEventHandlers()
{
imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
imagePicker.Canceled -= OnImagePickerCancelled;
}
}
}
in Android
in MainActivity
public class MainActivity : FormsAppCompatActivity
{
...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<Dictionary<string,Stream>> PickImageTaskCompletionSource { set; get; }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.ToString(), stream);
// Set the Stream as the completion of the Task
PickImageTaskCompletionSource.SetResult(dic);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
[assembly: Dependency(typeof(PhotoPickerService))]
namespace xxx.Droid
{
public class PhotoPickerService : IPhotoPickerService
{
public Task<Dictionary<string,Stream>> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
// Start the picture-picker activity (resumes in MainActivity.cs)
MainActivity.Instance.StartActivityForResult(
Intent.CreateChooser(intent, "Select Picture"),
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Dictionary<string,Stream>>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}
invoke it
Dictionary<string, Stream> dic = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
Stream stream;
string path;
foreach ( KeyValuePair<string, Stream> currentImage in dic )
{
stream = currentImage.Value;
path = currentImage.Key;
label.Text = path;
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream);
}
}
Update
If you want to get the path , you could invoke
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.Path, stream);

How to set badge counter above the toolbaritem in Xamarin.Forms?

I am creating an application where I need to implement a badge counter above the icon in ToolbarItem. I am creating a dependancy service for IOS and Android. I reference this URL.
After implementing this I am getting a perfect result in Android, but when I try to run it on an IOS device I'm not able to use that. In that
var rightButtonItems = vc?.ParentViewController?.NavigationItem?.RightBarButtonItems;
always returns null so I am not able to use and set badge counter above the application.
I am using .Net standard class library.
I have the same issue as here.
My code:
Badge.xaml
<ContentPage.ToolbarItems>
<ToolbarItem Icon="notification" Name="MenuItem1" Order="Primary" Priority="0" Text="Notification" Clicked="ToolbarItem_Clicked"/>
</ContentPage.ToolbarItems>
Badge.xaml.cs
private void ToolbarItem_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Notification());
}
private void ContentPage_Appearing(object sender, EventArgs e)
{
base.OnAppearing();
ViewModel.AppearingCommand?.Execute(null);
if (ToolbarItems.Count > 0)
DependencyService.Get<IToolbarItemBadgeService>().SetBadge(this, ToolbarItems.First(), "4", Color.Red, Color.White);
}
IToolbarItemBadgeService.cs(In shared project)
public interface IToolbarItemBadgeService
{
void SetBadge(Page page, ToolbarItem item, string value, Color backgroundColor, Color textColor);
}
ToolbarItemBadgeService.cs(In iOS project)
[assembly: Dependency(typeof(ToolbarItemBadgeService))]
namespace CustomerServiceApp.iOS
{
public class ToolbarItemBadgeService : IToolbarItemBadgeService
{
public void SetBadge(Page page, ToolbarItem item, string value, Color backgroundColor, Color textColor)
{
Device.BeginInvokeOnMainThread(() =>
{
var renderer = Platform.GetRenderer(page);
if (renderer == null)
{
renderer = Platform.CreateRenderer(page);
Platform.SetRenderer(page, renderer);
}
var vc = renderer.ViewController;
var rightButtonItems = vc?.ParentViewController?.NavigationItem?.RightBarButtonItems;//Here i get null in rightButtonItems
// If we can't find the button where it typically is check the child view controllers
// as this is where MasterDetailPages are kept
if (rightButtonItems == null && vc.ChildViewControllerForHomeIndicatorAutoHidden != null)//vc.ChildViewControllerForHomeIndicatorAutoHidden is also return null
foreach (var uiObject in vc.ChildViewControllerForHomeIndicatorAutoHidden)
{
string uiObjectType = uiObject.GetType().ToString();
if (uiObjectType.Contains("FormsNav"))
{
UIKit.UINavigationBar navobj = (UIKit.UINavigationBar)uiObject;
if (navobj.Items != null)
foreach (UIKit.UINavigationItem navitem in navobj.Items)
{
if (navitem.RightBarButtonItems != null)
{
rightButtonItems = navitem.RightBarButtonItems;
break;
}
}
}
}
var idx = page.ToolbarItems.IndexOf(item);
if (rightButtonItems != null && rightButtonItems.Length > idx)
{
var barItem = rightButtonItems[idx];
if (barItem != null)
{
barItem.UpdateBadge(value, backgroundColor.ToUIColor(), textColor.ToUIColor());
}
}
});
}
}
}
Can anyone look into this and suggest me what should I have to do in that?

How can I change the font for the header of a Navigation page with Xamarin Forms?

I can change the font color like this:
var homePage = new NavigationPage(new HomePage())
{
Title = "Home",
Icon = "ionicons_2_0_1_home_outline_25.png",
BarTextColor = Color.Gray,
};
But is there a way to change the font for the Title. I would like to change it for the iOS and Android platforms only. Hoping that someone knows of custom renderer code that can help me to do this.
You need Custom Renderer , refer to this sample
iOS
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustomNavigationPageRenderer))]
namespace CustomFontsNavigationPage.iOS.Renderers
{
public class CustomNavigationPageRenderer : NavigationRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var att = new UITextAttributes();
UIFont customFont = UIFont.FromName("Trashtalk", 20);
UIFont systemFont = UIFont.SystemFontOfSize(20.0);
UIFont systemBoldFont = UIFont.SystemFontOfSize(20.0 , FontAttributes.Bold);
att.Font = font;
UINavigationBar.Appearance.SetTitleTextAttributes(att);
}
}
}
}
Android
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustomNavigationPageRenderer))]
namespace CustomFontsNavigationPage.Droid.Renderers
{
public class CustomNavigationPageRenderer : NavigationPageRenderer
{
private Android.Support.V7.Widget.Toolbar _toolbar;
public override void OnViewAdded(Android.Views.View child)
{
base.OnViewAdded(child);
if (child.GetType() == typeof(Android.Support.V7.Widget.Toolbar))
{
_toolbar = (Android.Support.V7.Widget.Toolbar)child;
_toolbar.ChildViewAdded += Toolbar_ChildViewAdded;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if(disposing)
{
_toolbar.ChildViewAdded -= Toolbar_ChildViewAdded;
}
}
private void Toolbar_ChildViewAdded(object sender, ChildViewAddedEventArgs e)
{
var view = e.Child.GetType();
if (e.Child.GetType() == typeof(Android.Widget.TextView))
{
var textView = (Android.Widget.TextView)e.Child;
var spaceFont = Typeface.CreateFromAsset(Forms.Context.ApplicationContext.Assets, "Trashtalk.ttf");
var systemFont = Typeface.DEFAULT;
var systemBoldFont = Typeface.DEFAULT_BOLD;
textView.Typeface = spaceFont;
_toolbar.ChildViewAdded -= Toolbar_ChildViewAdded;
}
}
}
}
There is no need in a custom renderer on iOS, you can just use the Appearance API:
UINavigationBar.Appearance.SetTitleTextAttributes(new UITextAttributes
{
Font = UIFont.FromName("MyCoolFont", 20)
});
In Android you do need a renderer, however you should check against Android.Support.V7.Widget.AppCompatTextView and not Android.Widget.TextView.
Tested on Xamarin.Forms 3.4.0

Xamarin.Forms Xamarin Android

SaveFileDialog in XamarinForms
My requirement is to save a file in Xamarin.Forms like below image and what ever I tried so far is:
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged (e);
if (e.NewElement != null)
{
var customWebView = Element as CustomWebView;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
string root = Android.OS.Environment.ExternalStorageDirectory.ToString();
// Java.IO.File myDir = new Java.IO.File(root + "/WingsPdfs");
// myDir.Mkdir();
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Java.IO.File file = new Java.IO.File(path, "WingsPdfGenerated.pdf");
FileOutputStream outs = new FileOutputStream(file);
outs.Write(customWebView.PdfStream.ToArray());
outs.Flush();
outs.Close();
try
{
Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", file));
}
catch(Exception ex)
{
}
}
}
}
In the above code I hardcoded the location of the file but I want user to select particular location and save to the selected location.
Thank You
enter image description here

Removing Padding from Entry

So Entry does not have a padding attribute, however there is some definite padding that goes on the Entry.
Example
I have the "Michigan" Entry lined up with the "Select" Label below, however they look misaligned because the entry has some padding to the left. I tried the margin attribute that entry does have, however it did not work.
How do I get rid of that gap/padding?
I'd like to add that adding an offset margin does not working.
You need to make a custom renderer for the entry and set the Android EditText's PaddingLeft to 0 using the SetPadding method.
Excerpt from CustomEntryRenderer on Android:
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement == null) return;
Control.SetPadding(0, Control.PaddingTop, Control.PaddingRight, Control.PaddingBottom);
}
For me the custom render that worked was:
[assembly: Xamarin.Forms.ExportRenderer(typeof(MyApp.Views.Controls.CustomEntry), typeof(MyApp.Droid.Views.Controls.CustomRenderer.Android.CustomEntryRenderer))]
namespace MyApp.Droid.Views.Controls
{
namespace CustomRenderer.Android
{
public class CustomEntryRenderer : EntryRenderer
{
public CustomEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Background = new ColorDrawable(Color.Transparent);
Control.SetPadding(0, 0, 0, 0);
Control.Gravity = GravityFlags.CenterVertical | GravityFlags.Left;
Control.TextAlignment = TextAlignment.Gravity;
}
}
}
}
}
I was struggling with the same issue, but I solved it by creating a custom Entry type which adds a Padding property to Xamarin Forms' Entry:
public class CustomEntry : Entry
{
public static readonly BindableProperty PaddingProperty =
BindableProperty.Create(
nameof(Padding),
typeof(Thickness),
typeof(CustomEntry),
new Thickness());
public Thickness Padding
{
get { return (Thickness)this.GetValue(PaddingProperty); }
set { this.SetValue(PaddingProperty, value); }
}
}
Then I render this CustomEntry with a custom renderer, just as Danilow proposed, with the only difference, that I read the PaddingProperty from CustomEntry and apply it in the CustomEntryRenderer.
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
namespace CrossPlatformLibrary.Forms.Android.Renderers
{
public class CustomEntryRenderer : EntryRenderer
{
public CustomEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (this.Element is CustomEntry customEntry)
{
var paddingLeft = (int)customEntry.Padding.Left;
var paddingTop = (int)customEntry.Padding.Top;
var paddingRight = (int)customEntry.Padding.Right;
var paddingBottom = (int)customEntry.Padding.Bottom;
this.Control.SetPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
}
}
}
Beware: This code needs to be extended if you want to react on PaddingProperty changes - AND - you will need to write a custom renderer for IOS if you want to support the Padding property there too.
Here's the same thing implemented on iOS for anyone who needs it there too. It's basically setting the left view to have 0 width that does it, but you can also play with the "LeftViewMode" to hide it completely.
this.Control.LeftView = new UIView(new CGRect(0, 0, 0, this.Control.Frame.Height));
Full code
using CoreGraphics;
using UIKit;
using YourNamespace.iOS.CustomRenderers;
using YourNamespace.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(BorderlessEntry), typeof(BorderlessEntryRenderer))]
namespace YourNamespace.iOS.CustomRenderers
{
public class BorderlessEntryRenderer : Xamarin.Forms.Platform.iOS.EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
this.Control.LeftView = new UIView(new CGRect(0, 0, 0, this.Control.Frame.Height));
this.Control.LeftViewMode = UITextFieldViewMode.Always;
}
}
}
}

Resources