What I like about your videos is, No introduction music, no background music, no hyped introduction, "no beating around the bush". Always simple and to the point. Wholesome learning without wasting a minute. Thanks!
Thanks GuruPrasadh, I thought about making an intro but had the same feelings. I'd rather just hop right into the video! I'm glad others feel the same way and are eager to just get into the video content.
Hi Sean, This video confused a bit me. Instead of creating a delegate, wouldn't it be useful to create some simple class called as "CreateViewModelBase" and make it generic where TViewModel : ViewModelBase . And by injecting that TViewModel to constructor in this class, we can succeed to get that view model without specifying the delegate in the container by not creating lambda expression and not adding whole required services in the app.xaml. And if we add a method called as "CreateViewModel" to CreateViewModelBase class and returns to injected viewmodel directly we would not have to add all required services to constructors while delegate is registered. Sorry for my poor english :)
Yeah I actually really like this whole delegate method for handling the creation/state of our viewmodels. The factory pattern just became too much classes and interfaces to handle tbh. Much cleaner approach for sure.
maybe too early, obviously is good to not spam the API but it would be a good idea to have a way to refresh the values without restarting the application. I dont understand much of stock but they are restless right? maybe this is irrelevant specially if the buy page updates on search (still there should be a update button in case someone goes to get a coffee before buy the stock and in the mean time the stock crashed). a few remarks, at this stage when you don't add a symbol to the search it throws InvalidSymbolExeption(Symbol) maybe could simply make an if empty display message "need to add symbol." and having a "memory" for the fields we searched, usernames and passwords used would be great. maybe even have a drop down option for all the symbols available.
Hey great video thanks for the detailed explanation about the navigator in , could you help me? how to do to use the navigation in an embedded view? for example if insert a new view with a CRUD as for example to manage users, how to extend the code so that the embedded CRUD navigator with the main navigator works correctly. Thanks for your feedback and take care.
Hey MGA! I think I understand your question, but just want to clarify. Do you mean a situation where you would have multiple layers of navigation? Such as navigating to a page that has navigation between many sub-pages?
@@SingletonSean Hi yes i describing a multiple layers navigation. I am going to try to describe the multiple layers navigation for example I have a PeopleListView with a list of People, that list has und CRUD to manage or register each person. when the user clicks on create/edit person, the list with the CRUD disappears and an AddDetailsPersonView appears with a button to return to the PeopleListView and three radio buttons (Details, Address and Skills Pages). the first radio button "Details" handles the DetailsPersonView with the personal data (first name, last name, marital status etc) of each person to be created or selected. the second radio button "Address" manages the AddressesPersonView, the view has a CRUD (Buttons) for the addresses of each person, that is to say that each person can have more than one address for that reason it is introduced by CRUD one or more addresses. the third radio button "Skills" manages SkillsPersonView, the view has CRUD (Buttons) for the skills/study of each person, i.e. each person can have more than one skill, therefore one or more skills are entered via CRUD. MainView | PeopleListView (CRUD to add a Person) | - list of inserted Persons > DetailsPersonView - Form "Personal Data" > AddressesPersonView - CRUD manage the addresses - List of inserted Addresses > SkilsPersonView - CRUD manage the Skills - List of inserted Skills I hope I have described in the best way the case. best regards.
Great series so far, I am very happy that there are people making such content! I'd like to suggest something additionally for the view models. First of all, I think that replacing factories was really good thing to do, because to add a new view in this app would be a nightmare for someone who is no really familiar with the project. However, there are few more things that I think could be done to make the process even more simplified. The main problem for me is that switch statement in the SimpleTraderViewModelFactory which is always one additional step you need to do in order to add new view model. So a way to get around that would be to modify your ViewType enum and make it similar to the enums in Java so you would let's say have Login => typeof(LoginViewModel) Then in the factory, you would need to have IServiceProvider injected and you would call this._serviceProvider.GetRequiredService(viewType.ViewModelType) And lastly, you would need to register your viewmodels as Transient (the ones that need to be). Here's a snipped of what I wrote: pastebin.com/FF35cDBw I know that injecting dependency container is not a good thing, but I would prefer this over having maintain additional switch statements. I hope this is helpful and good luck with the app, looking forward to seeing it ready!
Hey Cyecize, that's a really creative solution! This is a great way to leverage the power of dependency injection containers. As you mentioned, I agree that injecting the container isn't a good thing, but I can't imagine it being terrible in this scenario. It's not like the container is getting injected everywhere since it's just wrapped in your Navigator. I also like how this makes registering view models as Transient relevant, where as before everything was resolved one time so it didn't matter if it was Transient/Singleton/etc. I also like Java enums (Java will always have a special place in my heart, lol). I'm not sure I'll implement this in this series, but I'm going to try it out. I'm planning to do a little series solely on navigation since everyone always has questions about it. Of course there's so many different ways to do things, so I'm trying to figure out what is the best to spread knowledge on. Perhaps this could be relevant in the series (and of course you'll get the shoutout in the video if I make it, haha). Thanks for the suggestion!
Great, but I have to admit that I still have a lot learn before I get used to dependency injections and other loose-coupling things. In my case, I don't always get all the 3 indexes from the API... If I switched between the different views and then reloaded the home one, I normally got the 3 indexes but not always... I don't know if the following tutorials implement a refresh functionality, but I'll certainly try to add it myself.
Great video! Might be out of the scope for this series, but I've been trying to figure out how to approach handling new windows via DI. I followed a similiar approach of having a WindowFactory which would handle the creation of new windows and registering them with DI so any services were injected but struggled to get the services to dispose after the window closes. Would it be possible for you to cover this topic in one of your upcoming videos?
@@SingletonSean Yeah the Window Control, as I'm injecting services to the windows constructor I was hoping that all services for that window would get disposed once I closed it. I'm guessing its because I'm not correctly disposing the window object properly but not sure where this should be handled, I'd guess maybe in the factory and subscribe to the closed event?
I'm working my way through this episode and I have a question. What is the advantage to using the CreateViewModel delegate vs just registering a viewModel in the App.xaml.cs with simply service.AddSingleton and then using just the viewModel in the SimpleTraderViewModelFactory constructor/fields/switch statement? I've watched a lot of episodes, lol, and I may have just forgotten what it solves, but its working with the simpler way ie, its injecting the viewModels dependencies for me automatically since I've registered them already, so I'm just wondering... To make this question a little bit less abstract, here is the simplest flow that is working for me: 1) I register the BuyViewModel 2) I comment out the delegate registration services.AddSingleton(); //services.AddSingleton(services => //{ // return () => services.GetRequiredService(); //}); 3) Then I just let DI build the object for me in the Factory (I've removed other viewmodels for this sample's sake) public class SimpleTraderViewModelFactory : ISimpleTraderViewModelFactory { private readonly CreateViewModel _createHomeViewModel; private readonly BuyViewModel _buyViewModel;
public SimpleTraderViewModelFactory(CreateViewModel createHomeViewModel, BuyViewModel buyViewModel) { _createHomeViewModel = createHomeViewModel; _buyViewModel = buyViewModel; } public ViewModelBase CreateViewModel(ViewType viewType) { return viewType switch { ViewType.Home => _createHomeViewModel(), ViewType.Buy => _buyViewModel, _ => throw new ArgumentException($"The ViewType does not have a ViewModel: {viewType}", nameof(viewType)) }; } } Thank you in advance. :-)
I also found this can be simplified syntactically services.AddSingleton(); services.AddSingleton(services => { return () => services.GetRequiredService(); }); can be (replace with method group, then use expression lambda services.AddSingleton(); services.AddSingleton(services => services.GetRequiredService); :-)
Hey JC, the BuyViewModel should only be registered as Singleton, whoops! Thanks for catching. Anyways, the advantage of using the CreateViewModel delegate is in case the view model is not a singleton and you want to resolve a different/new view model when you switch views. Since the BuyViewModel is a singleton, it is safe to just inject it into your view model factory. However, if you want to switch it to transient in the future and get a new instance when you switch views, you would need to use the CreateViewModel delegate or implement some sort of factory to get a new instance from your service provider.
I found an interesting (to me) way to clean up the DI container a bit. I've never heard of them, perhaps I should have by now, lol, but you can make "local functions" that can be placed inside of another function and have scope to its parent variables. So similar to using #Region tags, which I hate, you can organize your registrations within the CreateServiceProvider() function similar to this. (It's just another way to do the same thing, but I'm liking it so far. Ignore the names, I just grabbed a sample from my project you're helping me to make, lol, but the logic is the same) private IServiceProvider CreateServiceProvider() { IServiceCollection services = new ServiceCollection(); RegisterMiscellaneous(); RegisterRepositories(); RegisterServices(); RegisterViewModels(); services.AddScoped(s => new MainWindow(s.GetRequiredService())); return services.BuildServiceProvider(); void RegisterMiscellaneous() { services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } void RegisterRepositories() { services.AddSingleton(); services.AddSingleton(); //services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } void RegisterServices() { services.AddSingleton(); services.AddSingleton(); ... } void RegisterViewModels() { services.AddSingleton(); services.AddSingleton(s => { return () => new LoginViewModel(s.GetRequiredService(), s.GetRequiredService()); }); services.AddSingleton(); services.AddSingleton(s => s.GetRequiredService);
services.AddSingleton(); services.AddSingleton(s => s.GetRequiredService); ... } ... } ...You can even nest "local functions" within a "local function" if you need to organize further. (not shown here, but I am doing so for my ViewModels, which are growing like crazy)
Hey JC, nice organization! Lately, I've been taking it a step further and moving my register methods into separate files (ex: AddViewModelsExtension.cs) and then creating an extension method for my service collection in the file (ex: AddViewModels(serviceCollection)). It's so clean, I might have to do it in SimpleTrader too. I suppose it all depends on how large the application is and how many dependencies you register. I love organization lol.
What I like about your videos is, No introduction music, no background music, no hyped introduction, "no beating around the bush". Always simple and to the point. Wholesome learning without wasting a minute.
Thanks!
Thanks GuruPrasadh, I thought about making an intro but had the same feelings. I'd rather just hop right into the video! I'm glad others feel the same way and are eager to just get into the video content.
StraightforwardSean
Hi Sean, This video confused a bit me. Instead of creating a delegate, wouldn't it be useful to create some simple class called as "CreateViewModelBase" and make it generic where TViewModel : ViewModelBase . And by injecting that TViewModel to constructor in this class, we can succeed to get that view model without specifying the delegate in the container by not creating lambda expression and not adding whole required services in the app.xaml. And if we add a method called as "CreateViewModel" to CreateViewModelBase class and returns to injected viewmodel directly we would not have to add all required services to constructors while delegate is registered. Sorry for my poor english :)
Yeah I actually really like this whole delegate method for handling the creation/state of our viewmodels. The factory pattern just became too much classes and interfaces to handle tbh. Much cleaner approach for sure.
Thanks Brandon! I definitely agree. I've started using the delegate approach in most of my apps nowadays.
maybe too early, obviously is good to not spam the API but it would be a good idea to have a way to refresh the values without restarting the application.
I dont understand much of stock but they are restless right? maybe this is irrelevant specially if the buy page updates on search (still there should be a update button in case someone goes to get a coffee before buy the stock and in the mean time the stock crashed).
a few remarks, at this stage when you don't add a symbol to the search it throws InvalidSymbolExeption(Symbol) maybe could simply make an if empty display message "need to add symbol."
and having a "memory" for the fields we searched, usernames and passwords used would be great. maybe even have a drop down option for all the symbols available.
Hey great video
thanks for the detailed explanation about the navigator in ,
could you help me?
how to do to use the navigation in an embedded view? for example if insert a new view with a CRUD as for example to manage users, how to extend the code so that the embedded CRUD navigator with the main navigator works correctly.
Thanks for your feedback and take care.
Hey MGA! I think I understand your question, but just want to clarify. Do you mean a situation where you would have multiple layers of navigation? Such as navigating to a page that has navigation between many sub-pages?
@@SingletonSean Hi yes i describing a multiple layers navigation.
I am going to try to describe the multiple layers navigation
for example I have a PeopleListView with a list of People, that list has und CRUD to manage or register each person.
when the user clicks on create/edit person, the list with the CRUD disappears and an AddDetailsPersonView appears with a button to return to the PeopleListView and three radio buttons (Details, Address and Skills Pages).
the first radio button "Details" handles the DetailsPersonView with the personal data (first name, last name, marital status etc) of each person to be created or selected.
the second radio button "Address" manages the AddressesPersonView, the view has a CRUD (Buttons) for the addresses of each person, that is to say that each person can have more than one address for that reason it is introduced by CRUD one or more addresses.
the third radio button "Skills" manages SkillsPersonView, the view has CRUD (Buttons) for the skills/study of each person, i.e. each person can have more than one skill, therefore one or more skills are entered via CRUD.
MainView
|
PeopleListView (CRUD to add a Person)
| - list of inserted Persons
> DetailsPersonView
- Form "Personal Data"
> AddressesPersonView
- CRUD manage the addresses
- List of inserted Addresses
> SkilsPersonView
- CRUD manage the Skills
- List of inserted Skills
I hope I have described in the best way the case.
best regards.
Great series so far, I am very happy that there are people making such content!
I'd like to suggest something additionally for the view models.
First of all, I think that replacing factories was really good thing to do, because to add a new view in this app would be a nightmare for someone who is no really familiar with the project.
However, there are few more things that I think could be done to make the process even more simplified.
The main problem for me is that switch statement in the SimpleTraderViewModelFactory which is always one additional step you need to do in order to add new view model.
So a way to get around that would be to modify your ViewType enum and make it similar to the enums in Java so you would let's say have
Login => typeof(LoginViewModel)
Then in the factory, you would need to have IServiceProvider injected and you would call this._serviceProvider.GetRequiredService(viewType.ViewModelType)
And lastly, you would need to register your viewmodels as Transient (the ones that need to be).
Here's a snipped of what I wrote: pastebin.com/FF35cDBw
I know that injecting dependency container is not a good thing, but I would prefer this over having maintain additional switch statements.
I hope this is helpful and good luck with the app, looking forward to seeing it ready!
Hey Cyecize, that's a really creative solution! This is a great way to leverage the power of dependency injection containers. As you mentioned, I agree that injecting the container isn't a good thing, but I can't imagine it being terrible in this scenario. It's not like the container is getting injected everywhere since it's just wrapped in your Navigator. I also like how this makes registering view models as Transient relevant, where as before everything was resolved one time so it didn't matter if it was Transient/Singleton/etc. I also like Java enums (Java will always have a special place in my heart, lol).
I'm not sure I'll implement this in this series, but I'm going to try it out. I'm planning to do a little series solely on navigation since everyone always has questions about it. Of course there's so many different ways to do things, so I'm trying to figure out what is the best to spread knowledge on. Perhaps this could be relevant in the series (and of course you'll get the shoutout in the video if I make it, haha). Thanks for the suggestion!
Great, but I have to admit that I still have a lot learn before I get used to dependency injections and other loose-coupling things. In my case, I don't always get all the 3 indexes from the API... If I switched between the different views and then reloaded the home one, I normally got the 3 indexes but not always... I don't know if the following tutorials implement a refresh functionality, but I'll certainly try to add it myself.
Great video! Might be out of the scope for this series, but I've been trying to figure out how to approach handling new windows via DI. I followed a similiar approach of having a WindowFactory which would handle the creation of new windows and registering them with DI so any services were injected but struggled to get the services to dispose after the window closes. Would it be possible for you to cover this topic in one of your upcoming videos?
Hey Billeh, I think I have an idea of your issue. Are you referring to an actual Window (like the control), or the view models for the view?
@@SingletonSean Yeah the Window Control, as I'm injecting services to the windows constructor I was hoping that all services for that window would get disposed once I closed it. I'm guessing its because I'm not correctly disposing the window object properly but not sure where this should be handled, I'd guess maybe in the factory and subscribe to the closed event?
Hi, I noticed the source code has both addSingleton and addScoped in the app.xaml.cs
is that supposed to be that way?
I'm working my way through this episode and I have a question.
What is the advantage to using the CreateViewModel delegate vs just registering a viewModel in the App.xaml.cs with simply
service.AddSingleton and then using just the viewModel in the SimpleTraderViewModelFactory constructor/fields/switch statement?
I've watched a lot of episodes, lol, and I may have just forgotten what it solves, but its working with the simpler way
ie, its injecting the viewModels dependencies for me automatically since I've registered them already, so I'm just wondering...
To make this question a little bit less abstract, here is the simplest flow that is working for me:
1) I register the BuyViewModel
2) I comment out the delegate registration
services.AddSingleton();
//services.AddSingleton(services =>
//{
// return () => services.GetRequiredService();
//});
3) Then I just let DI build the object for me in the Factory (I've removed other viewmodels for this sample's sake)
public class SimpleTraderViewModelFactory : ISimpleTraderViewModelFactory
{
private readonly CreateViewModel _createHomeViewModel;
private readonly BuyViewModel _buyViewModel;
public SimpleTraderViewModelFactory(CreateViewModel createHomeViewModel,
BuyViewModel buyViewModel)
{
_createHomeViewModel = createHomeViewModel;
_buyViewModel = buyViewModel;
}
public ViewModelBase CreateViewModel(ViewType viewType)
{
return viewType switch
{
ViewType.Home => _createHomeViewModel(),
ViewType.Buy => _buyViewModel,
_ => throw new ArgumentException($"The ViewType does not have a ViewModel: {viewType}", nameof(viewType))
};
}
}
Thank you in advance. :-)
I also found this can be simplified syntactically
services.AddSingleton();
services.AddSingleton(services =>
{
return () => services.GetRequiredService();
});
can be (replace with method group, then use expression lambda
services.AddSingleton();
services.AddSingleton(services => services.GetRequiredService);
:-)
Hey JC, the BuyViewModel should only be registered as Singleton, whoops! Thanks for catching.
Anyways, the advantage of using the CreateViewModel delegate is in case the view model is not a singleton and you want to resolve a different/new view model when you switch views. Since the BuyViewModel is a singleton, it is safe to just inject it into your view model factory.
However, if you want to switch it to transient in the future and get a new instance when you switch views, you would need to use the CreateViewModel delegate or implement some sort of factory to get a new instance from your service provider.
Congrats , Another great video
Really Great Tutorial. Thanks a lot.
I found an interesting (to me) way to clean up the DI container a bit. I've never heard of them, perhaps I should have by now, lol, but you can make "local functions" that can be placed inside of another function and have scope to its parent variables. So similar to using #Region tags, which I hate, you can organize your registrations within the CreateServiceProvider() function similar to this. (It's just another way to do the same thing, but I'm liking it so far. Ignore the names, I just grabbed a sample from my project you're helping me to make, lol, but the logic is the same)
private IServiceProvider CreateServiceProvider()
{
IServiceCollection services = new ServiceCollection();
RegisterMiscellaneous();
RegisterRepositories();
RegisterServices();
RegisterViewModels();
services.AddScoped(s => new MainWindow(s.GetRequiredService()));
return services.BuildServiceProvider();
void RegisterMiscellaneous()
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
}
void RegisterRepositories()
{
services.AddSingleton();
services.AddSingleton();
//services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
}
void RegisterServices()
{
services.AddSingleton();
services.AddSingleton();
...
}
void RegisterViewModels()
{
services.AddSingleton();
services.AddSingleton(s =>
{
return () => new LoginViewModel(s.GetRequiredService(),
s.GetRequiredService());
});
services.AddSingleton();
services.AddSingleton(s => s.GetRequiredService);
services.AddSingleton();
services.AddSingleton(s => s.GetRequiredService);
...
}
...
}
...You can even nest "local functions" within a "local function" if you need to organize further. (not shown here, but I am doing so for my ViewModels, which are growing like crazy)
Hey JC, nice organization! Lately, I've been taking it a step further and moving my register methods into separate files (ex: AddViewModelsExtension.cs) and then creating an extension method for my service collection in the file (ex: AddViewModels(serviceCollection)). It's so clean, I might have to do it in SimpleTrader too. I suppose it all depends on how large the application is and how many dependencies you register. I love organization lol.