Hi! I'm Mark Jack Milian and i'm here to aswer a few questions
Platform/Feature | Package name | Stable | Status |
---|---|---|---|
Core | Xam.Zero.Sem |
||
Core | Xam.Zero.DryIoc.Sem |
All packages are compliant with Semantic Versioning
A simple, easy, agile and fluent framework for Xamarin forms which encapsulates all the features of Xamarin by supporting developers with a real MVVM architecture. With Xamarin.Zero you will be able to build up the structure of your app in Zero seconds with no waste of time!
With the continuous and more frequent evolution of Xamarin, it has become necessary having a framework that supports the structure and functionality of the new Shell component, available from Xamarin.Forms 4.
Please add a reference of our nuget (inserire link al nuget) to your Cross-Platform Xamarin.Forms project.
As usual, we have to set the start up point of our application and, like in Xamarin.Forms Vanilla, we can do it by settings a value of MainPage. the fluency of Xamarin Zero permit us to set IoC container, navigation containers and theirs order in one row only:
public partial class App : Application
{
public static readonly Container Container = new Container();
public App()
{
this.InitializeComponent();
ZeroApp.On(this)
.WithContainer(DryIocZeroContainer.Build(Container))
.RegisterShell(() => new AppShell())
.RegisterShell(() => new TabbedShell())
.StartWith<AppShell>();
}
...
}
Where:
- Container is our favourite implementation of an IoC Container;
- ZeroApp is a wrapper around Xamarin.Forms bootstrapping the main Application;
- AppShell and TabbedShell are two sample shells which act as navigation containers. Please see Samples section for further details.
NB: Xamarin Zero will register all Pages and ViewModels in bootstrap time. This will be possible only if every view model will extends the ZeroBaseModel class. The registration is Singleton by default (you can choose for transient using Attribute or builder method); to make it multi-instance style, we have to use the Transient attribute.
there are two mandatory ways to define the binding context of out pages and both use a markup extension mechanism:
- If the page is within a Shell, like in a tabbed or master-detail navigation, we have to specify to the markup both the view and view model locations.
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
x:Name="Home"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:markupExtensions="clr-namespace:Xam.Zero.MarkupExtensions;assembly=Xam.Zero"
xmlns:home="clr-namespace:Xam.Zero.Dev.Features.Home;assembly=Xam.Zero.Dev"
BindingContext="{markupExtensions:ShellPagedViewModelMarkup ViewModel={x:Type home:HomeViewModel}, Page={x:Reference Home}}"
x:Class="Xam.Zero.Dev.Features.Home.HomePage">
...
</ContentPage>
Where ViewModel is the type of the Viewmodel we want to bind to the view and Page is a reference to the ContentPage
- If the page is independent from the Shell, for example after a page push in the navigation stack, you can use markup extension method with just the view model location is requested. Or you can use binding by convention: if no BindingContext with Markup is defined Xam.Zero will look for a ViewModel that is called like: "PageName"+ViewModel. So You can just push a SecondPage and Zero will try to bind with a SecondPageViewModel (nice!). NB ViewModel type must be defined on the same assembly of Page
<?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:markupExtensions="clr-namespace:Xam.Zero.MarkupExtensions;assembly=Xam.Zero"
xmlns:second="clr-namespace:Xam.Zero.Dev.Features.Second;assembly=Xam.Zero.Dev"
BindingContext="{markupExtensions:ViewModelMarkup ViewModel={x:Type second:SecondViewModel}}"
x:Class="Xam.Zero.Dev.Features.Second.SecondPage">
...
</ContentPage>
the markup extension will advise Xamarin Zero about the binding between view and viewModel, making the binding and navigation mechanism very simple to manage.
As said before, the navigation is very easy after markup definition; we just have to make our view model inherit from ZeroBaseModel, in order to gain all the signature we want for navigating (and not only)
Task Push<T>(object data = null, bool animated = true) where T: Page {}
Task PushModal<T>(object data = null, bool animated = true) where T: Page {}
Task Pop(object data = null, bool animated = true) {}
Task PopModal(object data = null, bool animated = true) {}
With the useful signatures Push and Pop, we can pass data as object parameters. This data are trapped within the Init() and ReverseInit() of ZeroBaseModel, in which we can cast to a type we want to use.
This is introduced in version 1.1.0 and is a custom implementation of ICommand with many useful features. the goal is to keep the ViewModel as clean as possible by automatically tracking dependencies and composing a flow related to the execution of the command. You can find usage examples in "CommandPageViewModel" in this repo.
You can create instances of ICommand using the ZeroComandBuilder and you can customize the flow of the activity in a descriptive way.
When you create a zerocommand you have to specify an INotifyPropertyChanged instance (usually the viewmodel) so that the CanExecute expression is evaluated by finding properties that exist on the viewmodel in order to re-evaluate the canexecute automatically. Example:
this.TestSuccessCommand = ZeroCommand.On(this)
.WithCanExecute(()=> !this.IsBusy && !string.IsNullOrEmpty(this.Name))
.WithExecute((commandParam, context) => this.InnerShowMessageAction())
.Build();
So CanExecute on this ICommand is automatically evaluated when IsBusy or SomeProperty changed (all tracked dependencies must be implement propertychanged)
You can add a dependency on a observablecollection using ".WithRaiseCanExecuteOnCollectionChanged", when the collection changed the canexecute expressio will be evaluated again.
Error Handler
Is possible to intercept exceptions in order to keep the executor method as clean as possible:
this.TestErrorCommand = ZeroCommand.On(this)
.WithCanExecute(this.InnerExpression())
.WithExecute((commandParam, context) => this.InnerManageErrorWithoutSwallow())
.WithErrorHandler(exception => base.DisplayAlert("Managed Exception", exception.Message, "ok"))
.Build();
Before and After Execute
Is possibile to run some logic before execute and after execute. If before execute return false it will stop the execution.
this.BeforeRunEvaluationCommadn = ZeroCommand.On(this)
.WithCanExecute(this.InnerExpression())
.WithExecute((commandParam, context) => this.InnerEvaluateCanRun())
.WithBeforeExecute(context => base.DisplayAlert("Before Run Question", "Can i run?", "yes", "no"))
.WithAfterExecute(context =>
base.DisplayAlert("I'm running after a execution", "I'll not run if evaluation fail!", "ok"))
.Build();
You can pass data between flow step (before, execute, after) using context:
this.ContextEvaluationCommand = ZeroCommand.On(this)
.WithCanExecute(this.InnerExpression())
.WithExecute((commandParam, context) => this.InnerShowMessageAction())
.WithBeforeExecute(context =>
{
var stopWatch = new Stopwatch();
context.Add(stopWatch);
stopWatch.Start();
return true;
})
.WithAfterExecute(async context =>
{
var stopWatch = context.Get<Stopwatch>();
stopWatch.Stop();
await this.DisplayAlert("Evaluation", $"Executed in {stopWatch.ElapsedMilliseconds} ms", "OK");
stopWatch.Reset();
})
.Build();
Auto Invalidate Command
ZeroCommand can autoinvalidate itself during execution so it auto prevent double-tap button.
this.AutoInvalidateCommand = ZeroCommand.On(this)
.WithAutoInvalidateWhenExecuting()
.WithExecute(async (o, context) =>
{
await Task.Delay(1000);
await base.DisplayAlert("Auto invalidate", "Now button should be disabled!", "ok");
}).Build();
Concurrent Execution
You can set how many concurrent execution could support:
.WithConcurrentExecutionOf(3)
where 3 is the number of concurrent execution (default value is 1)
Xamarin Zero offers many useful signature awesomely designed to satisfy our MVVM desire!
PreviousModel and CurrentPage help us organizing data
ZeroBaseModel PreviousModel { get; set; }
Page CurrentPage { get; set; }
PrepareModel() and ReversePrepareModel() are called after a page push and pop from the navigation stack. Complete control is achieved by Appearing and Disappearing event handlers support.
virtual void CurrentPageOnDisappearing(object sender, EventArgs e){}
virtual void CurrentPageOnAppearing(object sender, EventArgs e){}
virtual Task PrepareModel(object data){}
virtual Task ReversePrepareModel(object data){}
A full display managing is guaranteed.
Task<bool> DisplayAlert(string title, string message, string accept, string cancel){}
Task DisplayAlert(string title, string message, string cancel){}
Task<string> DisplayActionSheet(string title, string cancel, string destruction, string[] buttons){}
Task<string> DisplayPrompt(string title, string message, string accept = "OK", string cancel = "Cancel", string placeholder = null, int maxLength = -1, Keyboard keyboard = null, string initialValue = "")
ZeroApp helps us using builder pattern for bootstrapping our application.
ZeroApp On(Application app){}
ZeroApp WithContainer(IContainer container){}
ZeroApp RegisterShell<T>(Func<T> shell) where T : Shell{}
void Start(){}
void StartWith<T>() where T : Shell{}
ZeroApp WithTransientViewModels()
ZeroApp WithTransientPages()
Default lifecycle for pages and viewmodels is singleton. You can register all as transient using WithTransientViewModels/Pages, or you can use [Transient] attribute.
it is responsible of switching navigation containers. Default use case is change from a login status to a logout one.
void SwitchContainer<T>() where T : Shell{}
Xamarin.Zero does not force you to use a specific IoC container, however it offers you an implementation through Xam.Zero.DryIoC nuget. DryIoC is a fast and lean IoC container for .NET. Otherwise you can always use one of your favourite implementations.
During bootstrapping, on application start up, Xamarin.Forms IMessagingCenter Interface will be registered, so that we can inject it in view models constructor implementing Dependency Injection. This does not preclude the use of the standard Xamarin.Forms singleton mechanism.
Xam.Zero gracefully implements this pattern, by whom you can manage more efficiently navigation events. In the case you can subscribe to CurrentPageOnAppeanring and CurrentPageOnDisappearing events and even extend them by adding more login in your ViewModel, without worrying about unsubscribe to them. The WeakEventHandler class is a public sealed one in Xam.Zero, so you can use it for managing every kind of event.
- Simple/Tabbed navigation app by switching navigation containers;
- Custom IoC containers;