Prism MVVM: Load Modules on demand via configuration file - mvvm

I want a Prism MVVM App with a config file, in which I could define which Modules are loaded in predefined regions on demand.
So if I click a button, it should check the config file and and load a module and place it into the certain region.
In the PRISM Modularity Quickstart this would be ModuleE right?
Could somebody make a small example?
EDIT:
I have this so far:
MainWindow.xaml
<Window x:Class="MmuTest2.Views.MainWindow"
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"
Title="{Binding Title}" Height="350" Width="525">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Row="0"
Grid.Column="0"
prism:RegionManager.RegionName="ModuleA" />
<ItemsControl Grid.Row="0"
Grid.Column="1"
prism:RegionManager.RegionName="ModuleB" />
<ItemsControl Grid.Row="1"
Grid.Column="0"
prism:RegionManager.RegionName="ModuleC" />
<Button
Grid.Row="1"
Grid.Column="1"
Command="{Binding LoadCommand}">Load Modules on demand</Button>
</Grid>
</Window>
MainWindowViewModel.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Prism.Commands;
using Prism.Modularity;
using Prism.Mvvm;
namespace MmuTest2.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
this.LoadCommand = new DelegateCommand(
() => {
// Load Modules!
},
() => true
);
}
public ICommand LoadCommand { get; private set; }
}
}
ModuleA.cs
using Prism.Modularity;
using Prism.Regions;
using System;
namespace ModuleA
{
[Module(ModuleName = "ModuleA")]
public class ModuleA : IModule
{
IRegionManager _regionManager;
public ModuleA(RegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
this
._regionManager
.RegisterViewWithRegion("ModuleA", typeof(Views.ModuleAView));
}
}
}
Bootstrapper.cs
using System;
using Microsoft.Practices.Unity;
using Prism.Unity;
using MmuTest2.Views;
using System.Windows;
using Prism.Modularity;
namespace MmuTest2
{
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog catalog = new ConfigurationModuleCatalog();
return catalog;
}
protected override void ConfigureModuleCatalog()
{
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(ModuleA.ModuleA));
}
}
}
EDIT2:
I found a quick and dirty way to load dynamically modules via directory monitoring.
Now I just need a way to specify in which region the new module should be shown via a config file.

You don't have to read the config file at all. You just load the module using the IModuleManager when you are ready. The class that implements IModule will be responsible for injecting views into regions.
See these samples:
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/7-Modules%20-%20AppConfig
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/7-Modules%20-%20LoadManual

Related

Unable to show Xamarin Forms MVVM binding result in listview

