Implement Prism in existing Xamarin.Forms Shared Application - c#

I would like to know of there is a valid way to implement Prism in a existing Xamarin.Forms Shared project. I'm sure others would also like to know if they can profit from what Prism has to offer without having to convert their existing project to a PCL project. All existing examples I've found show a PCL project(probably for a good reason).
To try implementing Prism in my project I installed the Prims.Unity.Forms nuget to each platform project.
I tried to inherit from PrismApplication:
public partial class App : PrismApplication
{ }
But, this does not work. The app class does not allow me to inherit from a different base class.
Adding the following lines to my android project did not help:
protected override void OnCreate(Bundle bundle)
{
LoadApplication(new App(new AndroidInitializer()));
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{ }
}
Perhaps I am missing something or trying something that structurally is not possible.

Yes, it's completely possible.
First, ensure nuget packages are installed.
For example, for Prism and Unity you need:
<package id="Prism.Core" version="6.3.0" ... />
<package id="Prism.Forms" version="6.3.0" .../>
<package id="Prism.Unity.Forms" version="6.3.0" ... />
<package id="Unity" version="4.0.1" ... />
Add missing folders(just for order)
And move your existing page to the Views folder, but remember to
adjust the namespaces or your binding just won't work.
Change application base type
Remember to change application base type in code and XAML.
using Prism.Unity;
using Xamarin.Forms;
namespace XamPrismShared
{
public partial class App : PrismApplication
{
public App (IPlatformInitializer platformInitializer):base(platformInitializer)
{
}
}
}
Set the first page and its ViewModel
Implement OnInitialized and RegisterTypes. Remember that you need to register each type you want to use with Prism as a page.
using Prism.Unity;
using Xamarin.Forms;
namespace XamPrismShared
{
public partial class App : PrismApplication
{
public App (IPlatformInitializer platformInitializer):base(platformInitializer)
{
}
protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync("MainPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<NavigationPage>();
Container.RegisterTypeForNavigation<MainPage>();
}
}
}
If you have existing pages
Add ViewModelLocator.AutowireViewModel="True"to your existent views in order to allow Prism to automatically bind with their respective ViewModel.
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True" x:Class="x:Class="XamPrismShared.Views.MainPage"" Title="MainPage">
<Label Text="{Binding Title}"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentPage>
Add the missing ViewModel
using Prism.Mvvm;
namespace XamPrismShared.ViewModels
{
public class MainPageViewModel : BindableBase
{
public MainPageViewModel()
{
Title = "Hi from Prism.";
}
public string Title { get; set; }
}
}
Add Platform initializers in each platform project
Add the missing platform initializers and fix Xamarin.Forms load.
For Android,
using Android.App;
using Android.Content.PM;
using Android.OS;
using Microsoft.Practices.Unity;
using Prism.Unity;
namespace XamPrismShared.Droid
{
[Activity (Label = "XamPrismShared", Icon = "#drawable/icon", Theme="#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate (Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.Init (this, bundle);
LoadApplication (new XamPrismShared.App(new AndroidPlatformInitializer()));
}
}
public class AndroidPlatformInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
}
For iOS,
using Foundation;
using Microsoft.Practices.Unity;
using Prism.Unity;
using UIKit;
namespace XamPrismShared.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new XamPrismShared.App(new iOSPlatformInitializer()));
return base.FinishedLaunching (app, options);
}
}
public class iOSPlatformInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
}
And voilà
If you have any question or want to check, you can review the code in Github https://github.com/jesulink2514/Xamarin-Prism-shared-project

Related

Xamarin.Forms: App does not show elements on the page

