Xamarin Forms Xlabs - xamarin.forms

I want to add a button which should be above the listView as same as how the whatsapp people have done and i want the same thing by using Xamarin Forms, i have tried doing with the xlab PopupLayout but i was unable to fix the position of the button as shown in the image the problem is with the different screen sizes and orientations..
So can any1 help me how to fix the location of the popup by using xlab popuplayout in xamarin forms and it should handle all the screen sizes and orientations.

Have a look at this great post by Alex Dunn. He implements a Floating Action Button (as it is called) on Android and iOS through Xamarin.Forms. It is basic, but you can extend on it yourself.
The gist is you create a control in your shared code, like this:
public partial class FloatingActionButton : Button
{
public static BindableProperty ButtonColorProperty = BindableProperty.Create(nameof(ButtonColor), typeof(Color), typeof(FloatingActionButton), Color.Accent);
public Color ButtonColor
{
get
{
return (Color)GetValue(ButtonColorProperty);
}
set
{
SetValue(ButtonColorProperty, value);
}
}
public FloatingActionButton()
{
InitializeComponent();
}
}
Now on Android implement a custom renderer, like this:
using FAB = Android.Support.Design.Widget.FloatingActionButton;
[assembly: ExportRenderer(typeof(SuaveControls.Views.FloatingActionButton), typeof(FloatingActionButtonRenderer))]
namespace SuaveControls.FloatingActionButton.Droid.Renderers
{
public class FloatingActionButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ViewRenderer<SuaveControls.Views.FloatingActionButton, FAB>
{
protected override void OnElementChanged(ElementChangedEventArgs<SuaveControls.Views.FloatingActionButton> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
var fab = new FAB(Context);
// set the bg
fab.BackgroundTintList = ColorStateList.ValueOf(Element.ButtonColor.ToAndroid());
// set the icon
var elementImage = Element.Image;
var imageFile = elementImage?.File;
if (imageFile != null)
{
fab.SetImageDrawable(Context.Resources.GetDrawable(imageFile));
}
fab.Click += Fab_Click;
SetNativeControl(fab);
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
Control.BringToFront();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var fab = (FAB)Control;
if (e.PropertyName == nameof(Element.ButtonColor))
{
fab.BackgroundTintList = ColorStateList.ValueOf(Element.ButtonColor.ToAndroid());
}
if (e.PropertyName == nameof(Element.Image))
{
var elementImage = Element.Image;
var imageFile = elementImage?.File;
if (imageFile != null)
{
fab.SetImageDrawable(Context.Resources.GetDrawable(imageFile));
}
}
base.OnElementPropertyChanged(sender, e);
}
private void Fab_Click(object sender, EventArgs e)
{
// proxy the click to the element
((IButtonController)Element).SendClicked();
}
}
}
And on iOS, like this:
[assembly: ExportRenderer(typeof(SuaveControls.Views.FloatingActionButton), typeof(FloatingActionButtonRenderer))]
namespace SuaveControls.FloatingActionButton.iOS.Renderers
{
[Preserve]
public class FloatingActionButtonRenderer : ButtonRenderer
{
public static void InitRenderer()
{
}
public FloatingActionButtonRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
// remove text from button and set the width/height/radius
Element.WidthRequest = 50;
Element.HeightRequest = 50;
Element.BorderRadius = 25;
Element.BorderWidth = 0;
Element.Text = null;
// set background
Control.BackgroundColor = ((SuaveControls.Views.FloatingActionButton)Element).ButtonColor.ToUIColor();
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
// add shadow
Layer.ShadowRadius = 2.0f;
Layer.ShadowColor = UIColor.Black.CGColor;
Layer.ShadowOffset = new CGSize(1, 1);
Layer.ShadowOpacity = 0.80f;
Layer.ShadowPath = UIBezierPath.FromOval(Layer.Bounds).CGPath;
Layer.MasksToBounds = false;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "ButtonColor")
{
Control.BackgroundColor = ((SuaveControls.Views.FloatingActionButton)Element).ButtonColor.ToUIColor();
}
}
}
}
You should now be able to use your button from XAML and code as you like.
Here is the XAML sample:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:SuaveControls.FabExample" xmlns:controls="clr-namespace:SuaveControls.Views;assembly=SuaveControls.FloatingActionButton" x:Class="SuaveControls.FabExample.MainPage">
<StackLayout Margin="32">
<Label Text="This is a Floating Action Button!" VerticalOptions="Center" HorizontalOptions="Center"/>
<controls:FloatingActionButton x:Name="FAB" HorizontalOptions="CenterAndExpand" WidthRequest="50" HeightRequest="50" VerticalOptions="CenterAndExpand" Image="ic_add_white.png" ButtonColor="#03A9F4" Clicked="Button_Clicked"/>
</StackLayout>
</ContentPage>
Please note that all credits for this go out to Alex. All his code for this is up here. In the past I have also used the NControls code code to create something like this. And I'm sure there are more awesome libraries out there. However, have a good look at the support for libraries. If I'm not mistake the XLabs packages aren't supported anymore.

