Skip to content

A collection of plugins and base classes that I find useful, maybe you will too

License

Notifications You must be signed in to change notification settings

TrueGeek/Xamarin.Plugin.TrueGeek.XFHelpers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Xamarin Forms Helpers

XFHelpers is, at it's core, a base View and ViewModel providing super simple ViewModel based navigation. MVVM using as much built-in-Xamarin as possible without the need for heavy frameworks.

The only requirements are Xamarin.Forms (4.3) and Xamarin.Essentials (1.3.1). The entire package is only 25kb.

Several common converters and helpers are included. Feature requests and PRs are welcome.

A sample project is available in the /Sample directory.

A change log is available under Releases.

XFHelpers NuGet
XFHelpers.iOS NuGet
XFHelpers.Droid NuGet

To Install

  1. Install the XFHelpers NuGet package into your Forms project

  2. If you do not plan on using the LongPressEffect nothing further is needed. You only need to install the platform specific NuGet packages if you are going to use LongPressEffect

  3. Android

  4. iOS

    • Install the XFHelpers.Droid NuGet package into your Droid project

    • In AppDelegate call TrueGeek.XFHelpers.iOS.Effects.iOSLongPressedEffect.Init(); before you call return base.FinishedLaunching(app, options);


Features


Base View

All views must inherit from TGBasePage.

In the XAML this looks like this:

<baseView:TGBasePage
    xmlns:baseView="clr-namespace:TrueGeek.XFHelpers.Views;assembly=TrueGeek.XFHelpers"
    x:Class="TGSample.Views.HomePage"

and in the code behind simply this

public partial class HomePage : TGBasePage

SafeAreaInsets

SafeAreaInsets is a readonly Thickness. On Android this will always return the default new Thickness as it's value isn't needed. On iOS it returns what the device feels the SafeAreaIset is, either 20 or 40px depending on the phone type and wether it has a notch or not.

Use this instead of ios:Page.UseSafeArea="true" in your XAML pages to avoid white space on phones without notches. Instead, add padding at the top of your page of the amount returned by this property.

ClearBackButtonTextOnAllPages

If you don't like the text before the back button in your navigation pages, then set this in your App.xaml.cs App() function:

TrueGeek.XFHelpers.Init.ClearBackButtonTextOnAllPages = true;

IsBusyOverlay

When IsBusy is set on the ViewModel (see below) a busy overlay will be shown. This can be overriden like this, in your App.xaml.cs App() function:

TrueGeek.XFHelpers.Init.CustomActivityIndicator = new YourCustomComponent();

If you don't override the component the default is used, which is simply a new ActivityIndicator().

Example in sample project.


Base ViewModel

View models must inherit from TGBaseViewModel like this

public class HomeViewModel : TGBaseViewModel

IsBusy

If IsBusy is set to True the busy overlay will be shown on the page.

Title

This is provided so that it can be bound by the page.

DeviceIsTabletOrDesktop

This returns true if Xamarin.Essentials returns that either the Device.Idiot is Tablet or Desktop.

DisplayActionSheet

DisplayActionSheet(string title, string cancelButtonText, params string[] buttons);

Shows an action sheet using a reference to Application.Current.MainPage.

Note that if null is passed as cancelButtonText then Android will display the ActionSheet as a model without the ability to cancel it, even if the user taps on the background.

DisplayAlert

DisplayAlert(string title, string message, string accept)
DisplayAlert(string title, string message, string accept, string cancel)

Shows an alert box, with or without a cancel button, using a reference to Application.Current.MainPage.

DisplayPrompt

DisplayPrompt(string title, string message, string accept, string cancel, string placeholder = null, int maxLength = -1, Keyboard keyboard = null)

Shows a prompt using a reference to Application.Current.MainPage.

INotifyPropertyChanged

BaseViewModel inherits from INotifyPropertyChanged. If you add properties to your view model you can do so like this

private bool myProperty;
public bool MyProperty
{
    get => myProperty;
    set => SetProperty(ref myProperty, value);
}

You can also call OnPropertyChanged() directly

private bool myProperty;
public bool MyProperty
{
    get => myProperty;
    set
    {
        
        myProperty = value;

        // if you do not pass any value it assumed the enclosing property (MyProperty in this case)
        OnPropertyChanged();

        // the easiest way to notify another property is with a => operator
        OnPropertyChanged(() => my2ndProperty);

        // you can also use the nameof() function
        OnPropertyChanged(nameof(my3rdProperty));

        // this is a bad practice! don't use strings!
        OnPropertyChanged("my4thPropirty");

    }
}

ViewModel Navigation

Initialization

After setting the MainPage in app.xaml.cs, call Init.NavigationService. This function can be added to app.xaml.cs to do this all in one step:

public void SetMainPage<T>() where T : Page
{            
    MainPage = new NavigationPage(Activator.CreateInstance<T>());   // instead of a NavigationPage you could also use an AppShell
    TrueGeek.XFHelpers.Init.NavigationService = new TrueGeek.XFHelpers.Services.NavigationService(MainPage.Navigation);
}

Then you just call SetMainPage<HomePage>();

This initial page will need to have it's ViewModel specifically set in it's XAML, like this:

<baseView:TGBasePage
    xmlns:vm="clr-namespace:TGSample.ViewModels"
    ...

    <ContentPage.BindingContext>
        <vm:HomeViewModel />
    </ContentPage.BindingContext>

