animate page from right to left or left to right? - xamarin.forms

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.

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;
}
}

Run a background process to change backgroundimage of another page using timer in Xamarin.forms

Hi StackOverflow Team,
I am trying to run a background process in my App. This background process should update just Background image on one of the pages in the App every 15 seconds. So far, I tried to create a timer in the App OnStart() method and update the backgroundimage of the page in the BeginInvokeOnMainThread() method but with no success. Can anyone help me with this?
My Code -
{
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 20;
private readonly HomePage homePage;
public App()
{
InitializeComponent();
try
{
MainPage = new MainPage();
homePage = new HomePage();
}
catch(Exception ex)
{
string str = ex.Message;
}
}
protected override void OnStart()
{
if (!stopWatch.IsRunning)
{
stopWatch.Start();
}
Device.StartTimer(new TimeSpan(0, 0, 10), () =>
{
// Logic for logging out if the device is inactive for a period of time.
if (stopWatch.IsRunning && stopWatch.Elapsed.Seconds >= defaultTimespan)
{
//prepare to perform your data pull here as we have hit the 1 minute mark
// Perform your long running operations here.
Device.BeginInvokeOnMainThread(() =>
{
// If you need to do anything with your UI, you need to wrap it in this.
// homePage.BackgroundImageSource = "goldengate.jpg";
homePage.ChangeBackgroundImage();
});
stopWatch.Restart();
}
// Always return true as to keep our device timer running.
return true;
});
}
protected override void OnSleep()
{
//stopWatch.Reset();
}
protected override void OnResume()
{
//stopWatch.Start();
}
//void ChangeHomePageImage()
//{
// Navigation.PushAsync(new HomePage(appBackground));
// Navigation.RemovePage(this);
//}
}
MainPage -
<MasterDetailPage 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"
xmlns:local="clr-namespace:Excercise.Views"
x:Class="Excercise.MainPage" IsPresented="False">
<MasterDetailPage.Master>
<local:MenuPage x:Name="menuPage"></local:MenuPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:HomePage x:Name="homePage"></local:HomePage>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
HomePage -
public partial class HomePage : ContentPage
{
private SQLiteAsyncConnection _connection;
public HomePage()
{
InitializeComponent();
// BindingContext = new HomePageViewModel();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
loadData("");
}
public HomePage(string BackgroundimgPath)
{
InitializeComponent();
// BindingContext = new HomePageViewModel();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
loadData(BackgroundimgPath);
}
public HomePage(string City, string LocationKey, string StateID)
{
InitializeComponent();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
// BindingContext = new HomePageViewModel();
try
{
// Method Calls
}
catch (Exception)
{
DisplayAlert("Error", "There was an error loading this page.", "OK");
}
}
protected override void OnAppearing()
{
this.Title = App.AppTitle;
this.firstStacklayout.Margin = new Thickness(0, (Application.Current.MainPage.Height * 0.25), 0, 0);
base.OnAppearing();
}
you are creating an instance of HomePage and trying to update it, but it is NOT the same instance that is being displayed in your MasterDetail
try something like this
var md = (MasterDetailPage)MainPage;
var nav = (NavigationPage)md.DetailPage;
var home = (HomePage)nav.CurrentPage;
home.ChangeBackgroundImage();
alternately, you could use MessagingCenter to send a message to HomePage requesting that it udpate

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!

Xamarin Forms Xlabs

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.

Resources