WPF Prism - Send an object in navigatoin - c#

I'm writing a WPF/MVVM application using Prism/Unity. I'm having issues on how to transfer data from one usercontrol to another when navigating a region.
I have a region
This is populated by a user control("ContactsList") solely containing a grid, this grid is bound by ItemsSource & SelectedItem.
I want to navigate to ContractEdit and pass the Contact bound to SelectedItem.
LocalRegionManager.RequestNavigate(ContactRegions.MainRegion, ContactsUri.ContactsEdit);
I can pass a single value in he Datacontext or the Uri but I don't want to have to go back to the Database to get the data when I already have the whole Item in the list view model.
How can I pass a whole object from the list usercontrol to the edit control?
Thanks in advance.
Conclusion
I added the EventAggregator
public ListViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
{
_eventAggregator = eventAggregator;
_regionManager = regionManager;
}
Published the even when navigating
private void OnSendData()
{
_regionManager.RequestNavigate(ShellRegions.LeftRegion, ModuleAUris.Edit);
_eventAggregator.GetEvent<UserEvent>().Publish(_selectedItem);
}
Then subscribed to the even to get the object.
public EditViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
{
_regionManager = regionManager;
_eventAggregator = eventAggregator;
BackCommand = new DelegateCommand(OnBack);
_eventAggregator.GetEvent<UserEvent>().Subscribe((e) => UserEntity = e);
}

There is a handy example of this in the prism examples downloaded with the prism installer; where they use a separate file for subscribing/unsubscribing to an event. There's a desktop and Silverlight version.
It shows how to create a token used to unsubscribe from the event also.
Oli

You can have a NavigationManager that will know what is the current Contact. You just have to set it and get it for example in the NavigatedFrom and NavigatedTo, or somewhere else. It can either be in its own module or in the core project, as long as CurrentContact is an IContact.
You can also have a service in the list module that will access the current contact from other modules.
The service might be more appropriate if te NavigationManager has no other purpose.
edit
These services explained on msdn.

Related

How to add custom service to wpf prism project which will be accessible in all viewmodels?

I am working on application WPF in Prism framework.
Now I need two things . 1. a custom dialog box to show various custom messages and 2. Save and update and get Application Settings from a file stored in system by Json .
Now I have created a class which does the Jason related stuffs and have few APIs like get and set and save obj to Json and Json to Obj. Now I need this operations in all ViewModels of my class .
I do not want to create an instance of this class in all ViewModels separately.
So , I am looking for some prism supported services which can help in this direction.
I have seen this question (What exactly are "WPF services"?) but it is not giving me what I want or may be I am understanding the answers in this question.
I would be obliged if someone gives me a hints in this regards
First create an interface IJsonService and implement this on a class JsonService.
add these two functions in Interface IJsonService :
object DeSerializeJsonToObject(string fileName);
void SerializeObjectToJson(object obj);
Now in App.xaml.cs write the following code to register the service.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IJsonService, JsonService>();
}
Now go to any viewmodel where you want this service. Create a field like JsonService .
Modify the constuctor of the ViewModel like following :
IEventAggregator eventAggregator;
IJsonService JsonService;
public SensorConfigViewModel(IEventAggregator _eventAggregator,
IJsonService jsonService)
{
this.eventAggregator = _eventAggregator;
this.JsonService = jsonService;
}
Now you can access methods declared in the interface IJsonService and which are implemented in function in JsonService.cs
Just type JsonService.DeSerializeJsonToObject(....) and you can access the service methods.
I have used latest Prism nuget packages. In old Prism packages there could be some differences.

Dependency Injection to Page ViewModels in WPF