I am currently learning to use Xamarin.Forms and C# (first week into it) and am trying to create a basic login app.
I started with a blank template. Now, when I run my app, only the basic screen pops up, but not the elements in the page. What is wrong?
App.xaml.cs
using Xamarin.Forms;
namespace XamarinApp
{
public partial class App : Application
{
public App()
{
InitializeComponent();
new LoginPage();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
LoginPage.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"
x:Class="XamarinApp.LoginPage">
<ContentPage.Content>
<StackLayout>
<Label
Text="Welcome"
/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
LoginPage.xaml.cs
using Xamarin.Forms;
namespace XamarinApp
{
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
}
}
TL-DR
I believe that to solve your problem you will need to change your line:
new LoginPage();
to:
MainPage = new LoginPage();
My reasoning
You need to specify what the MainPage is. This part can be seen as confusing when you first start out because the example page that is supplied is also called MainPage but one is a class/Page implementation and the other key part is a property provided by the Application class that your App class inherits from. So that the app when running knows it's starting Page.
Typically when you create a new Blank Xamarin.Forms app you will see the following code in App.xaml.cs
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
I suspect that your changes to add in your LoginPage somehow ended up removing the key part: MainPage = from that line.

MvvmCross + Xamarin.Forms "There is no implicit reference conversion..."

I am following the official MvvmCross Xamarin.Forms tutorial (link), and have the following error in Visual Studio:
The type 'PriceWatch.Forms.UWP.App' cannot be used as type parameter
'TApplication' in the generic type or method
'MvxWindowsApplication'. There is no implicit reference conversion from
'PriceWatch.Forms.UWP.App' to 'MvvmCross.ViewModels.IMvxApplication'.
The line of code that is highlighted is the following:
public abstract class PriceWatchApp : MvxWindowsApplication<MvxFormsWindowsSetup<App, FormsApp>, App, FormsApp>
{
}
I've double checked my code and the code from their official tutorial, and I can't spot any inconsistencies besides using "PriceWatch" namespace instead of "TipCalc"
Here is a screenshot of my Core, UI, and Xamarin.Forms UWP projects.
And here are some of the relevant code, if any of the other files in the solution need to be seen, just ask.
PriceWatch.Core.App.cs
namespace PriceWatch.Core
{
public class App : MvxApplication
{
public override void Initialize()
{
Mvx.IoCProvider.RegisterType<IDatabaseService, DatabaseService>();
RegisterAppStart<ProductsViewModel>();
}
}
}
PriceWatch.Forms.UI.FormsApp.xaml.cs
using Xamarin.Forms;
namespace PriceWatch.Forms.UI
{
public partial class FormsApp : Application
{
public FormsApp()
{
InitializeComponent();
}
}
}
PriceWatch.Forms.UWP.App.xaml.cs
using MvvmCross.Forms.Platforms.Uap.Core;
using MvvmCross.Forms.Platforms.Uap.Views;
using PriceWatch.Forms.UI;
using Xamarin.Forms;
namespace PriceWatch.Forms.UWP
{
public sealed partial class App
{
public App()
{
InitializeComponent();
}
}
public abstract class PriceWatchApp : MvxWindowsApplication<MvxFormsWindowsSetup<App, FormsApp>, App, FormsApp>
{
}
}

Xamarin AndroidX.Preferences - Cannot convert from 'SettingsFragment' to 'Android.Support.V4.App.Fragment'

I'm trying to update the settings/preferences screen of my Xamarin Android app, which was using the PreferenceManager class. Since I'm targeting Android 10, this class is deprecated and I'd like to use the AndroidX.Preference library instead.
I followed Google's guide for making a settings screen with AndroidX. Here's the code I'm using
namespace SmartLyrics
{
[Activity(Label = "Settings", ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.Orientation, ScreenOrientation = Android.Content.PM.ScreenOrientation.Portrait)]
public class SettingsFragment : PreferenceFragmentCompat
{
public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
{
AddPreferencesFromResource(Resource.Xml.perfs);
}
}
public class SettingsActivity : AppCompatActivity
{
protected override async void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.main_settings);
SupportFragmentManager
.BeginTransaction()
.Replace(Resource.Id.settingsContainer, new SettingsFragment()) //<--error here
.Commit();
}
}
}
I'm getting the error Cannot convert from 'SmartLyrics.SettingsFragment' to 'Android.Support.V4.App.Fragment'. I looked around online and couldn't find a workaround or anything about this error. I'd really like to use the AndroidX library to prevent any problems with unsupported code later on.
Any help is appreciated. Thanks!
Check which package AppCompatActivity refers to ?
If it:
using Android.Support.V7.App;
change it to :
using AndroidX.AppCompat.App;