Assumed Naming Conventions

It is assumed that:

  • There is a one-to-one relationship between a ViewModel and a View.
  • The view is named in the format Views\SamplePage
  • The view model is named in the format ViewModels\SampleViewModel

It is possible to override the assumption of the view name (but not the folder) by using NavigateTo and passing in the Page type:

NavigateTo<TViewModel, TPage>(object parameters)

Navigation Methods

NavigateTo<TViewModel>(object parameters = null)

Navigates to a ViewModel, optionally sending parameters

NavigateTo<TViewModel, TPage>(object parameters = null)

Navigates to a ViewModel overriding the asumption of naming convention for the page name

NavigateToModal<TViewModel>(object parameters = null, bool useNavigationPage = false, Style navigationPageStyle = null)

Navigates to a ViewModel as a modal

NavigateBack()

Navigates back

NavigateBackFromModal()

Navigates back from a modal

NavigateToRoot()

Navigates back to the root page

GetPageForMasterDetail<TViewModel>(object parameters = null, bool useNavigationPage = true, Style navigationPageStyle = null)

Gets a page that can be used in a master detail page. For example:

    var masterPage = NavigationService.GetPageForMasterDetail<HomeViewModel>(null, false);
    var detailPage = NavigationService.GetPageForMasterDetail<DetailView>(null, false);

    var masterDetailPage = new MasterDetailPage()
    {
        Master = masterPage,
        Detail = detailPage,
        Style = (Style)Application.Current.Resources["OurCustomStyle"],
    };

    MainPage = masterDetailPage;

Navigation Events

Navigation events fire in this order:

  1. ViewAppearingAsync
  2. ViewModelInit
  3. ViewDisappearing

ViewModelInit will include a parameter object that is either null or the value included in navigation (see methods above for how to send parameters).

ReturnParameters

Parameters can be sent backwards from one viewmodel to the previous one using ReturnParameters. See the sample ReturnParametersViewModel where it sets the return parameter:

private async Task SelectValueCommandExecutor(string parameter)
{
    ReturnParameters = parameter;
    await NavigationService.NavigateBack();
}

This is read in HomeViewModel this way:


private async Task GoToReturnParametersPageExecutor()
{
    NavigationService.OnPageDisappearing += NavigationService_OnPageDisappearing;
    await NavigationService.NavigateTo<ReturnParametersViewModel>();
}

private void NavigationService_OnPageDisappearing(object sender, PageDisappearingEventArgs e)
{
    if (e.ViewModelType == typeof(ReturnParametersViewModel))
    {
        var selectedValue = (string)e.ReturnParameters;
        NavigationService.OnPageDisappearing -= NavigationService_OnPageDisappearing;
    }
}


i18n Translation

Xamarin already provides i18n string translations. Add a Resources folder and AppResources.{LanguageCode}.resx files for each language you wish to support.

Let XFHelpers know how to access your resource files like this:

TrueGeek.XFHelpers.Init.ResourceManager = new ResourceManager("TGSample.Resources.AppResources", typeof(App).GetTypeInfo().Assembly);

The language shown defaults to the user's language (CultureInfo.CurrentCulture.TwoLetterISOLanguageName). You can override this by setting it manually like this:

TrueGeek.XFHelpers.Init.LanguageCode = "de";

XFHelpers provides a converter to use in XAML:

<baseView:TGBasePage
    ...
    xmlns:converters="clr-namespace:TrueGeek.XFHelpers.Converters;assembly=TrueGeek.XFHelpers">

        <Label Text="{converters:Translate name_of_string_from_resx_file}" />

Additionally, you can get string translations with the GetText() function:

TranslatedText = new TrueGeek.XFHelpers.Helpers.TranslationHelper().GetText("name_of_string_from_resx_file");


Simple PubSub Service

Super simple pub / sub service.

Create a static TrueGeek.XFHelpers.PubSub.Hub

Multiple subscribers can Subscribe()

When anyone calls Publish() all the subscribers are notified


Behaviors

Event To Command


Converters

Inverse Bool

Standard inverse bool converter - returns !true

String Bool

Returns false if string is null or empty


Extensions

Track Component Lifecycle

Extension that adds lifecycle events to custom components. Use like this:

public MyCustomGridComponent()
{
    InitializeComponent();
   this.TrackComponentLifecycle(OnAppearing, OnDisappearing);
}

private void OnDisappearing(object sender, EventArgs eventArgs)
{
}

private void OnAppearing(object sender, EventArgs eventArgs)
{
}

Effects

Long Press

Make sure you've added the NuGet package to your iOS and Android projects!

Use like this:

xmlns:effects="clr-namespace:TrueGeek.XFHelpers.Effects;assembly=TrueGeek.XFHelpers"

    <Button
        Command="{Binding PressCommand}"
        effects:LongPressedEffect.Command="{Binding LongPressCommand}"            
        >

        <Button.Effects>
            <effects:LongPressedEffect />
        </Button.Effects>

    </Button>

Logging

Any exceptions or warnings that occur within the XFHelpers code will be outputted to an action that you can capture if you are interested.

TrueGeek.XFHelpers.Init.LoggingReference => (string message, int logLevel, Dictionary<string, object> parameters)