Related

C#, Xamarin Forms: No Custom TextChangedEvent Raised on initialization

I'm creating an Xamarin.Forms MVVM App (only using Android) which needs certain buttons to be outlined red, whenever their text property holds a specific value. (Purpose: alert the user to press the button and select a value, which will change the Button Text Property and therefore remove the red outline)
To achieve this I've create the following documents:
A custom button CButton that extents the default Button:
public class CButton : Button
{
// this Hides the Default .Text-Property
public string Text
{
get => base.Text;
set
{
base.Text = value;
TextChangedEvent(this, new EventArgs());
}
}
// The Raised Event
protected virtual void TextChangedEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> handler = TextChanged;
handler(sender, e);
}
public event EventHandler<EventArgs> TextChanged;
}
A custom behavior makes use of the raised TextChangedEvent
public class ButtonValBehavior : Behavior<CButton>
{
protected override void OnAttachedTo(CButton bindable)
{
bindable.TextChanged += HandleTextChanged;
base.OnAttachedTo(bindable);
}
void HandleTextChanged(object sender, EventArgs e)
{
string forbidden = "hh:mm|dd.mm.yyyy";
if (forbidden.Contains((sender as CButton).Text.ToLower()))
{
//Do when Button Text = "hh:mm" || "dd.mm.yyyy"
(sender as CButton).BorderColor = Color.Gray;
}
else
{
//Do whenever Button.Text is any other value
(sender as CButton).BorderColor = Color.FromHex("#d10f32");
}
}
protected override void OnDetachingFrom(CButton bindable)
{
bindable.TextChanged -= HandleTextChanged;
base.OnDetachingFrom(bindable);
}
}
The relevant parts of the ViewModel look the following:
public class VM_DIVI : VM_Base
{
public VM_DIVI(O_BasisProtokoll base)
{
Base = base;
}
private O_BasisProtokoll _base = null;
public O_BasisProtokoll Base
{
get => _base;
set
{
_base = value;
OnPropertyChanged();
}
}
Command _datePopCommand;
public Command DatePopCommand
{
get
{
return _datePopCommand ?? (_datePopCommand = new Command(param => ExecuteDatePopCommand(param)));
}
}
void ExecuteDatePopCommand(object param)
{
//launch popup
var p = new PP_DatePicker(param);
PopupNavigation.Instance.PushAsync(p);
}
}
The .xmal looks the following (b is the xmlns of the Namespace):
<b:CButton x:Name="BTN_ED_Datum"
Text="{Binding Base.ED_datum, Mode=TwoWay}"
Grid.Column="1"
Command="{Binding DatePopCommand}"
CommandParameter="{x:Reference BTN_ED_Datum}">
<b:CButton.Behaviors>
<b:ButtonValBehavior/>
</b:CButton.Behaviors>
</b:CButton>
This solution works fine whenever the input is caused by user interaction. However, when a Value is assigned during the initialization of the Page no red outline is created, in fact the TextChangedEvent isn't raised. By using breakpoints I noticed that during initialization the Text Property of CButton is never set, eventhough it actually will be in the view.
Despite fiddling around with my solution I cannot make this work on initialization. I tried to work around this issue by outlining every button by default in their constructor, however this will outline every button red, even when their text value doesn't require them to be.
How can I achieve my initial goal?
Many thanks in advance!
It's been a while but if I recall correctly what I ended up doing was:
Changing the new Text-Property of my custom Button to CText and
Making sure that I have Mode=TwoWay activated for any Element, that doesn't have it enabled by default. (Look up Binding modes on msdn for more)
making CText a bindable property of CButton
My custom button now looks the following:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace EORG_Anton.Model
{
public class CButton : Button
{
public static readonly BindableProperty CTextProperty =
BindableProperty.Create(nameof(CText),
typeof(string),
typeof(CButton),
default(string),
BindingMode.TwoWay,
propertyChanged: OnTextChanged);
private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (CButton)bindable;
var value = (string)newValue;
control.CText = value;
}
public string CText
{
get => base.Text;
set
{
base.Text = value;
TextChangedEvent(this, new EventArgs());
}
}
protected virtual void TextChangedEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> handler = TextChanged;
handler(sender, e);
}
public event EventHandler<EventArgs> TextChanged;
}
}