How can I change style of the original tabbed page bar (android with xamarin.forms)?

good morning,
I'm designing my first app in xamarin.forms, but I ran into a problem that is the style of the tabbedpage bar in android.
On iOS it works great, but on Android it has a different display, but I would like to make the homogeneous thing on both operating systems know how to do it?
Screen Android https://ibb.co/cYRLvc4
Screen iOS https://ibb.co/ysnxBr8
You should use a custom effect, first update your android target to 9.0 (and support libraries)
then, create the Routing Effect in your Forms solution:
using Xamarin.Forms;
namespace YourApp
{
public class NoShiftEffect : RoutingEffect
{
public NoShiftEffect() : base("MyEffect.NoShiftEffect")
{
}
}
}
Then, in Android, create a custom renderer:
using Android.Support.Design.BottomNavigation;
using Android.Support.Design.Widget;
using Android.Views;
using yournamespace.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:ResolutionGroupName ("MyEffect")]
[assembly:ExportEffect (typeof(NoShiftEffect), "NoShiftEffect")]
namespace yournamespace.Droid
{
public class NoShiftEffect : PlatformEffect
{
protected override void OnAttached ()
{
if (!(Container.GetChildAt(0) is ViewGroup layout))
return;
if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
return;
// This is what we set to adjust if the shifting happens
bottomNavigationView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilityLabeled;
}
protected override void OnDetached ()
{
}
}
}
finally, in your tabbde page:
<TabbedPage.Effects>
<local:NoShiftEffect />
</TabbedPage.Effects>

How to load default view in Prism WPF C#

I am using this Repo:
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/17-BasicRegionNavigation
It works, but I need to make changes:
when run the app, I must click to the selected view to load, so I need to show the default view.
After click
the Bootstrapper.cs file code:
using Microsoft.Practices.Unity;
using Prism.Unity;
using NavigationParticipation.Views;
using System.Windows;
using Prism.Modularity;
using ModuleA;
namespace NavigationParticipation
{
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureModuleCatalog()
{
var catalog = (ModuleCatalog)ModuleCatalog;
catalog.AddModule(typeof(ModuleAModule));
}
}
}
Folder Structure (Solution)
And how to show The Login window Or another window before initialize the MainWindow?
One of the way is to add CompositeCommand somewhere outside modules so they all can use it...
public class ApplicationCommands
{
public static CompositeCommand NavigateCommand = new CompositeCommand();
}
and then if you wanna ViewA to be default
inside ModuleAModule in method Initialize() add
ApplicationCommands.NavigateCommand.Execute(typeof(ViewA));
if you want ViewB to be default then inside ModuleBModule in method Initialize() add
ApplicationCommands.NavigateCommand.Execute(typeof(ViewB));
There are couple more ways, but dont know your logic and all code used, so this one seems the easiest one.
and if you are using IRegionManager then you can do something like this.
_regionManager.RegisterViewWithRegion("YourRegionName", typeof(ViewA));
I solve A part of the Problem :
by adding this code to :
17-BasicRegionNavigation/ModuleA/ModuleAModule.cs
this code:
_regionManager.RequestNavigate("ContentRegion", new Uri("ViewA", UriKind.Relative));
where: ViewA correspond to the Target view that you want to show.
and the ContentRegion correspond to your MainRegion, you can find it in :
17-BasicRegionNavigation/BasicRegionNavigation/ViewModels/MainWindowViewModel.cs
and looks like:
using Microsoft.Practices.Unity;
using ModuleA.Views;
using Prism.Modularity;
using Prism.Regions;
using Prism.Unity;
using System;
namespace ModuleA
{
public class ModuleAModule : IModule
{
IRegionManager _regionManager;
IUnityContainer _container;
public ModuleAModule(RegionManager regionManager, IUnityContainer container)
{
_regionManager = regionManager;
_container = container;
}
public void Initialize()
{
_container.RegisterTypeForNavigation<ViewA>();
_container.RegisterTypeForNavigation<ViewB>();
_regionManager.RequestNavigate("ContentRegion", new Uri("ViewA", UriKind.Relative));
}
}
}
Now How To Add Login Window before MainWindow initialized ??

Categories

Resources