I am trying to implement MVVM approach in my xamarin forms application. During the implementations, I have hit a road block. I am unable to populate the list view with the data that i recieve from the server. I am unable to identify the binding issue.
Please let me know where is my mistake? What am I missing?
View Code
<?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="Test.Views.SubtaskPage"
Title="Select Subtask"
xmlns:viewModels="clr-namespace:Test.ViewModels; assembly=Test">
<ContentPage.BindingContext>
<viewModels:SubtaskPageViewModel/>
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="tbiAddSubtask" Text="Add Subtask" Clicked="tbiAddSubtask_Clicked"/>
</ContentPage.ToolbarItems>
<StackLayout Orientation="Vertical" Padding="10">
<ListView x:Name="lstSubtasks" ItemSelected="lstSubtasks_ItemSelected" IsPullToRefreshEnabled="True" RefreshCommand="{Binding RefreshCommand}" IsRefreshing="{Binding IsBusy}" ItemsSource="{Binding SubtaskList}}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem x:Name="menuAddTimeSpent" Clicked="menuItem_Clicked" CommandParameter="{Binding Ticket}" Text="Menu" />
</ViewCell.ContextActions>
<StackLayout Padding="20,0,0,0" HorizontalOptions="StartAndExpand" Orientation="Horizontal">
<Label Text="{Binding Subject}" VerticalTextAlignment="Center" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Response Class Code
public class SubtasksResponse
{
public int Status { get; set; }
public string Message { get; set; }
public List<Ticket> Subtasks { get; set; }
}
View Model Code
public class SubtaskPageViewModel : INotifyPropertyChanged
{
private SubtasksResponse _subtaskList;
public SubtasksResponse SubtaskList
{
get { return _subtaskList; }
set
{
_subtaskList = value;
OnPropertyChanged(nameof(SubtaskList));
}
}
private Command _refreshCommand;
public Command RefreshCommand
{
get
{
return _refreshCommand;
}
}
bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
OnPropertyChanged(nameof(IsBusy));
}
}
public SubtaskPageViewModel()
{
_refreshCommand = new Command(RefreshList);
}
async void RefreshList()
{
SubtaskList = await PopulateSubtaskList();
}
async Task<SubtasksResponse> PopulateSubtaskList()
{
RestService rs = new RestService();
IsBusy = true;
IsBusy = false;
var subtaskList = new SubtasksResponse();
subtaskList = await rs.GetSubtasksAsync(Convert.ToInt32(Application.Current.Properties["UserId"]));
return subtaskList;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
For starters we see you are binding the ListView to ItemsSource="{Binding SubtaskList} - when we then look at the ViewModel it seems that SubtaskList is of type SubtasksResponse, that type only has 3 properties.
But the item template inside your ListView is not using any of those 3 properties... it's using Ticket and Subject.
Are this properties of the class Subtasks? If so you need to bind the ListView directly to the List property for it to pick up the items in that collection.

ViewModelLocator in prism mvvm

Here is my Scenario,I need to create a simple uwp app and I have got a single viewmodel and multiple views..I am using prism mvvm/unity .
MainPage.xaml
<prism:SessionStateAwarePage
x:Class="MvvmSample.Views.MainPage"
xmlns:prism="using:Prism.Windows.Mvvm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MvvmSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" FontSize="29.333" />
<Button Content="Navigate" Command="{Binding del}"/>
</Grid>
Viewmodels.MainpageViewModel
public class MainPageViewModel : ViewModelBase
{
public string Title { get; set; }
public INavigationService NavigateToPage;
public static List<string> names = new List<string>() { "Anzal", "Rashid", "Kamil", "Fahad" };
public ObservableCollection<string> Mynames { get; set; }
public MainPageViewModel(INavigationService navigationservice)
{
this.Title = "Run Time";
NavigateToPage = navigationservice;
for (int i = 0; i < names.Count; i++)
{
Mynames.Add(names[i]);
}
del = new DelegateCommand(
() =>
NavigateToPage.Navigate(App.Expeirences.Second.ToString(),null);
);
}
}
SecondPage.xaml
<prism:SessionStateAwarePage
x:Class="Design3.Views.SecondPage"
xmlns:prism="using:Prism.Windows.Mvvm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Design3"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListBox ItemsSource="{Binding names}"/>
</Grid>
App.xaml.cs
sealed partial class App : PrismUnityApplication
{
public App()
{
this.InitializeComponent();
}
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterInstance<INavigationService>(this.NavigationService);
return base.OnInitializeAsync(args);
}
protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
this.NavigationService.Navigate(Expeirences.Main.ToString(), null);
Window.Current.Activate();
return Task.FromResult(true);
}
public enum Expeirences
{
Main,
Second
}
}
Now the problem occurs..How can I bind my secondpage to mainpageviewmodel???How to use my ViewModelLocator??
You want to register MainpageViewModel for SecondPage with the ViewModelLocationProvider, thus overriding the convention:
ViewModelLocationProvider.Register<SecondPage, MainpageViewModel>();
...preferably before navigating to the second page :-)

Prism MVVM Navigate Command