I am creating a WPF application using .NET core 3.1
I have developed ASP.Net applications in the past and I was excited to use this in WPF. I did some searching and realized DI in WPF isn't as straightforward as it is in ASP.Net, meaning you have to register Views and ViewModels.
My structure is like this
MainWindow
|---BalanceIntegrationPage
|---BalanceIntegrationViewModel
Everything is handled in XAML with the MainWindow.xaml.cs having only generated code, and the BalanceIntegrationPage.xaml.cs has a single line added to it in the constructor
DataContext = new ScaleIntegrationViewModel();
That could not be handled in the xaml because DI requires parameters in the constructor.
Here is my app.xaml.cs:
protected override async void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
ServiceCollection services = new ServiceCollection();
services.AddScoped<MainWindow>();
services.AddScoped<ScaleInterfacePage>();
services.AddScoped<ScaleIntegrationViewModel>();
services.AddScoped<IScale>(provider => new Scale("1234"));
ServiceProvider serviceProvider = services.BuildServiceProvider();
MainWindow mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.Show();
}
My ScaleIntegrationViewModel looks like:
public ScaleIntegrationViewModel(IJMDataIntegration jmContext = null, IBalanceIntegrationContext localContext = null, IScale scale = null)
{
_jmContext = jmContext ?? new JMDataIntegration();
_localContext = localContext ?? new BalanceIntegrationContext();
_scale = scale ?? new Scale("1234");
//JK read from config
_commPort = "1234";
}
I also attempted to use the pattern described here
When I step through the code, my IScale object in the ViewModel constructor is always null.
Any Suggestions??
edit:
Based off a comment, I removed the ViewModel call in the page constructor and instead assigned it on the .xaml
This forced me to create a default paramaterless constructor which then breaks DI.
It almost starts to seem like I need to inject the services into the MainWindow ctor and then pass them down to everything I call from there. Makes no sense to me, since at that point, I may as well throw away DI and just new them up when I need them.
You are missing the configuration for some dependencies. From the code you've posted, you missed to configure IJMDataIntegration and IBalanceIntegrationContext:
protected override async void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
ServiceCollection services = new ServiceCollection();
services.AddScoped<MainWindow>();
services.AddScoped<ScaleInterfacePage>();
services.AddScoped<IJMDataIntegration, JMDataIntegration>();
services.AddScoped<IBalanceIntegrationContext, BalanceIntegrationContext>();
services.AddScoped<IScale>(provider => new Scale("1234"));
services.AddScoped<ScaleIntegrationViewModel>();
ServiceProvider serviceProvider = services.BuildServiceProvider();
MainWindow mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.Show();
}
Also, as already mentioned, you have to inject the view model into the MainWindow too. This where the dependency graph starts, the root of the application:
partial class MainWindow : Window
{
public MainWindow(ScaleIntegrationViewModel viewModel)
{
this.DataContext = viewModel;
}
}
To enable the full power of Dependency Injection (and make mocking easier) you should make use of Dependency Inversion throughout the application. This means you should only depend on interfaces and therefore only have interface types in your constructors:
partial class MainWindow : Window
{
public MainWindow(IScaleIntegrationViewModel viewModel)
{
this.DataContext = viewModel;
}
}
Controls like pages should be generated via DataTemplate and not instantiated directly in XAML. All you need to do, is to inject the page view models e.g. into another view model. Bind them to a ContentPresenter and define an implicit DataTemplate which targets the type of the page view model. This template contains the actual page. See this example.
Search for View-Model-First pattern if you need more details. Basically, a view can be defined as a data template and associated with a view model type. Data templates can be defined as resources, or they can be defined inline within the control that will display the view model. The content of the control is the view model instance, and the data template is used to visually represent it. This technique is an example of a situation in which the view model is instantiated first, followed by the creation of the view.
This is the preferred way, especially in conjunction with Dependency Injection.
Dependency injection means that you inject the dependencies, but don't construct them by yourself.
In your example, you construct the viewmodel manually inside of your page. This is not DI. You have to inject an instance of the viewmodel into the page.
You don't show us how you inject the page into the main window. I am not sure that using DI to inject a page into a window is a good decision. This is UI, and you can describe everything without DI (by using the data templating and pure XAML).
Anyway, to inject your viewmodel into the page, just introduce a constructor parameter in your page:
public ScaleInterfacePage(ScaleIntegrationViewModel vm)
{
InitializeComponent();
DataContext = vm;
}
That's it, you're all set.

