Using device back pressed in fragment Xamarin Android? - android-fragments

I have an activity, and three fragments are called from it. Now, I want to use Back Press method to come back to the previous fragment or screen in Xamarin.Android? How, can I achieve this??

This will do the job:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using SupportTolbar = Android.Support.V7.Widget.Toolbar;
using Android.Support.V7.App;
using Android.Support.V4.Widget;
using Android.Graphics;
using SupportFragment = Android.Support.V4.App.Fragment;
using Android.Content.PM;
using Android.Graphics.Drawables;
using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json;
namespace App
{
[Activity(Label = "", Theme = "#style/MyTheme", ScreenOrientation = ScreenOrientation.Portrait)]
public class HomeActivity : AppCompatActivity
{
//Views that switch the fragments
public ImageView _viewTab1;
public ImageView _viewTab2;
public ImageView _viewTab3;
//fragmentContainer
private FrameLayout _frameLayoutFragmentContainer;
//fragments
public SupportFragment _currentFragment;
public SupportFragment _customFragment1;
public SupportFragment _customFragment2;
public SupportFragment _customFragment3;
private Stack<SupportFragment> _StackFragment;
protected async override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
_frameLayoutFragmentContainer = FindViewById<FrameLayout>(Resource.Id.frameContainer);
//initialize views that switch fragments
_viewTab1 = FindViewById<ImageView>(Resource.Id.viewTab1);
_viewTab2 = FindViewById<ImageView>(Resource.Id.viewTab2);
_viewTab3 = FindViewById<ImageView>(Resource.Id.viewTab3);
//catch events on button click what ever is the thing that switches the fragments
_viewTab1.SetOnClickListener(new TabOnClickListener(this));
_viewTab2.SetOnClickListener(new TabOnClickListener(this));
_viewTab3.SetOnClickListener(new TabOnClickListener(this));
//create fragments
_customFragment1 = new Fragment1();
_customFragment3 = new Fragment2();
_customFragment2 = new Fragment3();
_currentFragment = _customFragment1;
_StackFragment = new Stack<SupportFragment>();
var trans = SupportFragmentManager.BeginTransaction();
trans.Add(Resource.Id.frameContainer, _customFragment1, "First Fragment");
trans.Commit();
}
public override void OnBackPressed()
{
base.OnBackPressed();
}
public void ReplaceFragment(SupportFragment fragment) {
if (fragment.IsVisible)
return;
var trans = SupportFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.frameContainer, fragment);
trans.AddToBackStack(null);
trans.Commit();
_currentFragment = fragment;
}
}
public class TabOnClickListener : Java.Lang.Object, View.IOnClickListener
{
private HomeActivity homeActivity;
public TabOnClickListener(HomeActivity homeActivity)
{
this.homeActivity = homeActivity;
}
public void OnClick(View v)
{
switch (v.Id)
{
case Resource.Id.viewTab1:
homeActivity.ReplaceFragment(homeActivity._customFragment1);
break;
case Resource.Id.viewTab2:
homeActivity.ReplaceFragment(homeActivity._customFragment2);
break;
case Resource.Id.viewTab3:
homeActivity.ReplaceFragment(homeActivity._customFragment3);
break;
}
}
}
}

I made static delegate:
static class FragmentOnBackRouter
{
public static Action RegisteredAction { get; set; }
}
what I used here:
public override void OnBackPressed()
{
if (FragmentOnBackRouter.RegisteredAction != null)
FragmentOnBackRouter.RegisteredAction.Invoke();
else
base.OnBackPressed();
}
Then you can just assign delegate - usually assign it in your fragment's OnStart method and then pass null there in OnStop method. Simple. For functions simply use generics (type Function< T >).
I do not use activities much (2-3 per app) so it's easy and if I would have to handle it more complex, you can always create some abstract activity, inherite and override OnBackPressed there.

Related

Load page before content (data from database)