I've viewed webinar "MVVM Made Simple with Prism" (Brian Lagunas).
I followed his examples to create a main window with a menu with some
items. When operator select an item I've to open a view. Application
compiled and start but when I select an item nothing happen.
I've put some breakpoints to control that view to be opened was
istantiated, to control the correct uri and the correct key used in
extension RegisterTypeForNavigation. All appear correct. I've noticed that
when I select an item from the menu when I close the MainWindow application
don't stop automatically. I copy some strings of code to give you more
details...
Main windows xml (MenuPrincipale.xml)
<Window x:Class="PBEdPallets.Views.MenuPrincipale"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PBEdPallets.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
Title="Menu Principale:" Height="600" Width="850">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" x:Name="MenuItems" Height="19" Margin="5,5,5,0" VerticalAlignment="Top">
<MenuItem Header="Anagrafiche">
<MenuItem Header="Materiali" Command="{Binding NavigateCommand}" CommandParameter="Editor_AnaMateriali"/>
<MenuItem Header="Sottocataste" Command="{Binding NavigateCommand}" CommandParameter="Editor_AnaSottocataste"/>
<MenuItem Header="Tipi livelli pile" Command="{Binding NavigateCommand}" CommandParameter="Test"/>
</MenuItem>
</Menu>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
** Bootstrapper.cs **
using Prism.Unity;
using Microsoft.Practices.Unity;
using PBEdPallets.Views;
using System.Windows;
namespace PBEdPallets
{
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
//Importo la finestra di avvio.
return Container.Resolve<MenuPrincipale>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
//Container.RegisterType(typeof(object), typeof(Editor_AnaMateriali), "Editor_AnaMateriali");
//Container.RegisterType(typeof(object), typeof(Editor_AnaSottocataste), "Editor_AnaSottocataste");
Container.RegisterTypeForNavigation<Editor_AnaMateriali>("Editor_AnaMateriali");
Container.RegisterTypeForNavigation<Editor_AnaSottocataste>("Editor_AnaSottocataste");
Container.RegisterTypeForNavigation<Test>("Test");
}
}
public static class UnityExtensions
{
public static void RegisterTypeForNavigation<T>(this IUnityContainer container, string name)
{
container.RegisterType(typeof(object), typeof(T), name);
}
}
}
** MenuPrinciapaleViewModel **
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
namespace PBEdPallets.ViewModels
{
public class MenuPrincipaleViewModel: BindableBase
{
private readonly IRegionManager _regionManager;
public DelegateCommand<string> NavigateCommand { get; set; }
#region Costruttore
public MenuPrincipaleViewModel(IRegionManager regionalManager)
{
_regionManager = regionalManager;
//Definisco l'istanza di NavigateCommand.
NavigateCommand = new DelegateCommand<string>(Navigate);
}
#endregion Costruttore
#region Private Methods
private void Navigate(string uri)
{
try
{
//ContentRegion รจ il nome assegnato nel tag ContentControl
_regionManager.RequestNavigate("ContentRegion", new Uri(uri, UriKind.Relative));
}
catch (Exception)
{
throw;
}
}
#endregion Private Methods
}
}

Access ViewModel instance from within the ItemTemplate.DataTemplate of ItemsControl