View model not resolved when using an interface with Xamarin Forms and Caliburn Micro

I am attempting to register an interface for a viewmodel, so that I can inject the interface into another view model. I have something similar to the following code in the app constructor in a shared .net standard class library in a Xamarin Forms app using Caliburn Micro.
public App(SimpleContainer container)
{
this._container = container;
_container.Singleton<ISetupViewModel, SetupViewModel>();
InitializeComponent();
DisplayRootView<SetupView>();
}
Using this will display the correct view is displayed, however the SetupViewModel doesn’t get created. But when I change the registration to not use the interface as below, then the SetupViewModel is created and resolved.
_container.Singleton<SetupViewModel>();
Is there any reason that I can't register a view model with an interface?

Access IRegionManager Object from a Module ViewModel - Prism with Unity

I am trying to access the regionManager of the Shell from the Viewmodel of a module in a WPF-Prism Application with Unity for view-based Navigation
Following is the constructor of the ViewModel for a Module.
public LeftSelectionViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
}
Seems can't construct the Viewmodel object(I use auto-wiring).
Works fine when I remove the parameter from the Constructor.
What is missing?

How View is resolved in Prism

I am newbie in using Prism. I have created a sample application to understand it. I have some queries on, how it is working.
Question 1:
public class HelloModuleCls : IModule
{
IRegionManager regionManager;
IUnityContainer container;
public void Initialize()
{
RegisterViewAndServices(); // Injecting occurs here
if (regionManager.Regions.ContainsRegionWithName("ViewHolder"))
{
regionManager.Regions["ViewHolder"].Add
(
***container.Resolve().View***
);
}
}
protected void RegisterViewAndServices()
{
//since angular brace is not appearing, I am using ( , )....
container.RegisterType(IHelloView, HelloViewUC)();
container.RegisterType(IHelloViewModel, EmployeeVM)();
}
}
public class EmployeeVM : INotifyPropertyChanged, IHelloViewModel
{
public EmployeeVM(IHelloView targetview)
{
}
}
Upon hitting the line, container.Resolve().View,
the ctor of the VM, gets executed with, view type got injected in the param "targetview".
1. So, what will happen behind the scene, when that line got a hit...?
Question 2:
Project Name:- HelloModule (Another Silvelight Class Library referred in the startup Silverlight Application)
public class HelloModuleCls : IModule
{
public HelloModuleCls(IRegionManager rm, IUnityContainer um)
{
regionManager = rm;
container = um;
}
}
(Shell.Xaml) (In Startup Silverlight Application)
<ContentControl Prism:RegionManager.RegionName="ViewHolder" Grid.Row="0">
</ContentControl>
This Module is in another project. I am having the Region in the startup project. I have seen that, in the ctor of the HelloModuleCls, the region got which is used in the Startup project, got injected perfectly...
2. So, how the prism is passing those regions..... Is it like, once the region got created, then it will be injected to all the available Modules ctor or some other concept.
May I kindly know, how this is working out, So I can able to understand more.
Thanks in advance.
Answer 1: it resolve the one view, normally you want to register views with some key to be able to differentiate them.
Answer 2: it's the other way round, the regions exist in the application and the modules use them. They need to somehow know the name of the region, of course.
Unsorted comments:
RegisterType<ISomeInterface, OneOfItsImplementations>() lives in the Microsoft.Practices.Unity namespace, so add using Microsoft.Practices.Unity; to "see the angular brace appearing"
use something like regionManager.RegisterViewWithRegion("MyRegion", typeof(AView)) to make the view automatically appear in the region
to switch views in a region, have a look at prism's navigation features, like RegisterTypeForNavigation and RequestNavigate
the view uses the viewmodel, not the other way round. The viewmodel normally doesn't know about the view.

Categories

Resources