iOS 14 DatePicker renderer

I am having some difficulty figuring out how to properly update my existing custom renderer I have for my DatePicker on iOS to display the DatePicker with a different preferred style as is mentioned in this article here (albeit it is for swift) https://medium.com/better-programming/introducing-swifts-new-modern-date-picker-37bb5e0a106
My renderer is as follows:
public class BorderlessDatePickerRenderer : DatePickerRenderer
{
public override void LayoutSubviews()
{
base.LayoutSubviews();
var element = Element as BorderlessDatePicker;
Control.BorderStyle = UITextBorderStyle.None;
if (element.Date.Year == 1900) {
Control.Text = "";
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var element = Element as BorderlessDatePicker;
if (Control != null && element.Date.Year == 1900) {
Control.Text = "";
}
}
The BorderlessDatePicker itself is just an empty class that extends the DatePicker Xamarin.Forms control. The root of my woes is that I am not sure how to properly set a PreferredDatePickerStyle on my Control object given that Control is a UITextField under the hood instead of a UIDatePicker. In essence what I would like to do is instead of displaying the Date picker using the compact style that seems to be default for iOS 14, I would like for it to be displayed as wheels instead by being to do something like:
PreferredDatePickerStyle = UIDatePickerStyle.Wheels;
After some more researching, and browsing the xamarin github, I've come across this solution:
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if(e.NewElement != null && this.Control != null)
{
try
{
if (UIDevice.CurrentDevice.CheckSystemVersion(14, 0))
{
UIDatePicker picker = (UIDatePicker)Control.InputView;
picker.PreferredDatePickerStyle = UIDatePickerStyle.Wheels;
}
} catch (Exception ex)
{
Log.Error(ex, "Failed to set PreferredDatePickerStyle to be UIDatePickerStyle.Wheels for iOS 14.0+");
}
}
}

animate page from right to left or left to right?

in my application i am using PushModalAsync and PopModalAsync for page navigation but page animation is displaying from bottom top and top to bottom respectively. Is there any other option to animate page from right to left or left to right?
Welcome to SO!
If using PushModelAsync , there is a workaround to achieve that effect .
Create a CustomPageRenderer for PageTwo in iOS solution :
[assembly: ExportRenderer(typeof(PageTwo), typeof(CustomPageRenderer))]
namespace XamarinMoelNavigationStyle.iOS
{
public class CustomPageRenderer :PageRenderer
{
public override void ViewWillAppear(bool animated)
{
var transition = CATransition.CreateAnimation();
transition.Duration = 0.5f;
transition.Type = CAAnimation.TransitionPush;
transition.Subtype = CAAnimation.TransitionFromRight;
View.Layer.AddAnimation(transition, null);
base.ViewWillAppear(animated);
}
}
}
We will add animation inside ViewWillAppear method .
When poping to previous MainPage , we can deal with that in ContentPage as follow :
private void Button_Clicked(object sender, EventArgs e)
{
PageTwoView.TranslateTo(300, 0, 500);
Device.StartTimer(TimeSpan.FromSeconds(0.5), () =>
{
// Do something
Navigation.PopModalAsync(false);
return false; // True = Repeat again, False = Stop the timer
});
}
Here PageTwoView is defined from Xaml :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="PageTwoView"
x:Class="XamarinMoelNavigationStyle.PageTwo">
...
</ContentPage>
Note : When MainPage navigate to PageTwo , need to disable the animation .
Such as :
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new PageTwo(), false);
}
The effect :
===============================Update #1==============================
Also create a CustomPageRenderer for PageTwo in Android solution :
public class CustomPageRenderer : PageRenderer
{
public CustomPageRenderer(Context context) : base(context)
{
}
protected override void OnAttachedToWindow()
{
var metrics = Resources.DisplayMetrics;
Android.Views.Animations.Animation translateAnimation = new TranslateAnimation(metrics.WidthPixels, 0, 0, 0);
translateAnimation.Duration = 500;
Animation = translateAnimation;
translateAnimation.Start();
base.OnAttachedToWindow();
}
}
The effect:
===============================Update #2=================================
If there are much ContentPages need to navigate, you can modify the CustomPageRenderer to be used for all ContentPage. As follow:
[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
...
Then using Preferences to set flag in Forms and get flag to know whether need the animation.
private void Button_Clicked(object sender, EventArgs e)
{
PageTwo pageTwo = new PageTwo();
// set a flag here
Preferences.Set("ModelAnimation", true);
Navigation.PushModalAsync(pageTwo, false);
}
Now the renderer will know whehter need the animatin:
//Android
protected override void OnAttachedToWindow()
{
var myValue = Preferences.Get("ModelAnimation", false);
if (myValue)
{
var metrics = Resources.DisplayMetrics;
Android.Views.Animations.Animation translateAnimation = new TranslateAnimation(metrics.WidthPixels, 0, 0, 0);
translateAnimation.Duration = 500;
Animation = translateAnimation;
translateAnimation.Start();
}
base.OnAttachedToWindow();
}
//iOS
public override void ViewWillAppear(bool animated)
{
var myValue = Preferences.Get("ModelAnimation", false);
if (myValue)
{
var transition = CATransition.CreateAnimation();
transition.Duration = 0.5f;
transition.Type = CAAnimation.TransitionPush;
transition.Subtype = CAAnimation.TransitionFromRight;
View.Layer.AddAnimation(transition, null);
}
base.ViewWillAppear(animated);
}
after sliding complete 1st page is open as popup and not looking like first page to second page animation..
About this issue, you can share some code and .gif to explain that.

Xamarin Forms to Xamarin.Android

I have some pages in Xamarin.Forms,since I need to build a custom camera,so I used Xamarin.Android.
now I need to go to Xamarin.Android/iOS from page of Xamarin.Forms and and back to Xamarin.Forms again. how can I do it?
pls help
You don't necessarily need to jump between Xamarin.Forms Views/Pages and Project Specific (iOS/Android) Views manually you can do it by using CustomRenderes which are part of Xamarin.Forms.
Basically you would create a Custom Page on Xamarin.Forms then specifying in each platform project how you want that page to look. You would attach this CustomRender to your Custom Page (the one you made in Xamarin.Forms) and the Framework would do the rest for you.
You can read about Custom Renderers here. And you can see a very similar example of what you are looking right in the Xamarin documentation here.
Hope this helps.-
Welcome to SO !
Baesd on a Xamarin Forms project , you can use Custom Renderer to achieve that .
Such as Creating the Custom Control :
public class CameraPreview : View
{
public static readonly BindableProperty CameraProperty = BindableProperty.Create (
propertyName: "Camera",
returnType: typeof(CameraOptions),
declaringType: typeof(CameraPreview),
defaultValue: CameraOptions.Rear);
public CameraOptions Camera {
get { return (CameraOptions)GetValue (CameraProperty); }
set { SetValue (CameraProperty, value); }
}
}
In Xaml :
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
<ContentPage.Content>
<StackLayout>
<Label Text="Camera Preview:" />
<local:CameraPreview Camera="Rear"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Now you can create the Custom Renderer on each Platform.
Android :
[assembly: ExportRenderer(typeof(CustomRenderer.CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.Droid
{
public class CameraPreviewRenderer : ViewRenderer<CustomRenderer.CameraPreview, CustomRenderer.Droid.CameraPreview>
{
CameraPreview cameraPreview;
public CameraPreviewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomRenderer.CameraPreview> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
cameraPreview.Click -= OnCameraPreviewClicked;
}
if (e.NewElement != null)
{
if (Control == null)
{
cameraPreview = new CameraPreview(Context);
SetNativeControl(cameraPreview);
}
Control.Preview = Camera.Open((int)e.NewElement.Camera);
// Subscribe
cameraPreview.Click += OnCameraPreviewClicked;
}
}
void OnCameraPreviewClicked(object sender, EventArgs e)
{
if (cameraPreview.IsPreviewing)
{
cameraPreview.Preview.StopPreview();
cameraPreview.IsPreviewing = false;
}
else
{
cameraPreview.Preview.StartPreview();
cameraPreview.IsPreviewing = true;
}
}
...
}
}
iOS :
[assembly: ExportRenderer (typeof(CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.iOS
{
public class CameraPreviewRenderer : ViewRenderer<CameraPreview, UICameraPreview>
{
UICameraPreview uiCameraPreview;
protected override void OnElementChanged (ElementChangedEventArgs<CameraPreview> e)
{
base.OnElementChanged (e);
if (e.OldElement != null) {
// Unsubscribe
uiCameraPreview.Tapped -= OnCameraPreviewTapped;
}
if (e.NewElement != null) {
if (Control == null) {
uiCameraPreview = new UICameraPreview (e.NewElement.Camera);
SetNativeControl (uiCameraPreview);
}
// Subscribe
uiCameraPreview.Tapped += OnCameraPreviewTapped;
}
}
void OnCameraPreviewTapped (object sender, EventArgs e)
{
if (uiCameraPreview.IsPreviewing) {
uiCameraPreview.CaptureSession.StopRunning ();
uiCameraPreview.IsPreviewing = false;
} else {
uiCameraPreview.CaptureSession.StartRunning ();
uiCameraPreview.IsPreviewing = true;
}
}
...
}
}
More info can refer to this official sample .

Is it possible to change the border of a single Entry in xamarin forms?

I was wondering if someone can point out how I can change a single entry?. I've made a custom renderer which changes the border of an entry to red but what I really want is only to change one entry if validation fails from black to red.
Picture of entries:
My renderer:
[assembly: ExportRenderer(typeof(App.RedFrameEntry), typeof(RedFrameEntryRenderer))]
namespace App.iOS
{
public class RedFrameEntryRenderer : EntryRenderer
{
public bool isInvalid = false;
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.BorderStyle = UITextBorderStyle.RoundedRect;
Control.Layer.CornerRadius = 4;
Control.Layer.BorderColor = Color.FromHex("#c60303").ToCGColor();
Control.Layer.BorderWidth = 0;
if (isInvalid)
{
Control.Layer.BorderWidth = 2;
}
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}
}
And my code:
private void ChangeEntryOnValidationFail(string text, Entry entry, int numberOfChar)
{
if (String.IsNullOrEmpty(text) || text.Length < numberOfChar)
{
// TODO: Change to RedFrameEntry
}
else
{
// TODO: Change back to default
}
}
I'd suggest you create a CustomEntry class which has a base class of Entry, so instead of changing the border of all entries, you can just call the CustomEntry whenever you need it.
public class CustomEntry : Entry{
}
then use:
[assembly: ExportRenderer(typeof(CustomEntry), typeof(RedFrameEntryRenderer))]
Hope it helps!

Resources