Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, and Uno / Win UI Applications..
This list of changes was auto generated.
This is the 3rd public preview for Prism 7.2.
This preview provides a number of bug fixes and enhancements.
Currently, the only way to show any type of dialog with Prism is by using the PopupWindowAction
in combination with System.Windows.Interactivity
. To be honest, I really dislike this approach. It's over complex, highly verbose, difficult to implement, and is very limited. The limitations are covered pretty well in Issue #864
Instead, I created a new IDialogService
API that will replace the PopupWindowAction altogether. This service will allow developers to show any dialog they want either modal, or non-modal, and have complete control over their dialog logic.
The implementation looks like this:
public interface IDialogService
{
void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback);
void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback);
}
The idea here is that Prism will no longer provide any built-in dialogs like Notification or Confirmation. Mainly because the Prism implementations are UGLY and will never match the styling of your beautiful WPF application. So, it's important that you are able to register your own dialogs.
Your dialog view is a simple UserContro l that can be designed anyway you please. The only requirement it has a ViewModel that implements IDialogAware
set as it's DataContext. Preferably, it will utilize the ViewModelLocator
<UserControl x:Class="HelloWorld.Dialogs.NotificationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Width="300" Height="150">
<Grid x:Name="LayoutRoot" Margin="5">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Message}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" TextWrapping="Wrap" />
<Button Command="{Binding CloseDialogCommand}" CommandPrameter="true" Content="OK" Width="75" Height="25" HorizontalAlignment="Right" Margin="0,10,0,0" Grid.Row="1" IsDefault="True" />
</Grid>
</UserControl>
Next you need a ViewModel that implements IDialogAware
which is defined as follows
public interface IDialogAware
{
bool CanCloseDialog();
void OnDialogClosed();
void OnDialogOpened(IDialogParameters parameters);
string Title { get; set; }
event Action<IDialogResult> RequestClose;
}
Here is a simple example of what an IDialogAware
ViewModel may look like.
public class NotificationDialogViewModel : BindableBase, IDialogAware
{
private DelegateCommand<string> _closeDialogCommand;
public DelegateCommand<string> CloseDialogCommand =>
_closeDialogCommand ?? (_closeDialogCommand = new DelegateCommand<string>(CloseDialog));
private string _message;
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
private string _title = "Notification";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public event Action<IDialogResult> RequestClose;
protected virtual void CloseDialog(string parameter)
{
bool? result = null;
if (parameter?.ToLower() == "true")
result = true;
else if (parameter?.ToLower() == "false")
result = false;
RaiseRequestClose(new DialogResult(result));
}
public virtual void RaiseRequestClose(IDialogResult dialogResult)
{
RequestClose?.Invoke(dialogResult);
}
public virtual bool CanCloseDialog()
{
return true;
}
public virtual void OnDialogClosed()
{
}
public virtual void OnDialogOpened(IDialogParameters parameters)
{
Message = parameters.GetValue<string>("message");
}
}
To register a dialog, you must have a View (UserControl) and a corresponding ViewModel (which must implement IDialogAware
). In the RegisterTypes
method, simply register your dialog like you would any other service by using the IContainterRegistery.RegisterDialog
method.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterDialog<NotificationDialog, NotificationDialogViewModel>();
}
To use the dialog service you simply ask for the service in your VM ctor.
public MainWindowViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
}
Then call either Show
or ShowDialog
providing the name of the dialog, any parameters your dialogs requires, and then handle the result via a call back
private void ShowDialog()
{
var message = "This is a message that should be shown in the dialog.";
//using the dialog service as-is
_dialogService.ShowDialog("NotificationDialog", new DialogParameters($"message={message}"), r =>
{
if (!r.Result.HasValue)
Title = "Result is null";
else if (r.Result == true)
Title = "Result is True";
else if (r.Result == false)
Title = "Result is False";
else
Title = "What the hell did you do?";
});
}
The intent of the dialog API is not to try and guess exactly what type of parameters your need for all of your dialogs, but rather to just create and show the dialogs. To simplify common dialogs in your application the guidance will be to create an extension methods to simplify your applications dialogs.
For example:
public static class DialogServiceEstensions
{
public static void ShowNotification(this IDialogService dialogService, string message, Action<IDialogResult> callBack)
{
dialogService.ShowDialog("NotificationDialog", new DialogParameters($"message={message}"), callBack);
}
}
Then to call your Notifications use the new and improved API that you created specifically for your app.
_dialogService.ShowNotification(message, r =>
{
if (!r.Result.HasValue)
Title = "Result is null";
else if (r.Result == true)
Title = "Result is True";
else if (r.Result == false)
Title = "Result is False";
else
Title = "What the hell did you do?";
});
It's very common to be using a third-party control vendor such as Infragistics. In these cases, you may want to replace the standard WPF Window control that hosts the dialogs with a custom Window class such as the Infragistics XamRibbonWindow control.
In this case, just create your custom Window, and implement the IDialogWindow
interface:
public partial class MyRibbonWindow: XamRibbonWindow, IDialogWindow
{
public IDialogResult Result { get; set; }
….
}
Then register your dialog window with the IContainerRegistry
.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterDialogWindow<MyRibbonWindow>();
}
You can control the properties of the DialogWindow by using a style via an attatched property on the Dialog UserControl
<prism:Dialog.WindowStyle>
<Style TargetType="Window">
<Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" />
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="ShowInTaskbar" Value="False"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:Dialog.WindowStyle>
To clarify, this is to replace the PopupWindowAction. I want to remove that mess completely from Prism
After numerous user survey's and interviews it became apparent that the intent of INavigatingAware had been become unclear and that users were actually overwhelmingly asking for a breaking change. INavigatingAware is no longer supported. For those who may be using OnNavigatingTo with INavigationAware this will be most impactful as a behavior change as INavigatingAware has been removed from INavigationAware meaning that it will no longer be called. For those who have implemented INavigatingAware directly you will see a build error. The impact should be minimal by simply renaming all instances of INavigatingAware to Initialize.
IAutoInitialize is designed for those cases where you are passing objects around. By default we do a Non-Case-Sensitive lookup between the Navigation Parameters and the Properties in your ViewModel. If one is found we will automatically try to set the property for you.
_navigationService.NavigateAsync("ViewB" ("title", "Hello World"), ("fooBar", "some other value"));
public class ViewBViewModel : IAutoInitialize
{
public string Title { get; set; }
public string FooBar { get; set; }
}
In the above example your ViewModel will be initialized with the values passed in the NavigationParameters. If these are static variables (meaning the won't change during the lifecycle of your View/ViewModel you would not need to implement INotifyPropertyChanged for them.
public class ViewBViewModel
{
[NavigationParameter(true)
public string Title { get; set; }
[NavigationParameter("fooBar")
public string Foo { get; set; }
}
In this example you'll notice that we have some slight variations where we have added NavigationParameter attributes. The attributes allow you to decorate your properties to make them Required or specify a different name that you will use in your NavigationParameters. Note that if you make a property Required and you fail to pass the parameter this will throw an Exception that you will need to check for in the NavigationResult.
Automatic Registration can be used for either an Application or Module. Note that there is an optional Automatic property. When Automatic is set to true Prism will not look for any attributes but will simply look for any contained types that are a Page type. If there is a Page type it will automatically be registered.
[AutoRegisterForNavigation]
public class App : PrismApplication
{
}
[AutoRegisterForNavigation(Automatic = true)]
public class AwesomeModule : Imodule
{
}
In the above example when the App is initializing Prism will reflect on the assembly of the App to look for any RegisterForNavigation Attributes first at an assembly level and then at a page level.
[assembly: RegisterForNavigation(ViewType = typeof(ViewA))]
[assembly: RegisterForNavigation(ViewType = typeof(ViewB), Name = "viewB")]
[assembly: RegisterForNavigation(ViewType = typeof(ViewC), Name = "platformSpecific", RuntimePlatform = RuntimePlatform.Android)]
[assembly: RegisterForNavigation(ViewType = typeof(ViewD), Name = "platformSpecific", RuntimePlatform = RuntimePlatform.iOS)]
// OR
// Registered as ViewA
[RegisterForNavigation]
public class ViewA : ContentPage { }
[RegisterForNavigation(Name = "viewB")]
public class ViewB : ContentPage { }
// Ignored if no attribute exists unless Automatic Registration was enabled
public class ViewB : ContentPage { }
// Only Registered on iOS
[RegisterForNavigation(RuntimePlatform = RuntimePlatform.iOS)]
public class ViewD : ContentPage { }
This is the 2nd public preview for Prism 7.2, and the 1st public preview of the new Prism for UWP (aka Prism.Windows). Note that the UWP API's may still be in flux prior to release.
This preview provides a number of bug fixes and enhancements.
#Prism Core
This is a special preview for Microsoft Connect.
<ContentPage xmlns:prism="http://prismlibrary.com">
<ListView>
<ListView.Behaviors>
<prism:EventToCommandBehavior ... />
</ListView.Behaviors>
</ListView>
</ContentPage>
In addition to the typical bug fixes Prism 7.1 brings a much more consolidated approach to developing applications. Perhaps this is most clear with Prism.Modularity now being defined entirely in the Prism.Core providing a consistent API for Prism.Forms and Prism.WPF, as well as making it available for the first time to Prism.Windows (in an upcoming preview).
Due to the major changes in where certain types are for Modularity (and Navigation for Prism.Forms), you may experience binary incompatibility issues when using libraries that target older versions of Prism.
Going forward we will only consider support for containers that work properly for Prism. The Prism team advises all developers to update projects to use the new PackageReference
, this will help simplify maintaining which packages you need to update by simply targeting the Prism Container package (see Unity notice).
After much discussion the Prism team has decided that this will be the final release for Autofac. Modularity is a core capability for Prism, which requires the use of a Mutable container. The fact that the Autofac team has chosen to make Autofac Immutable prevents us from being able to support the use of containers.
Similarly the Prism team has decided that we will no longer be shipping or supporting MEF for WPF, nor will support be added for any other platforms.
Unity continues to be a very popular and fully supported container. For those upgrading to Prism 7.1 from Prism 6.X, note that you will need to uninstall the Unity references in your existing projects as the new maintainer for Unity has completely rearchitected the Unity Container. This included changing namespaces and changing how the library was shipped forcing the Prism Team to change the NuGet that we target from Unity
to Unity.Container
.
NavigationParameters
to use the new INavigationParameters
BREAKING
Task INavigationService.NavigateAsync
to Task<INavigationResult> INavigationService.NavigateAsync
Task<bool> INavigationService.GoBackAsync
to Task<INavigationResult> INavigationService.GoBackAsync
BREAKING
INavigationService
to be named navigationService
inside of a ViewModel.System.Type
to System.String
and is equal to Type.AssemblyQualifiedName
The concept of a Partial View is to support a custom layout which may be reused across multiple pages, and eliminate ViewModel logic duplication by allowing that custom layout to rely on its own ViewModel. To use a Partial View you must set the ViewModelLocator.AutowirePartialView
property with a reference to the containing page as shown here. You should not set the ViewModelLocator.AutowireViewModel
property on the Partial View unless you are explicitly opting out as setting this property to true
directly may result in the ViewModel being incorrectly set.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AwesomeApp.Views"
xmlns:prism="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
x:Name="self"
x:Class="AwesomeApp.Views.ViewA">
<StackLayout>
<local:AwesomeView mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}" />
<Entry Text="{Binding SomeValue" />
</StackLayout>
</ContentPage>
Xamarin.Forms 3.1 introduces a new DependencyResolver. This enables applications using Dependency Injection to use their Container to resolve Platform Renderers and Effects which makes it possible to inject any services such as a logger. Since the default constructor was deprecated in Android Renderers as of Xamarin.Forms 2.5, Prism now includes a specific Android binary that enables the DependencyResolver to resolve types using
The Container Provider now allows types like ValueConverters to include Dependency Injection of Types/Services in the ctor, and allows the converter to be declared in XAML
namespace Contoso.Converters
{
public class MyValueConverter : IValueConverter
{
private ILoggerFacade _logger { get; }
public MyValueConverter(ILoggerFacade logger)
{
_logger = logger;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting value", Category.Debug, Priority.None);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting Value Back...", Category.Debug, Priority.None);
return value;
}
}
}
This can then be used in XAML using the ContainerProvider as follows:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ioc="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:converters="using:Contoso.Converters"
x:Class="Contoso.Views.ViewA">
<ContentPage.Resources>
<ResourceDictionary>
<ioc:ContainerProvider x:TypeArguments="converters:MyValueConverter" x:Key="myValueConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Entry Text="{Binding Demo,Converter={StaticResource myValueConverter}}" />
</ContentPage>
**Implemented relative back behavior ("../../") **
Previously, when using the relative back feature ("../" ) you would be required to provide a page that would be pushed onto the navigation stack. That is no longer required. You may now chain together any number of relative go-back instructions to "go back".
For example:
Given your navigation stack looked like this: NavigationPage/UserList/UserDetails/LoginPage/EditUser
You can now indicate how many pages to remove, or "go back" by chaining the relative go-back instruction "../".
NavigationService.NavigateAsync("../../../");
This would result in going back three pages leaving you with the following navigation stack:
NavigationPage/UserList
NOTE - The pages in the stack prior to the current page (last page in the nav stack) will be removed without calling any INavAware methods. Only the current page (EditUser in this example) would perform a proper GoBackAsync and execute the INavAware methods.
NOTE - This feature is only supported while within the context of a NavigationPage.
You can now use XAML Navigation Extensions to Navigate. This works well for scenarios where you may have a partial "MenuView" that may be reused across multiple Pages or where there may be no real reason to implement Navigation inside of a ViewModel.
<!-- basic nav -->
<Button Text="Go To About" Command"{prism:NavigateTo 'About'}" />
<!-- basic nav without animation-->
<Button Text="Go To About" Command"{prism:NavigateTo 'About', Animated=False}" />
<!-- basic modal nav -->
<Button Text="Go To About" Command"{prism:NavigateTo 'About', UseModalNavigation=True}" />
<!-- custom can navigate support -->
<Button Text="Go To About" Command="{prism:NavigateTo 'About'}" prism:Navigation.CanNavigate="{Binding CanNavigate}" />
<!-- go to new page, but remove one along the way -->
<Button Text="Go To Settings" Command"{prism:NavigateTo '../Settings'}" />
<!-- nav with VM Parameters -->
<Button Text="Go To About"
Command"{prism:NavigateTo 'About'}"
CommandParameters="{Binding MyNavParams}" />
<!-- Go Back -->
<Button Text="Go Back" Command="{prism:GoBack}" />
<!-- Go Back To Root -->
<Button Text="Go Back To Root" Command="{prism:GoBack ToRoot}" />
<!-- Xaml defined parameters -->
<Button Text="Go To About" Command="{prism:NavigateTo 'About'}" >
<Button.CommandParameter>
<prism:XamlNavigationParameters Parent="{x:Reference this}">
<prism:XamlNavigationParameter Key="MainPageViewModel" Value="{Binding .}" />
</prism:XamlNavigationParameters>
</Button.CommandParameter>
</Button>
<!-- can navigate on a parent object-->
<ContentView>
<ContentView prism:Navigation.CanNavigate="False">
<ContentView>
<Button Text="Cannot Navigate" Command="{prism:GoBack}" />
</ContentView>
</ContentView>
</ContentView>
The Bootstrapper has been marked obsolete and it is recommended to use the PrimsApplication class going forward. This results in less code to get started, and an easier and more intuitive API.
Note: The Bootstrapper class is not being removed, it is only marked obsolete to drive awareness of the new PrismApplication class.
Added new Prism.Ioc namespace to handle DI container abstractions. Interacting with the container is now done via the IContainerProvider
and the IContainerRegistry
interfaces. The IContainerProvider
interface is used to resolve services from the container. The IContainerRegistry
is used to register types with the container. Access to the actual DI container can be achieved by using the GetContainer()
extension method off of the IContainerRegistry
and IContainerProvider
interfaces.
This release introduces the final API alignment for Prism.Modularity. This undoes some breaking changes made in Preview 3 to make the upgrade path easier for WPF apps that declare ModuleCatalogs in XAML. This also introduces support for Partial Views for Xamarin Forms.
NavigationParameters
to use the new INavigationParameters
BREAKING
Task INavigationService.NavigateAsync
to Task<INavigationResult> INavigationService.NavigateAsync
Task<bool> INavigationService.GoBackAsync
to Task<INavigationResult> INavigationService.GoBackAsync
BREAKING
INavigationService
to be named navigationService
inside of a ViewModel.System.Type
to System.String
and is equal to Type.AssemblyQualifiedName
The concept of a Partial View is to support a custom layout which may be reused across multiple pages, and eliminate ViewModel logic duplication by allowing that custom layout to rely on its own ViewModel. To use a Partial View you must set the ViewModelLocator.AutowirePartialView
property with a reference to the containing page as shown here. You should not set the ViewModelLocator.AutowireViewModel
property on the Partial View unless you are explicitly opting out as setting this property to true
directly may result in the ViewModel being incorrectly set.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AwesomeApp.Views"
xmlns:prism="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
x:Name="self"
x:Class="AwesomeApp.Views.ViewA">
<StackLayout>
<local:AwesomeView mvvm:ViewModelLocator.AutowirePartialView="{x:Reference self}" />
<Entry Text="{Binding SomeValue" />
</StackLayout>
</ContentPage>
Xamarin.Forms 3.1 introduces a new DependencyResolver. This enables applications using Dependency Injection to use their Container to resolve Platform Renderers and Effects which makes it possible to inject any services such as a logger. Since the default constructor was deprecated in Android Renderers as of Xamarin.Forms 2.5, Prism now includes a specific Android binary that enables the DependencyResolver to resolve types using
The Container Provider now allows types like ValueConverters to include Dependency Injection of Types/Services in the ctor, and allows the converter to be declared in XAML
namespace Contoso.Converters
{
public class MyValueConverter : IValueConverter
{
private ILoggerFacade _logger { get; }
public MyValueConverter(ILoggerFacade logger)
{
_logger = logger;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting value", Category.Debug, Priority.None);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting Value Back...", Category.Debug, Priority.None);
return value;
}
}
}
This can then be used in XAML using the ContainerProvider as follows:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ioc="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:converters="using:Contoso.Converters"
x:Class="Contoso.Views.ViewA">
<ContentPage.Resources>
<ResourceDictionary>
<ioc:ContainerProvider x:TypeArguments="converters:MyValueConverter" x:Key="myValueConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Entry Text="{Binding Demo,Converter={StaticResource myValueConverter}}" />
</ContentPage>
**Implemented relative back behavior ("../../") **
Previously, when using the relative back feature ("../" ) you would be required to provide a page that would be pushed onto the navigation stack. That is no longer required. You may now chain together any number of relative go-back instructions to "go back".
For example:
Given your navigation stack looked like this: NavigationPage/UserList/UserDetails/LoginPage/EditUser
You can now indicate how many pages to remove, or "go back" by chaining the relative go-back instruction "../".
NavigationService.NavigateAsync("../../../");
This would result in going back three pages leaving you with the following navigation stack:
NavigationPage/UserList
NOTE - The pages in the stack prior to the current page (last page in the nav stack) will be removed without calling any INavAware methods. Only the current page (EditUser in this example) would perform a proper GoBackAsync and execute the INavAware methods.
NOTE - This feature is only supported while within the context of a NavigationPage.
You can now use XAML Navigation Extensions to Navigate. This works well for scenarios where you may have a partial "MenuView" that may be reused across multiple Pages or where there may be no real reason to implement Navigation inside of a ViewModel.
<!-- basic nav -->
<Button Text="Go To About" Command"{prism:NavigateTo 'About'}" />
<!-- basic nav without animation-->
<Button Text="Go To About" Command"{prism:NavigateTo 'About', Animated=False}" />
<!-- basic modal nav -->
<Button Text="Go To About" Command"{prism:NavigateTo 'About', UseModalNavigation=True}" />
<!-- custom can navigate support -->
<Button Text="Go To About" Command="{prism:NavigateTo 'About'}" prism:Navigation.CanNavigate="{Binding CanNavigate}" />
<!-- go to new page, but remove one along the way -->
<Button Text="Go To Settings" Command"{prism:NavigateTo '../Settings'}" />
<!-- nav with VM Parameters -->
<Button Text="Go To About"
Command"{prism:NavigateTo 'About'}"
CommandParameters="{Binding MyNavParams}" />
<!-- Go Back -->
<Button Text="Go Back" Command="{prism:GoBack}" />
<!-- Go Back To Root -->
<Button Text="Go Back To Root" Command="{prism:GoBack ToRoot}" />
<!-- Xaml defined parameters -->
<Button Text="Go To About" Command="{prism:NavigateTo 'About'}" >
<Button.CommandParameter>
<prism:XamlNavigationParameters Parent="{x:Reference this}">
<prism:XamlNavigationParameter Key="MainPageViewModel" Value="{Binding .}" />
</prism:XamlNavigationParameters>
</Button.CommandParameter>
</Button>
<!-- can navigate on a parent object-->
<ContentView>
<ContentView prism:Navigation.CanNavigate="False">
<ContentView>
<Button Text="Cannot Navigate" Command="{prism:GoBack}" />
</ContentView>
</ContentView>
</ContentView>
The Bootstrapper has been marked obsolete and it is recommended to use the PrimsApplication class going forward. This results in less code to get started, and an easier and more intuitive API.
Note: The Bootstrapper class is not being removed, it is only marked obsolete to drive awareness of the new PrismApplication class.
Added new Prism.Ioc namespace to handle DI container abstractions. Interacting with the container is now done via the IContainerProvider
and the IContainerRegistry
interfaces. The IContainerProvider
interface is used to resolve services from the container. The IContainerRegistry
is used to register types with the container. Access to the actual DI container can be achieved by using the GetContainer()
extension method off of the IContainerRegistry
and IContainerProvider
interfaces.
Prism now has support for SourceLink. This adds metadata that allow Visual Studio to determine the exact commit that was built and pull the source from GitHub from that commit, thus allowing you to step directly into Prism's code while debugging.
Trust is important, and we believe you should be able to trust that the NuGet Packages we ship are authentic and have not been tampered with. As such all Prism NuGet packages are now signed by our EV Certificate providing you the ability to see very clearly that the package is authentically from the Prism team.
NavigationParameters
to use the new INavigationParameters
BREAKING
Task INavigationService.NavigateAsync
to Task<INavigationResult> INavigationService.NavigateAsync
Task<bool> INavigationService.GoBackAsync
to Task<INavigationResult> INavigationService.GoBackAsync
BREAKING
INavigationService
to be named navigationService
inside of a ViewModel.System.Type
to System.String
and is equal to Type.AssemblyQualifiedName
Xamarin.Forms 3.1 introduces a new DependencyResolver. This enables applications using Dependency Injection to use their Container to resolve Platform Renderers and Effects which makes it possible to inject any services such as a logger. Since the default constructor was deprecated in Android Renderers as of Xamarin.Forms 2.5, Prism now includes a specific Android binary that enables the DependencyResolver to resolve types using
The Container Provider now allows types like ValueConverters to include Dependency Injection of Types/Services in the ctor, and allows the converter to be declared in XAML
namespace Contoso.Converters
{
public class MyValueConverter : IValueConverter
{
private ILoggerFacade _logger { get; }
public MyValueConverter(ILoggerFacade logger)
{
_logger = logger;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting value", Category.Debug, Priority.None);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting Value Back...", Category.Debug, Priority.None);
return value;
}
}
}
This can then be used in XAML using the ContainerProvider as follows:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ioc="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:converters="using:Contoso.Converters"
x:Class="Contoso.Views.ViewA">
<ContentPage.Resources>
<ResourceDictionary>
<ioc:ContainerProvider x:TypeArguments="converters:MyValueConverter" x:Key="myValueConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Entry Text="{Binding Demo,Converter={StaticResource myValueConverter}}" />
</ContentPage>
**Implemented relative back behavior ("../../") **
Previously, when using the relative back feature ("../" ) you would be required to provide a page that would be pushed onto the navigation stack. That is no longer required. You may now chain together any number of relative go-back instructions to "go back".
For example:
Given your navigation stack looked like this: NavigationPage/UserList/UserDetails/LoginPage/EditUser
You can now indicate how many pages to remove, or "go back" by chaining the relative go-back instruction "../".
NavigationService.NavigateAsync("../../../");
This would result in going back three pages leaving you with the following navigation stack:
NavigationPage/UserList
NOTE - The pages in the stack prior to the current page (last page in the nav stack) will be removed without calling any INavAware methods. Only the current page (EditUser in this example) would perform a proper GoBackAsync and execute the INavAware methods.
NOTE - This feature is only supported while within the context of a NavigationPage.
You can now use XAML Navigation Extensions to Navigate. This works well for scenarios where you may have a partial "MenuView" that may be reused across multiple Pages or where there may be no real reason to implement Navigation inside of a ViewModel.
<!-- basic nav -->
<Button Text="Go To About" Command"{prism:NavigateTo 'About'}" />
<!-- basic nav without animation-->
<Button Text="Go To About" Command"{prism:NavigateTo 'About', Animated=False}" />
<!-- basic modal nav -->
<Button Text="Go To About" Command"{prism:NavigateTo 'About', UseModalNavigation=True}" />
<!-- custom can navigate support -->
<Button Text="Go To About" Command="{prism:NavigateTo 'About'}" prism:Navigation.CanNavigate="{Binding CanNavigate}" />
<!-- go to new page, but remove one along the way -->
<Button Text="Go To Settings" Command"{prism:NavigateTo '../Settings'}" />
<!-- nav with VM Parameters -->
<Button Text="Go To About"
Command"{prism:NavigateTo 'About'}"
CommandParameters="{Binding MyNavParams}" />
<!-- Go Back -->
<Button Text="Go Back" Command="{prism:GoBack}" />
<!-- Go Back To Root -->
<Button Text="Go Back To Root" Command="{prism:GoBack ToRoot}" />
<!-- Xaml defined parameters -->
<Button Text="Go To About" Command="{prism:NavigateTo 'About'}" >
<Button.CommandParameter>
<prism:XamlNavigationParameters Parent="{x:Reference this}">
<prism:XamlNavigationParameter Key="MainPageViewModel" Value="{Binding .}" />
</prism:XamlNavigationParameters>
</Button.CommandParameter>
</Button>
<!-- can navigate on a parent object-->
<ContentView>
<ContentView prism:Navigation.CanNavigate="False">
<ContentView>
<Button Text="Cannot Navigate" Command="{prism:GoBack}" />
</ContentView>
</ContentView>
</ContentView>
The Bootstrapper has been marked obsolete and it is recommended to use the PrimsApplication class going forward. This results in less code to get started, and an easier and more intuitive API.
Added new Prism.Ioc namespace to handle DI container abstractions. Interacting with the container is now done via the IContainerProvider
and the IContainerRegistry
interfaces. The IContainerProvider
interface is used to resolve services from the container. The IContainerRegistry
is used to register types with the container. Access to the actual DI container can be achieved by using the GetContainer()
extension method off of the IContainerRegistry
and IContainerProvider
interfaces.
Note that all Prism applications using Unity will need to uninstall Unity due to a change from depending on Unity nuget to Unity.Container. This change reduces the number of Unity assemblies being referenced by 75% and removes an obscure secondary reference to CommonServiceLocator for WPF applications.
Prism 7.1 is NOT compatible with the Xamarin Forms WPF backend.
NavigationParameters
to use the new INavigationParameters
BREAKING
Task INavigationService.NavigateAsync
to Task<INavigationResult> INavigationService.NavigateAsync
Task<bool> INavigationService.GoBackAsync
to Task<INavigationResult> INavigationService.GoBackAsync
BREAKING
The Container Provider now allows types like ValueConverters to include Dependency Injection of Types/Services in the ctor, and allows the converter to be declared in XAML
namespace Contoso.Converters
{
public class MyValueConverter : IValueConverter
{
private ILoggerFacade _logger { get; }
public MyValueConverter(ILoggerFacade logger)
{
_logger = logger;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting value", Category.Debug, Priority.None);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
_logger.Log("Converting Value Back...", Category.Debug, Priority.None);
return value;
}
}
}
This can then be used in XAML using the ContainerProvider as follows:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ioc="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:converters="using:Contoso.Converters"
x:Class="Contoso.Views.ViewA">
<ContentPage.Resources>
<ResourceDictionary>
<ioc:ContainerProvider x:TypeArguments="converters:MyValueConverter" x:Key="myValueConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Entry Text="{Binding Demo,Converter={StaticResource myValueConverter}}" />
</ContentPage>
The Bootstrapper has been marked obsolete and it is recommended to use the PrimsApplication class going forward. This results in less code to get started, and an easier and more intuitive API.
Added new Prism.Ioc namespace to handle DI container abstractions. Interacting with the container is now done via the IContainerProvider
and the IContainerRegistry
interfaces. The IContainerProvider
interface is used to resolve services from the container. The IContainerRegistry
is used to register types with the container. Access to the actual DI container can be achieved by using the GetContainer()
extension method off of the IContainerRegistry
and IContainerProvider
interfaces.
For apps that have not upgraded from 6.X to 7.0, please be sure to read the initial 7.0 release notes for a complete listing of changes.
This will break users who have existing implementations of IModule. Note that Initialize has been completely removed. All Initialization logic should be moved to OnInitialized. Any services that need to be resolved can now be resolved using IContainerProvider in OnInitialized.
IModule.OnInitialized()
=> IModule.OnInitialized(IContainerProvider)
IModule.Initialize
Before upgrading from Prism 6.3, please be sure to read through the release notes as there are several major and breaking changes. This release is only for Prism Core and Prism for Xamarin Forms ONLY.
Ninject has been removed as a supported DI Container for Prism Forms due to compatibility issues with Ninject on iOS and Android.
.NET Standard Support
Added method to clear all errors on ErrorsContainer
Removed requirement for parameter in delegate definition of DelegateCommand.ObservesCanExecute Breaking
Example:
Old (v6.3): ObservesCanExcute((**vm**) => Property)
New (v7.0): ObservesCanExecute(() => Property)
Add capability to DelegateCommand's ObservesProperty to observe properties of complex types
Example:
ObservesProperty(() => Property.NestedProperty.NestedPoperty)
#650: Navigating between Tabs Breaking
#683: .NET Standard support
#765: Deep linking within a NavigationPage results in an incorrect ContentPage Title on Android
#921: Add ability to Remove pages from the navigation stack
#946: Support designating useModalNavigation for specific segments in Uri navigation
#1007: Re-license to MIT, following the Microsoft led projects
#1008: NavigationParameters: Not possible to specify parent type as generic parameter T
#1109: Retrieve current URI from Prism Xamarin.Forms NavigationService
#1111: Include pdb file in nuget package
#1024: XF: NavigationService swallowing Exceptions
#1025: Xamarin.Forms deprecated the Device.OS property in favor of the RuntimePlatform property. The following changes were made to accommodate these changes: IDeviceService
deprecated OnPlatform methods
added DeviceRuntimePlatform property - this mimics the XF API and returns a string
added RuntimePlatform property which returns an Enum of type RuntimePlatform and wraps the XF string-based values
#1027: Support macOS
#1044: When Detail of MasterDetailPage is null, Modal Navigation is possible
#1045: When calling NavigateAsync with NavigationPage, old page's Destroy on the stack is not called
#1046: When calling DeepLink from Navigation Stack, the second page becomes Modal navigation
#1054: When deep linking within NavigationPage, page order is reversed
#1063: Master of MasterDetailPage will not be destroyed
#1074: Optimize Xamarin Forms DryIoc adapter performance
#1085: Error occurs if null path is specified in EventToCommandBehavior
#1089: DebugLogger does not work in nuget-package
#1099: Introduce a Behavior factory into the navigation service
#1132: Prism Forms Modularity extension methods
#1133: IModuleCatalog AddModule returns wrong type
#1139: Modularity Enhancements
#1175: Upgraded to Unity 5 Breaking
#1076: IActiveAware not working when ContentPage (as child of a TabbedPage) is wrapped into a NavigationPage
#1166: Custom NavigationPage is not working properly Prism 7
#1187: Change TabbedPage navigation behavior Breaking
#1198: Rename IApplicationLifecycle to IApplicationLifecycleAware Breaking
#1200: Add GoBackToRoot method
#1232: PageNavigationService should check for MultiPage Parent
#1225: OnNavigatedTo Contains Extra Parameter
#1254: remove support for DependenyService autoresolution Breaking
#1286: Adding modules to Prism in newest PR version
#1325: Removes support for Ninject due to container incompatibility with iOS and Android
added IPageLifecycleAware and PageLifecycleAwareBehavior to enable responding to the Page's Appearing and Disappearing events
Container Extensions
Container.RegisterForNavigationOnPlatform<MainPage, MainPageViewModel>("Main",
new Platform<MainPage_Android>(RuntimePlatform.Android),
new Platform<MainPage_iOS>(RuntimePlatform.iOS));
Added new Prism.Ioc namespace to handle DI container abstractions. Interacting with the container is now done via the IContainerProvider
and the IContainerRegistry
interfaces. The IContainerProvider
interface is used to resolve services from the container. The IContainerRegistry
is used to register types with the container. Access to the actual DI container can be achieved by using the GetContainer()
extension method off of the IContainerRegistry
and IContainerProvider
interfaces.
Notable changes:
ModuleCatalog
propertyConfigureModuleCatalog()
to ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
RegisterTypes()
to RegisterTypes(IContainerRegistry containterRegistry)
CreateContainer()
methodCreateNavigationService()
methodCreateLogger()
methodCreateModuleManager()
methodCreateModuleCatalog()
methodContainer
property is now of type IContainerProviderConfigureContainer
to RegisterRequiredTypes(IContainerRegistry containterRegistry)
RegisterTypeForNavigation
to RegisterForNavigation
Initialize()
method - move all existing code into RegisterTypes
and OnInitialized
methodsRegisterTypes(IContainerRegistry containerRegistry)
method - used to register types with the containerOnInitialized()
method - used to perform any action after the module has been created, and types have been registered with the container.NavigateAsync("TabbedPage/ViewA/ViewB")
will select the tab ViewA in the TabbedPage if it exists, then continue navigating. If ViewA does not exist as a tab, then you will continue to navigate as expected.
NavigateAsync("TabbedPage/NavigationPage/ViewA/ViewB")
would search the TabbedPage for the first instance of a tab that was a NavigationPage, then navigate within the Navigation page the remaining ViewA and ViewB pages. This provided the ability to deep link into a tab's nested navigation page, but there was no way to opt-out of this behavior.
Also, the INavigationAware methods did not get called to all tabs meaning that you would be responsible for forwarding any parameters to all your tabs.
NavigateAsync("TabbedPage/ViewA/ViewB")
will no longer select any tabs, but rather continue processing the navigation as separate pages on the navigation stack.
If you wish to select a tab, you will now use a parameter called "selectedTab" to indicate which tab you would like to select.
NavigateAsync("TabbedPage?selectedTab=MiddleTab/ViewA/ViewB")
This will navigate to the TabbedPage, selected the "MiddleTab" and continue navigating ViewA and ViewB onto the navigation stack.
For tabs that are wrapped inside a NavigationPage, you do not need to change anything. The syntax is the same. This will search each tab to see if it is a navigation page and if the CurrentPage of the NavigationPage matches "MiddleTab", the tab will be selected. This is much more flexible than the existing behavior as before it would take the first instance of the NavigationPage found. Now you can choose which tab exactly.
There is a constant in the KnownNavigationParameters
called SelectedTab
that you can use instead of a string.
NavigateAsync($"TabbedPage?{KnownNavigationParameters.SelectedTab}=MiddleTab")
Some INavigationAware methods are now fired on all Tabs:
Note: the parameter key selectedTab
is now a reserved parameter name. Similar to a reserved keyword in C#. If you use this in your app as a key, you may experience undesired behavior.
Note: At this time there is no native support for deep linking into a Tab that is a navigation page. Please let me know if you need this functionality.
You can now dynamically create tabs when navigating to a TabbedPage by using the KnownNavigationParameters.CreateTab constant or by using the "createTab" parameter name.
_navigationService.NavigateAsync("MyTabbedPage?createTab=ViewA&createTab=ViewB");
This will create a TabbedPage and then create two tabs, ViewA and ViewB.
To create a tab that wraps a page in a NavigationPage, simply denote this as a nested hierarchy using the | character.
_navigationService.NavigateAsync("MyTabbedPage?createTab=NavigationPage|ViewA");
This feature allows you to remove pages form the navigation stack of a NavigationPage while at the same time navigating to a new page. For each page you wish to remove, prefix your navigation path with ../
.
Example Our app requires a user to login in order to edit "User" profile information. This will require the user to be prompted to login and then once successful continue to edit the profile. This leaves us with the following navigation stack:
"NavigationPage/UserList/UserDetails/LoginPage/EditUser"
If we were to hit the back button from the EditUser
page, we would be returned to the LoginPage
. This is not the desired experience. Instead, we want to go back to the UserDetails
which was just updated. To achieve this we provide the following navigation URI from the LoginPage page with a successful login attempt.
NavigationService.NavigateAsync("../EditUser");
This would remove the LoginPage and then immediately navigate to the "EditUser" page which we now have access to.
You can chain the ../
instruction for each page that needs to be removed from the stack.
Given: "NavigationPage/ViewA/ViewB/ViewC/ViewD"
Navigate from ViewD with: NavigationService.NavigateAsync("../../../ViewE");
Results in: "NavigationPage/ViewA/ViewE"
Note: This only works when navigating within a NavigationPage! Modal pages are not supported.
You can now control the various segments of a deep ink by setting the useModalNavigation
parameter on any specific navigation segment in the URI.
NavigatAsync("NavigationPage/ViewA/ViewB/NavigationPage?useModalNavigation=true/ViewC/ViewD");
You can also use the built-in constant KnownNavigationParameters.UseModalNavigation
NavigatAsync($"NavigationPage/ViewA/ViewB/NavigationPage? {KnownNavigationParameters.UseModalNavigation} =true/ViewC/ViewD");
Note: the parameter key useModalNavigation
is now a reserved parameter name. Similar to a reserved keyword in C#. If you use this in your app as a key, you may experience undesired behavior.