I am new to WPF and MVVM Prism. I have been an ASP.NET developer for more than 5 years and recently switched to a WPF project.
I am currently using Prism 5.0 with Unity. The main purpose of following the pattern is to implement modularity and loose coupling.
My question is this: I would like to make my User Object universal and accessible across all modules.
This is what I've done so far. Upon start up, users are greeted with a login screen (LoginView.xaml) in Login project. LoginViewModel will then validate credentials. Upon successful validation, LoginViewModel will then pass this retrieved object to a static class in Infrastructure project. Since user login is only single / universal instance, I have created a static class under Infrastructure project to hold the user object.
I have tried GenericPrincipal, while it does persist data across views, it's not sophisticated enough to hold data that I need. Hence I went for static class instead.
Does anyone have a better suggestion around it?
Instead of registering your User object in a static class, I suggest you to register the User instance in the Unity container itself.
In your LoginViewModel, you should get an instance of your IUnityContainer class.
public LoginViewModel(IUnityContainer container)
{
Container = container;
}
In your Login method, you register your user object:
private void Login(object obj)
{
...
if (user.Authenticated)
{
Container.RegisterInstance("CurrentUser", user);
}
...
}
To access your object, you use the following code snippet:
Container.Resolve<YourUserClassHere>("CurrentUser");
For more details see:
Persisting user credentials in WPF w/Unity and MVVM
Related
When I see some examples about how to use dependency injection in a WPF application, I have seen that this is configure in the app.xaml.cs file, that it is execute before any window is showed.
But in my case, some dependencies depends on the selection of the user in the first windows.
This is the case. I want to have an application that allow to upload and download files from two different clouds. The user selects from a dropbox which cloud he wants to use. Once it is selected, the whole application will use the selected cloud. If the user wants to use the other cloud, he has to close and run the application again (it is a bit silly behaviour, but it is to simplify and I think it expose the doubt better).
How the user need to select the cloud, I can't configure the dependency in the app file.
My code is this:
interface ICloudService
{
UploadFile(string pathFileToUpload);
DownloadFile(string pathToSaveFile);
}
class CloudOneService() : ICloudService
{
//Implementation
}
class CloudTwoService() : ICloudService
{
//Implementation
}
In the app.xaml.cs file, I should to configure the dependencies, something like that:
public partial class App : Application
{
public App()
{
host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddScoped<ICloudService, CloudOneService>();
}).Build();
}
}
But this code first it will use always CloudOneService and second, it is run before the user can select the cloud.
So I am not sure how could I configure the dependency injection when it depends on the selection of the user.
How could I do it?
Thanks.
So, as I see it, your application has two states:
where the user did not yet select something and
after the selection happened
Now, the question is: Do you need the interface to be available in state 1? If yes, then you should provide "something" there. If no, then you can easily resolve it when going into state 2, e.g. by using a factory class, like you suggested.
enum CloudServiceType
{
One,
Two
}
interface ICloudServiceFactory
{
ICloudService GetService(CloudServiceType selectedCloud);
}
If you need to have an ICloudService ready before the selection, you could either inject a "default" one using regular DI or just let the factory provide a default one with a GetDefault() method.
I want to centralized all bussiness logic into viewmodel. but i have problem with implemetation event. so i think i create delegate in code behind for reference. and the implementation in view model by pass view object to view model. how to do this?
<UserControl x:Class="Project.Views.IndexView">
<PasswordBox PasswordChanged="PasswordChangedHandler"/>
</UserControl>
C#
public partial class IndexView
{
public IndexView()
{
InitializeComponent();
DataContext = new IndexViewModel(this);
}
private delegate void PasswordChangedHandler(object sender, RoutedEventArgs args);
}
public class IndexViewModel
{
public IndexViewModel(UserControl view)
{
view.PasswordChangedHandler = this.PasswordChangedHandler;
}
public string Password { get; set; }
private void PasswordChangedHandler(object sender, RoutedEventArgs args)
{
var passwordBox = (PasswordBox)sender;
Password = passwordBox.Password;
}
}
If you want to stay out of code behind you are going to have to look into implementing EventToCommand: Support in Framework, and Core as long as you reference Windows.Interactivity.WPF Nuget
Bind command to Loaded event of view
MVVM Light: Adding EventToCommand in XAML without Blend, easier way or snippet?
https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/may/mvvm-commands-relaycommands-and-eventtocommand
"I want to centralized all bussiness logic into viewmodel." - Business logic belongs to the Model (but I guess you know this).
"by pass view object to view model. how to do this?" - You should never pass an object of the View to the View Model.
If your real problem is "How to pass the password to the view model", then you should know that you need to pass it explicitly. This is how the control is designed to be used.
The reason for this is, that the password is a very critical information. Passing the password around the application always introduces a privacy or security issue. Strings are stored in the memory as their simple byte representation/plaintext. The memory is public. This means, everybody can read a string from the memory. That's why the PasswordBox exposes the password as SecureString.
The general recommendation is to avoid this classic authentication system where the user enters a plaintext password.
The recommendation is to use existing authentication flows.
For example, you can use Windows authentication. By storing application data in the user domain, you can be sure that only the user that is currently logged in is using your application: Windows authentication ensures that a user of a instance-per-user desktop application is properly authenticated.
Alternatively, use OAuth authentication.
Modern web applications ask the user to login with their Google or Facebook account etc. Those authentication flows are very secure. The password handling is completely external as it is delegated to a 3rd party service (on behalf of the user that must have a related account). This service will maintain the implemented authentication procedure to keep it secure up to the latest security standards.
Although I highly recommend against implementing a custom authentication flow, the following example is meant to show you how PasswordBox is intended to be used:
MainWindow.xaml
<Window>
<PasswordBox PasswordChanged="OnPasswordChanged" />
<Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
private ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
this.ViewModel = new ViewModel();
this.DataContext = this.ViewModel;
}
private void OnPasswordChanged(object sender, RoutedEventArgs args)
{
var passwordBox = sender as PasswordBox;
this.ViewModel.HandlePassword(passwordBoc.SecurePassword);
}
}
The general practice is to ensure that the desktop application is installed as instance-per-user (so that we can rely on the OS authentication system) or to use a OAuth service. OAuth is very popular for web applications where an URL can be visited from any system (which declares local OS level authentication as meaningless).
I am building a Winforms app.
I have few Projects
The main project is the winforms project
I have DB project (using EF core) and business project with my services
I am using Ninject for DI injection of the business services. I am initializing the StandartKernel in the main form with all my registered bindings.
So when i open a Form from the Main form i am passing the needed services from the kernel to the constructor of the form.
The problem is when i want to create a form from other form (not the main one)
Example:
From the Main Form i create ClientForm
But i want to add to the client's collection "Cars" a new object so I want to create a Car form directly from the ClientForm. And here is the problem: in the ClientForm i dont have CarService to pass to the new form, i have only ClientService.
What is the best solution to do the DI in such a project?
I am thinking to make all my services static but...
Any other solution?
Well, I was working with something like that these days. I was using the Unity Container to make my DI. The code is not entirely complete and not optimized at all.
If you've seen or used ASP.NET APIs, I tried to play the same operation in a windows forms application.
I used a extension method like below to add all my forms to the Unity Container.
public static void AddForms(this IUnityContainer container, Assembly assembly = null)
{
if (assembly == null) assembly = Assembly.GetCallingAssembly();
foreach (var form in assembly.GetTypes().Where(x => typeof(Form).IsAssignableFrom(x)))
{
var initForm = form.GetCustomAttribute<InitialFormAttribute>();
if (initForm != null)
{
container.RegisterType(typeof(Form), form, "InitialForm", new TransientLifetimeManager());
}
else
{
container.RegisterType(form, new TransientLifetimeManager());
}
}
}
And use some coding to implements the startup just like in ASP.NET. At startup I added the EF Core DbContext to the container, just as I added everything I needed to be able to simply make the Unity Container create the instances of the forms for me, and I always have access to my services within the forms.
If you want to see the full code, comment below.
I am doing a course of MVVM for Xamarin Forms, and the teacher in one class used FreshIOC.Container.Register to "register different instances or dependencies", and I don't understand how that affects my code, if someone can explain it I would appreciate it.
My code as an example:
public App()
{
InitializeComponent();
FreshIOC.Container.Register<IContactService,ContactService>();
FreshIOC.Container.Register<IUserDialogs>(UserDialogs.Instance);
}
class MainViewModel : FreshBasePageModel
{
private IContactService service;
private IUserDialogs dialog;
public MainViewModel(IContactService Service, IUserDialogs Dialog)
{
service = Service;
dialog = Dialog;
}
public override async void Init(object initData)
{
dialog.ShowLoading();
var tempt = await service.GetData();
Contacts = tempt;
dialog.HideLoading();
}
}
I don't see what FreshIOC.Container.Register does, or how it connects to the MainViewModel class. By the way, there is another method called "Resolve" instead of "Register", If you could also explain that one I would appreciate it.
That is all, if you need anything more from my code I will provide it as soon as I see your request, thank you all so much for your time, have a nice day.
The Register registers your concrete classes at the IoC framework.
So, IoC in short will work like this:
Instead of making new ContactService all the time, you'll ask the IoC framework to give you one.
This has some benifits;
because you often register by interface, you only need to worry about the ContractService constructor at one place, and not all over the place.
it makes your code better testable because the consuming page is not responsible for creating the service. This might sound a bit mystic, but if you write unit test, you'll immediate see the benefits.
what does Register do?
It makes sure you can request this service from the IoC framework.
The first one registers the ContactService as an IContactService; so, if you request an IContactService you'll get the registered type.
The second one registers the instance of a type: if you request it, you'll always get that instance. Works well for settings and thread safe stuff. Works not at all for database related things.
What does Resolve do?
It enables you to retrieve a service from the IoC framework, but note: there might be better ways e.g. by constructor injection.
This code is an example of constructor injection: by registering the IContactService you've enabled the possibility to resolve the service automatically. If you ommit the registration this is not possible:
public MainViewModel(IContactService Service, IUserDialogs Dialog)
{
service = Service;
dialog = Dialog;
}
If you didnt use the IoC framework, you would have ended up with new ContactService in every model you where using, which can be considered as an antipattern for such services, because;
changing the implementation concrete type will result in a lot of code changes.
changing the constructor would lead to a lot of code changes
unittesting the consumer causes an instantation of the service, which can lead to side effects.
I have detected, that during loading the main page several controllers are instantiated (I think because the main page is built from several parts). The controllers instantiate the API classes to query some data through them. I was wondering how and where I could share the same API class instance between them.
I can imagine such a code:
class HomeController : Controller
{
private MyApi Api;
public HomeController()
{
this.Api = get the pervious MyApi instance form somewhere
if (this.Api == null) // 1st time
{
this.Api = new MyApi();
put this instance to somewhere to share between controllers
}
This "somewhere" is not a session, because next page load needs another MyApi instance. It must go to an object property which remains intact during the whole page load process, but is dismissed when the html result is generated. It must be really a simple thing, but I really don't know where it is :( Could somebody help me?
You can consider using Microsoft Unity Framework in your application.
Using Unity Dependency Injector you will be able to inject instances of MyApi class into the any controller and avoid writing " if (this.Api == null) " these types of checks and also managing instances of it in some Session or Application level variables, which makes code dirty.
For this specific problem "It must go to an object property which remains intact during the whole page load process, but is dismissed when the html result is generated", You can configure Unity Injected object to have a life time of "Scoped". Meaning, the object will be created once per request.
Here's is a link on configuring Unity in an asp.net core application
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2