I am creating my first Blazor web application for self education purposes. There is a simple database with data. Dataset is currently rather small. However while clicking on page link it takes some 1-2 seconds to load. Just wondering that how long it would take if dataset would consist of larger amount of items. Is there a way to load page first and then populate the data?
public class EmployeesBase : ComponentBase:
[Inject]
protected IRepository Repository { get; set; }
protected List<BlazorCompanyManager.Data.Employee> employees;
protected override void OnInitialized()
{
this.employees = this.Repository.GetEmployees();
}
public interface IRepository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlazorCompanyManager.Data
{
public interface IRepository
{
public List<Employee> GetEmployees();
public Employee GetEmployee(Guid id);
public bool UpdateEmployee(Employee employee);
public void AddEmployee(Employee employee);
public void DeleteEmployee(Guid id);
}
}
public class Repository : IRepository:
protected readonly ApplicationDbContext dbContext;
public Repository(ApplicationDbContext db)
{
this.dbContext = db;
}
public List<Employee> GetEmployees()
{
return this.dbContext.EmployeeTable.ToList();
}
I have tried to make it work with OnInitializedAsync and other override methods, but got no success so far. Could anyone give some idea on how it can be done?
You''re running an async code block synchronously, thus blocking the UI thread.
this.dbContext.EmployeeTable.ToList()
should look like this:
public async ValueTask<List<Employee>> GetEmployeesAsync()
{
using var dbContext = this.DBContext.CreateDbContext();
var list = await dbContext
.EmployeeeTable
.ToListAsync()
?? new List<TRecord>();
return list;
}
To do this you also need to move to an IDbContextFactory in your Repository. You can no longer rely on a single DbContext.
protected virtual IDbContextFactory<MyDbContext> DBContext { get; set; } = null;
public xxxxxRepository(IConfiguration configuration, IDbContextFactory<MyDbContext> dbContext)
=> this.DBContext = dbContext;
Startup/Program
var dbContext = configuration.GetValue<string>("Configuration:DBContext");
services.AddDbContextFactory<MyDbContext>(options => options.UseSqlServer(dbContext), ServiceLifetime.Singleton);
You component initialization then looks like this.
protected async override void OnInitializedAsyc()
{
this.employees = await this.Repository.GetEmployeesAsync();
}
Data loading will be dependant on your data server, but the UI will be responsive. You may need to consider paging as the data set grows - you can only display so many rows at once so why fetch them all at once!

How to access ViewWillAppear/ViewDidDisappear in ViewRenderer<HybridWebView, WkWebViewRenderer>?

I'm trying to port an Android existing custom ViewRenderer to iOS.
I'm using version 4.8 of Xamarin.Forms.
Specifically, I need to match Android's OnAttachedToWindow & OnDetachedFromWindow.
The Android Code is like the following:
public partial class HybridWebViewRenderer : ViewRenderer<HybridWebView, Android.Webkit.WebView>
{
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
...
}
protected override void OnDetachedFromWindow()
{
base.OnDetachedFromWindow();
...
}
}
According to this link, I would need to override ViewWillAppear which is the iOS equivalent to OnAttachedToWindow.
The custom Renderer for iOS is the following:
public partial class HybridWebViewRenderer : ViewRenderer<HybridWebView, WkWebViewRenderer>, IWKScriptMessageHandler
{
}
But the overrides are not available.
How can I do that?
ViewWillAppear and ViewWillDisappear are the life cycle method of UIViewController.So you could create the custom renderer of ContentPage
in the ContentPage that contains the WebView
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using App1;
using App1.iOS;
[assembly:ExportRenderer(typeof(MainPage),typeof(MyPageRenderer))]
namespace App1.iOS
{
public class MyPageRenderer:PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
MessagingCenter.Send<Object,UIViewController>(this,"ViewWillAppear",this); // send message when the method been called .
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
MessagingCenter.Send<Object, UIViewController>(this, "ViewWillDisappear", this);
}
}
}
in WebViewRenderer
Subscribe the message in the constructor .
MessagingCenter.Subscribe<Object, UIViewController > (this, "ViewWillDisappear",(arg,controller)=> {
// controller is the current UIViewController
});

How save session storage in Xamarin Forms?

i want to create data to all the application
using static System.Net.Mime.MediaTypeNames;
public class Session
{
void btnLoginClick()
{
NombreUsuario = Username.Text;
Application.Current.Properties["name"] = NombreUsuario;
Application.Current.Properties["IsLoggedIn"] = true;
}
}
i have this error
'MediaTypeNames.Application' does not contain a definition for 'Current'
My app:
namespace Dinamic.Trailer.Mobile.Forms
{
public partial class App : Application
{
public App()
{
Locator.ConfigureIoC();
InitializeComponent();
}
protected override async void OnStart()
{
await InitNavigation();
}
//onsleep()//OnStart
private Task InitNavigation()
{
var navigationService = Content.Resolve<MainNavigation>();
return navigationService.InitializeAsync();
}
}
}
Remove this line
using static System.Net.Mime.MediaTypeNames;
And use import the using Xamarin.Forms;
After that you can use Application.Current.Properties["key"] = value;
Use using xamarin.forms namespace while you use Application.Current.Properties for storing data. Either you could use App.Current.Properties if you do not want to add xamarin.forms namespace in your cs file.
Here App refer your App class in App.xaml.cs which inherit from Xamarin.forms.Application. so you can use App.Current.Properties instead Application.Current.Properties.
Thank you.

