I'm new to Xamarin.Forms and i can't seems to find how to show the dialog in a 24hour format instead of a AM/PM
can someone help me ?
Thank you
This page is really usefull to learn how to format dates in C#:
https://msdn.microsoft.com/en-us/library/8kb3ddd4%28v=vs.110%29.aspx
And with this code I was able to show a 24 hour format:
TimePicker timePicker = new TimePicker
{
Format = "HH:mm"
};
The only solution i found is here:
24h timepicker
Basically a custom renderer within each platform project will be needed.
Xamarin.iOS:
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using PersonalTrainer.iOS.View.Controls;
[assembly: ExportRenderer (typeof (TimePicker), typeof (TimePicker24HRenderer))]
namespace YourNamespace.iOS.View.Controls {
public class TimePicker24HRenderer : TimePickerRenderer {
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e) {
base.OnElementChanged(e);
var timePicker = (UIDatePicker)Control.InputView;
timePicker.Locale = new NSLocale("no_nb");
}
}
}
For Android:
[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePicker24HRenderer))]
namespace YourNamespace.Droid.View.Controls {
public class TimePicker24HRenderer : ViewRenderer<Xamarin.Forms.TimePicker, Android.Widget.EditText>, TimePickerDialog.IOnTimeSetListener, IJavaObject, IDisposable {
private TimePickerDialog dialog = null;
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e) {
base.OnElementChanged(e);
this.SetNativeControl(new Android.Widget.EditText(Forms.Context));
this.Control.Click += Control_Click;
this.Control.Text = DateTime.Now.ToString("HH:mm");
this.Control.KeyListener = null;
this.Control.FocusChange += Control_FocusChange;
}
void Control_FocusChange(object sender, Android.Views.View.FocusChangeEventArgs e) {
if (e.HasFocus) { ShowTimePicker(); }
}
void Control_Click(object sender, EventArgs e) {
ShowTimePicker();
}
private void ShowTimePicker() {
if (dialog == null) {
dialog = new TimePickerDialog(Forms.Context, this, DateTime.Now.Hour, DateTime.Now.Minute, true);
}
dialog.Show();
}
public void OnTimeSet(Android.Widget.TimePicker view, int hourOfDay, int minute) {
var time = new TimeSpan(hourOfDay, minute, 0);
this.Element.SetValue(TimePicker.TimeProperty, time);
this.Control.Text = time.ToString(#"hh\:mm");
}
}
}
For Windows Phone:
[assembly: ExportRenderer (typeof (MyTimePicker), typeof (TimePicker24HRenderer))]
namespace YourNamespace.WindowsPhone.View.Controls {
public class TimePicker24HRenderer : TimePickerRenderer {
// Override the OnElementChanged method so we can tweak this renderer post-initial setup
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.TimePicker> e) {
base.OnElementChanged (e);
if (Control != null) {
var nativeControl = (Windows.UI.Xaml.Controls.TimePicker)Control;
nativeControl.Foreground = new SolidColorBrush(Colors.Gray);
Control.ClockIdentifier = "24HourClock";
}
}
}
}
In XAML you can set the the 24 hours format string to TimePicker by using the Format property:
<TimePicker Format="HH:mm"/>
Ok. You have to do it plateform specific.. I did something like that.
So create an interface
public interface TimePickerInterface
{
void showTimePicker (ButtonOrLabel lbl);
}
In Android you create your timepicker class
[assembly:Dependency(typeof(yournamespace.MyTimePicker))]
namespace yournamespace
{
public class MyTimePicker:Java.Lang.Object,TimePickerInterface,Android.App.TimePickerDialog.IOnTimeSetListener
{
String originalText="";
LabelOrButton lbl=new LabelOrButton();
public void showTimePicker(LabelOrButton lbl)
{
var c = Forms.Context;
originalText = lbl.Text;
this.lbl = lbl;
var time = DateTime.Now.TimeOfDay;
TimePickerDialog dialog = new TimePickerDialog(c,this,time.Hours,time.Minutes,true);
dialog.SetCanceledOnTouchOutside (true);
dialog.Show ();
}
public void OnTimeSet (Android.Widget.TimePicker view, int hourOfDay, int minute)
{
if(view.IsShown)
lbl.Text = (hourOfDay<=9 ? ("0"+hourOfDay+""):hourOfDay+"")+":"+(minute<=9 ? ("0"+minute+""):minute+"");
}
}
}
And in your xamarin forms you call it in actionlistener of your button or gesturerecognizer of your label:
DependencyService.Get<TimePickerInterface> ().showTimePicker(myButtonOrLabel);
You can do the same thing for DatePicker..
Hope it helps..
Related
I want to show long text of picker item in two lines using custom render how can I achieve it?
First , you need a custom renderer for picker .
To override the item you need to replace the original view with a AlertDialog .
Then custom a ListView and set it as AlertDialog.View .
Then you can customize everything in the Adapter(here we need to customize the textview ).
Sample code
[assembly: ExportRenderer(typeof(Picker), typeof(MyPickerRenderer))]
namespace FormsApp.Droid
{
public class MyAdapter : ArrayAdapter
{
private IList<string> _data;
Context _context;
public MyAdapter(Context context, int resource , IList<string> data) : base(context,resource)
{
_context = context;
_data = data;
}
public override int Count => _data.Count;
public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
TextView textview = new TextView(_context);
textview.TextSize = 18;
textview.SetTextColor(Android.Graphics.Color.DarkGray);
textview.Ellipsize = TruncateAt.End;
textview.SetMaxLines(2); //this line
textview.Text = _data[position];
return textview;
}
}
public class MyListView : Android.Widget.ListView
{
public MyListView(Context context, IList<string> data) : base(context)
{
this.DividerHeight = 0;
this.Adapter = new MyAdapter(context, 0, data);
}
}
class MyPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyPickerRenderer(Context context):base(context)
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new MyListView(Context, model.Items);
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.SetPadding(35, 30, 35, 0);
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}
Testing code in Forms xaml
<Picker x:Name="picker" Title="Select a monkey">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>abc</x:String>
<x:String>aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbccccccccccccccccdddddddddddddddddddddd</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
Before
After
I am working in a Xamarin Forms APP, we want to go through different entries (not using TabIndex because we need a custom logic), but we want to keep the keyboard always on during looping through entries. We are using ReturnCommand to Focus the NEXT entry.
Create a CustomEntryRenderer in your Android project :
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
namespace your namespace
class CustomEntryRenderer:EntryRenderer
{
public CustomEntryRenderer(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
}
}
and add this in your MainActivity:
private bool _lieAboutCurrentFocus;
public override bool DispatchTouchEvent(MotionEvent ev)
{
var focused = CurrentFocus;
bool customEntryRendererFocused = focused != null && focused.Parent is CustomEntryRenderer;
_lieAboutCurrentFocus = customEntryRendererFocused;
var result = base.DispatchTouchEvent(ev);
_lieAboutCurrentFocus = false;
return result;
}
public override Android.Views.View CurrentFocus
{
get
{
if (_lieAboutCurrentFocus)
{
return null;
}
return base.CurrentFocus;
}
}
I am using Tabbed page in my xamarin forms project.
I am trying to use OnTabReselected event in MyTabsRenderer Class in android. But the events like OnTabSelected, OnTabReselected and OnTabUnselected are not getting called. Does anyone have any solution for this.
xamarin forms version: 3.2.0.871581
VS version : 15.8.6
Here is my code snippet(MyTabsRenderer Class):
using Android.OS;
using Android.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android.AppCompat;
using MyProject;
using MyProject.Droid;
using Xamarin.Forms.Platform.Android;
using System.ComponentModel;
using Android.Support.V4.View;
using Android.Content.Res;
using Android.Content;
using Android.Support.Design.Widget;
[assembly: ExportRenderer(typeof(TabController), typeof(MyTabsRenderer))]
namespace MyProject.Droid
{
public class MyTabsRenderer : TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
bool setup;
ViewPager viewPager;
TabLayout tabLayout;
public MyTabsRenderer(Context context) : base(context)
{
}
protected override void SetTabIcon(TabLayout.Tab tab, FileImageSource icon)
{
base.SetTabIcon(tab, icon);
}
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
//if (setup)
// return;
if (e.PropertyName == "Renderer" || e.PropertyName == "CurrentPage")
{
viewPager = (ViewPager)ViewGroup.GetChildAt(0);
tabLayout = (TabLayout)ViewGroup.GetChildAt(1);
setup = true;
ColorStateList colors = GetTabColor();
for (int i = 0; i < tabLayout.TabCount; i++)
{
var tab = tabLayout.GetTabAt(i);
SetTintColor(tab, colors);
}
tabLayout.SetOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager));
}
}
void OnTabReselected(TabLayout.Tab tab)
{
// To have the logic only on he tab on position 1
if (tab == null || tab.Position != 1)
{
return;
}
if (tab.Text == "Play")
{
tab.SetText("Pause");
tab.SetIcon(Resource.Drawable.icon);
// App.pauseCard = false;
}
else
{
tab.SetText("Play");
tab.SetIcon(Resource.Drawable.icon);
// App.pauseCard = true;
}
SetTintColor(tab, GetTabColor());
}
void SetTintColor(TabLayout.Tab tab, ColorStateList colors)
{
var icon = tab?.Icon;
if (icon != null)
{
icon = Android.Support.V4.Graphics.Drawable.DrawableCompat.Wrap(icon);
Android.Support.V4.Graphics.Drawable.DrawableCompat.SetTintList(icon, colors);
}
}
ColorStateList GetTabColor()
{
return ((int)Build.VERSION.SdkInt >= 23)
? Resources.GetColorStateList(Resource.Color.icon_tab, Forms.Context.Theme)
: Resources.GetColorStateList(Resource.Color.icon_tab);
}
}
}
It is probably because you are setting the OnTabSelectedListener wrong.
Try setting the listener to this instead: tabLayout.SetOnTabSelectedListener(this);
Also for inspiration check out TabbedPageRenderer.cs source code,
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
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;
}
}
}
}