Reputation: 41
I am currently working on my first Prism project and am stuck on the following problem:
My project consists of two regions, a ContentRegion and a MenuRegion, each of which should access the same instance of the ViewModel.
Within the MenuRegion some methods of the active ViewModel should be selectable. In the example below, the Save method should be able to be fired from both the ContentRegion and the MenuRegion.
The problem is that I initially create two different instances of ContentAViewModel and the Save method of the menu cannot access the current data of my ContentRegion.
I've tried registering the ViewModels as singletons, but unfortunately that doesn't work and probably violates Prism principles.
public partial class App
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModuleA>();
}
}
public class MainWindowViewModel : BindableBase
{
private readonly IRegionManager _regionManager;
public MainWindowViewModel(IContainerExtension container, IRegionManager regionManager)
{
_regionManager = regionManager;
_regionManager.RequestNavigate("ContentArea", "ContentAView");
_regionManager.RequestNavigate("MenuArea", "MenuA");
}
}
<Window
x:Class="PrismProject.Views.MainWindow"
xmlns:prism="http://prismlibrary.com/"
AllowsTransparency="True"
Background="Transparent"
xmlns:core="clr-namespace:PrismProject.Core;assembly=PrismProject.Core"
ResizeMode="CanResizeWithGrip"
WindowStyle="None">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="300" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--[...]-->
<ContentControl
Grid.Column="1"
Panel.ZIndex="10"
prism:RegionManager.RegionName="{x:Static core:RegionNames.MenuRegion}" />
<!--[...]-->
</Grid>
<ContentControl
Grid.Row="1"
prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}" />
</Window>
public class ModuleA : IModule
{
private readonly IRegionManager _regionManager;
public CalculatingModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ContentAView));
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ContentBView));
_regionManager.RegisterViewWithRegion(RegionNames.MenuRegion, typeof(MenuA));
_regionManager.RegisterViewWithRegion(RegionNames.MenuRegion, typeof(MenuB));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
//containerRegistry.RegisterSingleton<ContentAViewModel>();
//containerRegistry.RegisterSingleton<ContentBViewModel>();
ViewModelLocationProvider.Register<MenuA, ContentAViewModel>();
ViewModelLocationProvider.Register<MenuB, ContentBViewModel>();
}
}
public class ContentAViewModel: RegionViewModelBase
{
private readonly IRegionManager _regionManager;
public ContentAViewModel(IRegionManager regionManager) : base(regionManager)
{
SaveCommand = new(Save);
_regionManager = regionManager;
}
private void Save()
{
// logic
}
}
<UserControl
x:Class="PrismProject.Modules.ModuleA.Views.ContentAView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Button
Command="{Binding SaveCommand}"
Content="Save"/>
<!--[...]-->
</Grid>
</UserControl>
<UserControl
x:Class="PrismProject.Modules.ModuleA.Menus.MenuA"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Menu>
<MenuItem
Command="{Binding SaveCommand}"
Header="Save"/>
<!--[...]-->
</Menu>
</Grid>
</UserControl>
How can I assign the same ViewModel to multiple views while working with only one instance of the ViewModel?
Upvotes: 0
Views: 428
Reputation: 6796
I have to do this same thing. I used an elegant technique presented by Brian Lagunas (one of the authors of Prism) in one of his online videos. Is from an excellent tutorial site that I will not name so as not to sound like a shill but if you google "Prism Problems & Solutions: Loading Dependent Views" you will find it. I strongly recommend watching it. But I will give you the outline of the technique here.
(In my case, each of my module's main views needed to share its view model with a "Tools" view that Prism needed to automatically put into another region I defined)
PageToolAttribute
) that takes the name of the "companion" viewRegionBehavior
class that (in its override of OnAttach
) hooks on to the Prism Region's ActiveViews.CollectionChanged
event. In the handler you ask the newly added view if it has that PageToolAttribute
.DataContext
as the view (i.e., the view-model you want to share) and finally navigates the other region to the newly added view.ConfigureDefaultRegionBehaviors
function and call AddIfMissing
(on the supplied IRegionBehaviorFactory
) to add your attribute.It is a lot to take in and requires a bit of a deep dive into Prism but a surprisingly small amount of code and it works like a champ. I now have 7 different Modules, each with its own companion "Tool" view that gets navigated each time.
Upvotes: 1