Xamarin Forms:Prism MVVM:Android:Navigate to specific page when click on Push Notification [duplicate]

I'm new to Xamarin and Prism, so forgive me if I'm missing something obvious. I am following the example of this project in GitHub. However, I am getting the following error when running my Android project.
System.InvalidOperationException: The current type, MyApp.Abstractions.IFacebookManager, is an interface and cannot be constructed. Are you missing a type mapping?
The error is occurring in EntryPage.xaml.g.cs in the LoadFromXaml() method.
[global::Xamarin.Forms.Xaml.XamlFilePathAttribute("Views\\EntryPage.xaml")]
public partial class EntryPage : global::Xamarin.Forms.ContentPage {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "2.0.0.0")]
private void InitializeComponent() {
// Exception thrown here!
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(EntryPage));
}
}
This leads me to believe that I'm not using the IoC container properly, but I can't see what I'm doing differently from the example. The EntryPageViewModel's constructor takes IFacebookManager as a parameter and I understand that Unity should take care of this. I have a breakpoint on the constructor, but it's not being hit. This is a .NET Standard Xamarin solution. I would appreciate any help and guidance. Thank you!
Here's my App.xaml.cs
using MyApp.Abstractions;
using MyApp.Helpers;
using MyApp.Services;
using MyApp.ViewModels;
using MyApp.Views;
using Prism;
using Prism.Ioc;
using Prism.Unity;
using Xamarin.Forms;
namespace MyApp
{
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
ServiceResolver.Instance.Add<ICloudService, AzureCloudService>();
NavigationService.NavigateAsync("NavigationPage/EntryPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<EntryPage, EntryPageViewModel>();
}
}
}
Here's my MainActivity.cs
using Android.App;
using Android.Content.PM;
using Android.OS;
using MyApp.Droid.Services;
using MyApp.Abstractions;
using Android.Content;
using Xamarin.Facebook;
using Xamarin.Forms;
using Prism;
using Prism.Ioc;
namespace MyApp.Droid
{
[Activity(Label = "MyApp", Icon = "#drawable/icon", Theme = "#style/MainTheme", Name = "com.mydomain.myapp.MainActivity", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
ICallbackManager fbCallbackManager;
AndroidLoginProvider loginProvider;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();
global::Xamarin.Forms.Forms.Init(this, bundle);
DependencyService.Register<IFacebookManager, FacebookManager>();
LoadApplication(new App(new AndroidInitializer()));
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
var manager = DependencyService.Get<IFacebookManager>();
if (manager != null)
{
(manager as FacebookManager)._callbackManager.OnActivityResult(requestCode, (int)resultCode, data);
}
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
You are mixing the Xamarin Forms Dependency Service with the one provided with Prism.Unity
Instead of calling DependencyService.Register<IFacebookManager, FacebookManager>();
You need to register it with the AndroidInitializer .
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry container)
{
container.RegisterSingleton<IFacebookManager, FacebookManager>();
}
}
You can then always resolve a dependency manually by calling App.Container.Resolve inside OnActivityResult.
To complete the previous answer that helped me a lot to fix a similar issue:
with prism 7, you need to resolve a dependency via IContainerProvider as follow:
var container = (App.Current as Prism.Unity.PrismApplication).Container;
var manager = container.Resolve<IFacebookManager>();
In this case, Unity is used as the IoC "container"

Katana not finding start up method using reflection

I am working through Scott Allen's MVC 5 Fundamentals course on Pluralsight
The following code should work , but when I browse to localhost:8080 I get a blank page
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Owin.Hosting;
using Owin;
namespace ConsoleApplication1
{
using AppFunc = Func<IDictionary<string, object>, Task>;
class Program
{
static void Main(string[] args)
{
string uri = "http://localhost:8080";
using (WebApp.Start<Startup>(uri)) // Katana Please start, using the configuration from the Startup class and listening on the port given by the uri
{
Console.WriteLine("Started!");
Console.ReadKey();
Console.WriteLine("Stopping!");
}
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<HelloWorldComponent>();
}
}
public class HelloWorldComponent
{
AppFunc _next;
public HelloWorldComponent(AppFunc next)
{
_next = next;
}
// Katana uses reflection to find this Invoke function that matches the AppFunc signature
public Task Invoke(IDictionary<string, object> environment)
{
var response = environment["owin.ResonseBody"] as Stream;
using (var writer = new StreamWriter(response))
{
return writer.WriteAsync("Hello");
}
}
}
}
How do I get it working?
Everything is setup correctly, there is just a small typo in fetching the response object:
var response = environment["owin.ResponseBody"] as Stream;
using (var writer = new StreamWriter(response))
{
return writer.WriteAsync("Hello");
}
Notice the "p" in "ResponseBody"!

Resources