I have data template defined in a separate file (Styles.xaml).
Page has an instance of ViewModel (I'm using MVVM pattern).
My view model contains command which I'm trying to bind to from withing the data template.
How can I access it?
ElementName and RelativeSource are not resolved by my binding.
Page Xaml:
xmlns:partial="clr-namespace:PhoneApp7.Partial"
...
<phone:PhoneApplicationPage.DataContext>
<viewModel:DynamicViewModel />
</phone:PhoneApplicationPage.DataContext>
...
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<partial:DoItItemTemplate />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
View Model:
public class DynamicViewModel
{
public ObservableCollection<string> Items
{
get
{
return new ObservableCollection<string>
{
"item 1",
"item 2",
"item 2",
};
}
}
public RelayCommand<string> DoIt { get; set; }
public DynamicViewModel()
{
DoIt = new RelayCommand<string>(OnDoIt);
}
private void OnDoIt(string args)
{
MessageBox.Show(string.Format("Yay! {0}", args));
}
}
Item User Control Xaml
<UserControl x:Class="PhoneApp7.Partial.DoItItemTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Content="{Binding}" Command="{Binding DataContext.DoIt, ElementName=ItemsControl}" CommandParameter="{Binding}"></Button>
</UserControl>
You can find complete sample by the following link:
https://dl.dropboxusercontent.com/u/83972129/binding_issue.zip
The cleanest solution to this problem (IMHO) is to give the UserControl a Command dependency property, and have the Command property of the Button bind to that.
From the MainPage, you can then bind the new Command property of the UserControl to the desired command, by binding to DataContext.DoIt, with ElementName=ItemsControl.
This will also offer some extra flexibility to bind to other commands should the situation demand it, and not hardcode the command to bind to in the template itself.
The user control:
public partial class DoItItemTemplate : UserControl
{
// ...
private static void CommandChangedCallback(...)
{
DoItItemTemplate owner = (DoItItemTemplate)d;
owner.CommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
}
private void CommandChanged(ICommand oldValue, ICommand newValue)
{
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(...)
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// ...
}
<UserControl x:Class="PhoneApp7.Partial.DoItItemTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<Button Content="{Binding}"
Command="{Binding Path=Command, ElementName=root}"
CommandParameter="{Binding}"></Button>
</UserControl>
And in the main page:
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<partial:DoItItemTemplate Command="{Binding DataContext.DoIt, ElementName=ItemsControl}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
you can use class helper if u need; it will be useful specially if you need to this command in many controls or datatemplates.

How to wire up data context in modular application?

I am writing an application, WPF, using PRISM. I'm new so apologies if this question is poor form:
I have a module that up to now has a user control for displaying a list of inspections. My module has entities written and a DbContext class to access DB. My question is where should this get initialsed and passed into my ViewModel???????
Shell XAML
<Window x:Class="ChargeMgm.Desktop.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://www.codeplex.com/prism"
Title="EMS" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition MinHeight="100"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Calibri"
FontSize="16"
Foreground="SteelBlue"
Margin="5">Street Works Modules</TextBlock>
<Border BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="3" Grid.Row="1" Margin="5">
<ItemsControl prism:RegionManager.RegionName="MainRegion"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"/>
</Border>
</Grid>
</Window>
Bootstrapper class
class Bootstrapper : UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
return new Shell();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(DefectModule.DefectModule));
}
}
Module
public class DefectModule : IModule
{
private readonly IRegionManager regionManager;
private IUnityContainer container;
public DefectModule(IUnityContainer container, IRegionManager regionManager)
{
this.regionManager = regionManager;
this.container = container;
}
public void Initialize()
{
container.RegisterType<IDefectsView, DefectsView>();
container.RegisterType<IDefectsViewModel, DefectsViewModel>();
container.RegisterType<IDefectContext, DefectContext>();
var view = container.Resolve<IDefectsView>();
if(regionManager.Regions.ContainsRegionWithName("MainRegion"))
{
regionManager.Regions["MainRegion"].Add(view);
//regionManager.RegisterViewWithRegion("MainRegion", typeof(IDefectsView));
}
}
}
If you're using Unity then you're in luck. If you need it initialise you DB context then you can do something like this:
IModule implementation code (for your module)
// Create Module http://msdn.microsoft.com/en-us/library/ff648781.aspx
public class Module:IModule
{
private IUnityContainer _container;
public Module(IUnityContainer container,IRegionManager regionManager)
{
_regionManager=regionManager;
_container=container;
}
public Initialize()
{
_container.RegisterType<IView,View>();
_container.RegisterType<IViewModel,ViewModel>();
_container.RegisterType<IDBContext,DbContext>();
var view=_container.Resolve<IView>();
//Create Region http://msdn.microsoft.com/en-us/library/ff648829.aspx
_regionManager.Regions["MainRegion"].Add(view);
}
}
The above will register all of your view, viewmodel and dbcontext, resolve them and add them into a region. For the above to work I'm expecting the following:
public class View:IView
{
public View(IViewModel viewModel)
{
}
}
public class ViewModel:IViewModel
{
public ViewModel(IDbContext context)
{
}
}
Basically, I'm expecting your viewmodel to be injected into your View and your DB Context to be injected into your ViewModel using Constructor Injection.
BTW - the links in the code go to MS sites that will provide more background on Module creation and Regions. I've got one final link: This is a "Hello World" Prism app. It's for Silverlight but this is basically the same thing as a WPF app in terms of code structure so should be useful:Prism Hello World