This session was very enlightening. I really dug into the Caliburn.Micro documentation in order to fully understand the lesson. I would suggest the same for anyone following this instructional series. The project that I am implementing has some similar functionality however, I am starting to veer off from the follow along training. This is some incredible material that you are putting out here Tim. I am looking forward to pulling the data thru from my tables and populating the information in my desktop UI. Really people, read the Caliburn Documentation. There is some really good jems of functionality that can be taken advantage of... Thanks Tim for the awesome lesson.
Hi Tim, this is a great course and in general you are my go-to guy to develop a more concrete understanding of .NET concepts. Many thanks and keep it up!
Tim, really appreciate in some of your videos at the end you go through the next steps. Allows me to try to implement the features myself before you tell us how to do it. So I have figured out how to pass our User’s name to the next view, and more complex, pulled a sql list, and figured out how to get past a deserialize Json error, then bind it to a drop down list. Can’t wait for the next video!
I tried to find out how Event Aggregation are working... and this video was very, very helpful! Thank you. And it came just in time :D I'm currently learning to work with WPF and C# for my new job and I have the feeling that your content will be very helpful ;)
29:10 but you could set container as the only parameter in the constructor, and still have the backing properties, assigning them to containter.GetInstance() inside the constructor. Am I right?
You could but I prefer not to use manual requests from the container. If possible, I avoid pulling in the container directly and instead just use constructor injection. That leads to a clearer understanding of the dependencies of any one class. Having the container in the class means you have to read the entire class to identify what the class's dependencies are.
Instead of injecting SimpleContainer to the ShellViewModel in the constructor, could you use Caliburn.Micro's static Service Locator IoC instead? And do you mean to use BindingList instead of Caliburn.Micro's BindableCollection (in LoginViewModel)?
Yes, I could use IoC (and probably should have - I can update that). As for BIndingList, I used it because it has more awareness of the properties of my objects vs. the BindableCollection (which is an ObservableCollection).
We aren't at the optimization part yet, so performance isn't going to be perfect. However, this also isn't production. A LOT changes going to production. For instance, the API will only start once. After that, it will be live and will immediately respond to calls. Right now, we are running the startups synchronously - the API starts and then the WPF project starts. Even in production, a web server takes a bit to start. However, since that happens when you first load the app and then it doesn't happen again until you crash the server or load a new version of the website/API, it isn't an issue. Also, we are running in debug mode in Visual Studio. This is intentionally less efficient in order to make debugging easier. It holds onto variables longer, for instance, so we can capture them later to review what they were set to. It also has a debugging link into the app so that we can put breakpoints in and see how our code behaves one line at a time. All of that adds time.
Many thanks for doing these Tim, much appreciated. I do have a couple of questions if I may. 1. As I've never done WPF it's all very new and really liking the caliburn system - Is there a similar system for Winforms as I believe caliburn is just for WPF - Can't recall if you mentioned it in a previous video. 2. In the ShellViewModel where you activate the Login View Model and get a new instance of it each time, what happens with the old instance of it - I guess we don't have to worry about memory leaks here and Caliburn deals with this for us. 3. Why when placing margins do we go left, top, right, bottom - couldn't they make it the same as CSS - They had to be awkward!
Caliburn Micro does not work in WinForms. I don't know of a similar tool for WinForms. The old instance of LoginForm is no longer used or referenced. .NET will see that and the garbage collector will pick up the trash when it needs the memory. I agree. I don't know why they made the margins start from the left instead of the top. Unfortunately, it is too late now.
@@IAmTimCorey Obviously I'm going through one of your older courses right now. I am the "Software Architect" (that's the title they gave me) in charge of the Service Management system that my employer uses for IT Help, Purchasing, Employee management, and many other functions. I've been using PowerShell through the SOAP API to handle automation that the system itself doesn't manage. I was allowed to start migrating everything to a compiled language instead of a scripting system, but told it had to be in .Net. So I am learning C#, RESTful API, .Net, and SQL. This is a heavy load that is going to test the programming skills that I haven't used in 15 years. You are making that burden much lighter. I can't express how much I appreciate you for making these videos available to people like me.
We could. However, CM is designed to handle more than screens with the conductor. By using object, we allow it to be more flexible. Of course, I probably won't use anything but screens so I should probably change it to what I'll use.
Following along (and loving the course, thank you). For every spot you were changing LogOnEvent(Model) is there any downside to using Rename? (ctrl+R, ctrl+R)? At a glance it's changing the class, implementations and class name in one go.
I think your talk about the pros/cons of just injecting the container and resolving dependencies in the methods is an important one. I think it is important to point out a couple of other things related to this. If you specify all of your dependencies in the constructor then you are giving whoever consumes it no choice but to provide all of the relevant dependencies, rather than sticking in a container that MIGHT be able to give it what it needs later - The dependencies still need to be resolved, but it just pushes the buck in an unclear way in my opinion, and the consumer now has to look at the code itself to figure out what its dependencies are. This is not a good situation and leads to brittle code. Furthermore, specifying the dependencies up front makes unit testing a lot more straight-forward. In the video, you do inject the container, and your ShellViewModel becomes coupled to the Bootstrapper Configure method (Where the types are registered). What are your thoughts on this, and do you think there is another suitable way? Maybe injecting a factory for LoginViewModel instead - I'm new to this, so i'm just thinking out loud.
@Andrew Thompson - you are very right. Passing around the container is the sort of thing that causes headaches down the line - having the Container is a power that is easy to abuse and goes against the spirit of DI. In my opinion, the Bootstrapper should be the one to clear the Login VM. My method (which may also prove to be horrible) is to have the Shell VM expose its Login VM as a public property. The Bootstrapper subscribes to the Log on event (as it's also in the UI thread). In handling the event, the bootstrapper calls on all instances of the Shell VM and sets each one's Login VM to a new instance.
Hi Tim, since you usually ask for suggestions of what we would like to see in future videos, here's mine. I would like to see your way of managing the events from the UI, but not the "Click" event for a button, that is so easy with CM. What I mean, is an event that you specify event name and method name that handles it in your xaml. For example, MouseRightButtonUp for a button, SelectedChanged for a some list where an item can be selected, etc. If I use , it is recognised only if I implement the event in the view.xaml.cs code (well this is another doubt I have... how good is it to use the code behind when we use MVVM?). I've had a hard time finding the way to put it only in my ViewModel. I finally did but I think I did too much work and I'm not really sure if that is the best/an acceptable way. I could also just call the method in my view model from the code behind, but I thought CM would be there for me to have an almost empty xaml.cs code. Again, there is a concept concern here for me. If EventAggregation could be used, I don't know how I could raise the event, since in this video we are raising it from our .cs code. PS: Sorry for my long message.
I've done that somewhere but I cannot remember where. I'll add it to the suggestion list. Using the code-behind isn't the right solution and this wouldn't need the event aggregator unless you wanted to broadcast that event beyond the scope of the ViewModel for the form. This should help: stackoverflow.com/questions/16719496/caliburn-micro-enter-key-event
Amazing videos Tim I really love it and I learned a lot I am following your project and picked some bits and pieces however when I jump between 2 user controls I noticed it still show new instances for example for example, you may fill the sales form with some data then you move to a different form "not the login" then go back to sales form and whatever textbox you left it filled it is now empty so I don't think that having those top variables holding the view models are actually working unless you fixed this in a different video please guide me which one exactly Thanks
@@IAmTimCorey as what you mentioned at 30:00 the downside of clearing out all the viewmodel variables "we added 3 things in the cart and then and you know what I need to go to a different form for some reason lets they want to sign for the frequent shopper card so you pull up a new form for the frequent shopper card and fill in the information and we hit submit and then you go back and say "container" give me an instance of the shopping cart or salesviewmodel and its blank because it's not a singleton so you just lost all their cart and that is not ideal where if we already have that instance we can come back to it multiple times and the cart stays where we left it" Sorry I had to type down everything I heard just in case I misheard something or misunderstood something so you can correct me Once again thank you for the amazing videos and I hope your time allows you to continue supporting this fantastic channel
Hi Tim, I should have seen this a bit sooner... Thanks for your clear explanation. Two opinions exist within our team on where to apply the EventAggregator: only on ViewModel level (it is just a caliburn thing and leave it within this context), or everywhere in the code where a pub-sub pattern is needed (it is a design pattern). Whats your opinion about this?
I prefer to leave it at the Caliburn Micro level, since it is a UI-specific tool and then use a generic pub/sub for class library and down items if I need it. Otherwise you will be passing a UI dependency down into your other layers, thus bonding them to the UI.
Hi Tim, thank you for this video, I have a question which is not related to this video, can you or any of your followers advice me on a good reporting system which works with WPF using MVVM, also need to have an end user report designer.
@@IAmTimCorey Hi Tim, I am actually talking about paid one. someone recommend Fast Reports, and Stimulsoft but I am not sure which one to go for, anyway thanks for you reply.
EventHandle is awesome. Really powerful weapon: D! I could not understand it before, and i had a problem in my application with get user info to ShellView. Thanks! I have one question. If i want put registration page for new account. I should put business logic into APIHelper and do somethink like HttpResponseMessage to "api/Account/Register" ?
Not quite tracking what you are doing with your registration page. The logic for registration (if there is any) might go in APIHelper (probably not), the VM (maybe), or in a separate class.
While walking through this a second time I misnamed my SalesView as SaleView But my models had a different name.. If you get a message about unable to find view, check the name of the view, you may need to change it in both the Xaml and CS file that accompanies it to get it to load.
Hi Tim, great series I am trying to branch out a little and display the First and Lastname from the LoggedInUserModel created in an earlier video as a Singleton I ask for that model using _loggedInUserModel = _container.GetInstance(); and I can see this is returned when I set approprate breakpoints. What I am strugling with is how do you then update the view with that data. I have used for instance in the view and have created a FirstName property in the ShellViewModel public string FirstName { get { if(_loggedInUserModel == null) { return "Not logged in"; } else { return _loggedInUserModel.FirstName; } } set { _loggedInUserModel.FirstName = value; NotifyOfPropertyChange(() => FirstName); } } The property never seems to be set and the view not updated when the Login form closes. Any hints?
Worked it out! Whilst I was thinking I was notifying that the property had changed in the property setter, I really wasn't and I was notifying in the wrong spot. I simply needed to add a NotifyOfPropertyChange in the IHandle method so the ShellView knew that the login event had occured. i.e. I added NotifyOfPropertyChange(nameof(FirstName)); to the IHandle method. (well at least that what i think is happening, but it is now working 😃 so for now I am going with that.) I think my confusion came from the fact it is not really that the firstname property had changed that I was interested in, but that an event had occured. i.e. the login event, which had a property of FirstName. One of the things Tim often points out is to work through the problem. Whilst for many this is a very simple and fundamental thing, for someone learning I just coulnd't get my head around it. Although I did reach out to Stack Overflow for some direction after about a frustrating week of really not understanding what I was doing, I really learnt a lot on the journey and that learning is now well cemented in my memory. Very valuable wisdom Tim. Fantastic video series.
Hi Tim, in the ShellViewModel's constructor how are those objects passed in (or wired up)? It seems more and more items are added to the constructor yet we don't have to manually update any code that is consuming this constructor. How does this work? How does the program automatically know which objects to use to instantiate the ShellViewModel regardless of which classes you put in the constructor?
That is the job of the dependency injection system. We tell the dependency injection system that if we want an ISqlDataAccess class that it should return an instance of SqlDataAccess (just an example - I don't think we use these in the WPF application). Since we told dependency injection that at the beginning, now we can just ask for an ISqlDataAccess in the constructor and DI will take care of the rest. The cool thing is that it will go all the way down the dependency tree, so if SqlDataAccess asks for an ILogger, DI will handle that dependency during instantiation.
I have seen a static class in Caliburn.Micro which is "IoC" and I used it before to get a new view model without getting it in the constructor using the method IoC.Get(); Which would be the good practice to follow?
In general, get it from the constructor. It is a better practice. However, in the cases where you need multiple instances or you want the instance to be as short-lived as possible, use the IoC method.
Can I use this event aggregation also for pushing events from helper classes? Like a method using a 3rd party dll creates a specific event, I will inform the VMs about this event to react to it? Example: I am using tapi3 to grab phone calls, when the tapi_event fires, I want to inform the VMs to change the UserControl.
Hi, I really like using dependency injection, thus I am asking myself whether it makes sense to also create an instance of LogOnEvent via DI in the LoginViewModel. Does this make any sense or would that would go a little too far as LogOnEvent does not contain any coding? Thanks for your thoughts.
Hi Tim, Is it worth to learn making Windows apps nowadays? Or better start to learn ASP.NET web apps programming? Im starting with coding right now and im so confused what to choose and focus on
Absolutely, for a couple of reasons. First, there is a difficulty progression. Console apps are the easiest. Then WinForms, then WPF and ASP.NET MVC are probably tied, the WebAPI. If you start at ASP.NET MVC, for example, you should know C#, advanced OOP, the MVC UI pattern, HTML, CSS, JavaScript, bundling, NuGet, npm, Bootstrap, and more. If that looks overwhelming it is because it is when you don't know much about any of those. Once you get a few under your belt, adding one more isn't too hard. If you move up the difficulty ladder, that's what you do. If you skip to the end, you can get swamped (or you just write poor code in all of them and struggle a lot). The second reason why WinForm apps are valuable is because they are the application type of business. I know web apps are really powerful but businesses us a ton of desktop applications. Desktop apps are more powerful, quicker, and easier to develop. Think about your PC for a minute. How many applications do you have installed (Visual Studio, Office, Chrome, Notepad, etc.) Those are all desktop apps. Those aren't getting replaced anytime soon.
@@leeroy1986 So its now good choice to start with WPF? I know the basics of c#, html, css, a bit java and i want to do smth with it. Console apps are boring hah
In the APIHelper class, I removed the following lines from my GetLoggedInUserInfo method, as it is being done in the initialize method, and it works just fine. Is there a reason that we had to repeat it in the method, or a future video has taken care of it ..? apiClient.DefaultRequestHeaders.Accept.Clear(); apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Will you ever make an android version of this app? If not, then how would I call the API and use it in Flutter? I'm planning to use Flutter for the mobile version because it's cross platform and has great performance. Will be really helpful if you replied!
Yes, I plan on developing an app along with this project at some point. As for calling the API using Flutter, that's all on the Flutter side. You just need to log in first (the same way our WPF app does) and then attach that bearer token to the header on subsequent calls (like the WPF app does).
In trying to understand the Button events, I cannot understand how the CanAddToCart is hooked into the AddToCart button. This surfaced when I noticed that the buttons are disabled. But if I return 'true' from CanAddToCart, the AddToCart button is enabled. This came about as I was trying to find a typo where the AddToCart button was becoming enabled in a future session. Upon reverting my code and reworking this lesson I am now in sync with the expected display of the SalesViewModel. However, when I toggle the return from CanAddToCart the AddToCart button enables and disables appropriately. I'm just not seeing how that property is wired to that specific button. On a side note, As a Patreon supporter, should I address this question to you from there?
hi tim so in my shellview i made a menu when i click on a button its shows another tab here's my code is that correct or should i use the event public ShellViewModel(DashViewModel dashvm, IncidentViewModel InciVM) { _dashvm = dashvm; _inciVM = InciVM; ActivateItem(_dashvm);
The button click is technically an event. I don't see the need to have a different event (unless you need to alert the child forms that are already open).
I'm just going to drop this comment here since I'm not sure where this would go. Since we login during this video I figure it's an appropriate spot to mention something strange. When I try to login I have credentials from Windows 10 on a work computer being forced in as the username. My solution to this problem was to change the username and use that to login. Have you ever come up with any bugs like this and how would you suggest fixing something like that?
@@IAmTimCorey Must have to do with my work environment. They're finally testing out Windows 10 after years of anxiously waiting. I'm willing to bet it's something the IT dept did while setting them up. It seems my way around this was to set the credentials that were getting passed in as the username in the aspnet user table.
IAmTimCorey thanks for doing these tutorials. Awesome tutorials. However, some people might be turned off by the length of your videos like this one (over 40minutes). I wonder if you could do within 10 minutes video per concept instead of per whole application in your future videos. One concept at a time within 10 min is easier to digest and follow than 40-50min long one with many concepts that overwhelm the audience. Just my comment and suggestion. You are wonderful with what u doing still.
@@vacalepic6768 I disagree, maybe to get different individual concepts across this is useful but any useful content creation it would be counter productive and wouldn't flow properly. Most of these videos if broken down into smaller parts may lead to misunderstanding the greater goal for each implementation.
I have a problem with SalesView, when im clicking the button, nothing happen. Login is fine, i got 200 and all data, next events are okay (debug goes throughout the event) but nothing displaying. Even when i change ActiveItem(_salesVM) to ActiveItem(_container.GetInstance
I'm getting a runtime error on this line: _events.Subscribe(this); The error message is: "System.NullReferenceException: 'Object reference not set to an instance of an object.'". _events is null. This is in ShellViewModel.cs. The function is "public ShellViewModel(IEventAggregator events, SalesViewModel salesVM, SimpleContainer container)". The events parameter is coming in as null. Anybody have any idea why?
check bootstrapper.cs file => seek for protected override void Configure() method. i think u will probably find cause of ur trouble. u probably forgot to register IEventAggregator as singleton. does ur code in Configure() method relevant to _container looks like this snippet? _container.Singleton() .Singleton() .Singleton() .Singleton();
For me I had to pick Async try { ErrorMessage = ""; var result = await _apiHelper.Authenticate(UserName, Password); // Capture more information about the user await _apiHelper.GetLoggedInUserInfo(result.Access_Token); await _events.PublishOnUIThreadAsync(new LogOnEvent()); } The option you picked wasn't available And public async Task HandleAsync(LogOnEvent message, CancellationToken cancellationToken) { await ActivateItemAsync(_salesVM); } but it worked as expected
I think you are using a different version of Caliburn Micro than I am. I used the 3.x version in the .NET Framework section, because that was what was out. Then, when we upgraded to .NET Core, we upgraded the Caliburn Micro to 4.x as well.
This session was very enlightening. I really dug into the Caliburn.Micro documentation in order to fully understand the lesson. I would suggest the same for anyone following this instructional series. The project that I am implementing has some similar functionality however, I am starting to veer off from the follow along training. This is some incredible material that you are putting out here Tim. I am looking forward to pulling the data thru from my tables and populating the information in my desktop UI. Really people, read the Caliburn Documentation. There is some really good jems of functionality that can be taken advantage of... Thanks Tim for the awesome lesson.
I’m glad you are enjoying it. I do have a WPF playlist that covers a lot of Caliburn Micro basics as well.
Hi Tim, this is a great course and in general you are my go-to guy to develop a more concrete understanding of .NET concepts. Many thanks and keep it up!
I appreciate the kind words.
Awesome course thus far, learning a lot about how view models and views work. This is my first time working with API's learning there too!
Awesome!
Tim, really appreciate in some of your videos at the end you go through the next steps. Allows me to try to implement the features myself before you tell us how to do it. So I have figured out how to pass our User’s name to the next view, and more complex, pulled a sql list, and figured out how to get past a deserialize Json error, then bind it to a drop down list. Can’t wait for the next video!
Excellent!
I tried to find out how Event Aggregation are working... and this video was very, very helpful! Thank you. And it came just in time :D
I'm currently learning to work with WPF and C# for my new job and I have the feeling that your content will be very helpful ;)
Awesome!
Finally the pieces come together :-)
As always another great video.
Excellent!
@20:00 you can also just hit ctrl+r, ctrl+r twice; rename it; and hit enter and it'll be automatic.
Thanks for the tip
I learned a lot till now. Thx Tim
Great!
Hi Tim, What are your thoughts on Clean Architecture and would you be willing to do a video on it and other design patterns?
It is on the suggestion list.
Very good and clear explenation. Good job!
Thank you.
29:10 but you could set container as the only parameter in the constructor, and still have the backing properties, assigning them to containter.GetInstance() inside the constructor. Am I right?
You could but I prefer not to use manual requests from the container. If possible, I avoid pulling in the container directly and instead just use constructor injection. That leads to a clearer understanding of the dependencies of any one class. Having the container in the class means you have to read the entire class to identify what the class's dependencies are.
Instead of injecting SimpleContainer to the ShellViewModel in the constructor, could you use Caliburn.Micro's static Service Locator IoC instead?
And do you mean to use BindingList instead of Caliburn.Micro's BindableCollection (in LoginViewModel)?
Yes, I could use IoC (and probably should have - I can update that). As for BIndingList, I used it because it has more awareness of the properties of my objects vs. the BindableCollection (which is an ObservableCollection).
Great concept-explanations as always!
Thank you!
I measured. It took 2:38 secs to substitute some tiny login form by the empty sales one. Isn't that too slow?
We aren't at the optimization part yet, so performance isn't going to be perfect. However, this also isn't production. A LOT changes going to production. For instance, the API will only start once. After that, it will be live and will immediately respond to calls. Right now, we are running the startups synchronously - the API starts and then the WPF project starts. Even in production, a web server takes a bit to start. However, since that happens when you first load the app and then it doesn't happen again until you crash the server or load a new version of the website/API, it isn't an issue. Also, we are running in debug mode in Visual Studio. This is intentionally less efficient in order to make debugging easier. It holds onto variables longer, for instance, so we can capture them later to review what they were set to. It also has a debugging link into the app so that we can put breakpoints in and see how our code behaves one line at a time. All of that adds time.
@@IAmTimCorey Thank you so much for the very clear answer. Remarkable job Tim!
Many thanks for doing these Tim, much appreciated. I do have a couple of questions if I may.
1. As I've never done WPF it's all very new and really liking the caliburn system - Is there a similar system for Winforms as I believe caliburn is just for WPF - Can't recall if you mentioned it in a previous video.
2. In the ShellViewModel where you activate the Login View Model and get a new instance of it each time, what happens with the old instance of it - I guess we don't have to worry about memory leaks here and Caliburn deals with this for us.
3. Why when placing margins do we go left, top, right, bottom - couldn't they make it the same as CSS - They had to be awkward!
Caliburn Micro does not work in WinForms. I don't know of a similar tool for WinForms.
The old instance of LoginForm is no longer used or referenced. .NET will see that and the garbage collector will pick up the trash when it needs the memory.
I agree. I don't know why they made the margins start from the left instead of the top. Unfortunately, it is too late now.
@@IAmTimCorey Thanks for the reply - is AutoFac similar? or something quite different? Not used AF before.
AutoFac is just for Inversion of Control / Dependency Injection. It doesn't do all of the rest of the stuff Caliburn Micro does.
Tim is a "Basket is half-full" kind of guy. ;-)
I try.
@@IAmTimCorey Obviously I'm going through one of your older courses right now.
I am the "Software Architect" (that's the title they gave me) in charge of the Service Management system that my employer uses for IT Help, Purchasing, Employee management, and many other functions.
I've been using PowerShell through the SOAP API to handle automation that the system itself doesn't manage. I was allowed to start migrating everything to a compiled language instead of a scripting system, but told it had to be in .Net. So I am learning C#, RESTful API, .Net, and SQL.
This is a heavy load that is going to test the programming skills that I haven't used in 15 years. You are making that burden much lighter.
I can't express how much I appreciate you for making these videos available to people like me.
Remarkable Work Tim!
Thanks!
Thank you very much Tim
really appreciated efforts
You are welcome.
Hi, wouldn't it be better to specify that Conductor takes only objects that implement Screen instead of object?
We could. However, CM is designed to handle more than screens with the conductor. By using object, we allow it to be more flexible. Of course, I probably won't use anything but screens so I should probably change it to what I'll use.
Following along (and loving the course, thank you). For every spot you were changing LogOnEvent(Model) is there any downside to using Rename? (ctrl+R, ctrl+R)? At a glance it's changing the class, implementations and class name in one go.
Nope, that works fine (as long as it works).
I think your talk about the pros/cons of just injecting the container and resolving dependencies in the methods is an important one. I think it is important to point out a couple of other things related to this. If you specify all of your dependencies in the constructor then you are giving whoever consumes it no choice but to provide all of the relevant dependencies, rather than sticking in a container that MIGHT be able to give it what it needs later - The dependencies still need to be resolved, but it just pushes the buck in an unclear way in my opinion, and the consumer now has to look at the code itself to figure out what its dependencies are. This is not a good situation and leads to brittle code. Furthermore, specifying the dependencies up front makes unit testing a lot more straight-forward. In the video, you do inject the container, and your ShellViewModel becomes coupled to the Bootstrapper Configure method (Where the types are registered). What are your thoughts on this, and do you think there is another suitable way? Maybe injecting a factory for LoginViewModel instead - I'm new to this, so i'm just thinking out loud.
Yes, ideally you provide just the dependencies, not the container.
@Andrew Thompson - you are very right. Passing around the container is the sort of thing that causes headaches down the line - having the Container is a power that is easy to abuse and goes against the spirit of DI. In my opinion, the Bootstrapper should be the one to clear the Login VM. My method (which may also prove to be horrible) is to have the Shell VM expose its Login VM as a public property. The Bootstrapper subscribes to the Log on event (as it's also in the UI thread). In handling the event, the bootstrapper calls on all instances of the Shell VM and sets each one's Login VM to a new instance.
Hi Tim, since you usually ask for suggestions of what we would like to see in future videos, here's mine. I would like to see your way of managing the events from the UI, but not the "Click" event for a button, that is so easy with CM. What I mean, is an event that you specify event name and method name that handles it in your xaml. For example, MouseRightButtonUp for a button, SelectedChanged for a some list where an item can be selected, etc. If I use , it is recognised only if I implement the event in the view.xaml.cs code (well this is another doubt I have... how good is it to use the code behind when we use MVVM?). I've had a hard time finding the way to put it only in my ViewModel. I finally did but I think I did too much work and I'm not really sure if that is the best/an acceptable way. I could also just call the method in my view model from the code behind, but I thought CM would be there for me to have an almost empty xaml.cs code. Again, there is a concept concern here for me.
If EventAggregation could be used, I don't know how I could raise the event, since in this video we are raising it from our .cs code.
PS: Sorry for my long message.
I've done that somewhere but I cannot remember where. I'll add it to the suggestion list. Using the code-behind isn't the right solution and this wouldn't need the event aggregator unless you wanted to broadcast that event beyond the scope of the ViewModel for the form. This should help: stackoverflow.com/questions/16719496/caliburn-micro-enter-key-event
Amazing videos Tim I really love it and I learned a lot
I am following your project and picked some bits and pieces however when I jump between 2 user controls I noticed it still show new instances for example
for example, you may fill the sales form with some data then you move to a different form "not the login" then go back to sales form and whatever textbox you left it filled it is now empty so I don't think that having those top variables holding the view models are actually working unless you fixed this in a different video please guide me which one exactly
Thanks
I do believe I get a new form each time we switch between forms. I don't remember attempting to keep the same form.
@@IAmTimCorey as what you mentioned at 30:00 the downside of clearing out all the viewmodel variables "we added 3 things in the cart and then and you know what I need to go to a different form for some reason lets they want to sign for the frequent shopper card so you pull up a new form for the frequent shopper card and fill in the information and we hit submit and then you go back and say "container" give me an instance of the shopping cart or salesviewmodel and its blank because it's not a singleton so you just lost all their cart and that is not ideal where if we already have that instance we can come back to it multiple times and the cart stays where we left it"
Sorry I had to type down everything I heard just in case I misheard something or misunderstood something so you can correct me
Once again thank you for the amazing videos and I hope your time allows you to continue supporting this fantastic channel
Hi Tim, I should have seen this a bit sooner... Thanks for your clear explanation. Two opinions exist within our team on where to apply the EventAggregator: only on ViewModel level (it is just a caliburn thing and leave it within this context), or everywhere in the code where a pub-sub pattern is needed (it is a design pattern). Whats your opinion about this?
I prefer to leave it at the Caliburn Micro level, since it is a UI-specific tool and then use a generic pub/sub for class library and down items if I need it. Otherwise you will be passing a UI dependency down into your other layers, thus bonding them to the UI.
Hi Tim, thank you for this video, I have a question which is not related to this video, can you or any of your followers advice me on a good reporting system which works with WPF using MVVM, also need to have an end user report designer.
Reporting is tough. I don't have a great solution for you (unless you go paid and then there are a few companies that have some nice products).
@@IAmTimCorey Hi Tim, I am actually talking about paid one. someone recommend Fast Reports, and Stimulsoft but I am not sure which one to go for, anyway thanks for you reply.
EventHandle is awesome. Really powerful weapon: D! I could not understand it before, and i had a problem in my application with get user info to ShellView. Thanks! I have one question. If i want put registration page for new account. I should put business logic into APIHelper and do somethink like HttpResponseMessage to "api/Account/Register" ?
Not quite tracking what you are doing with your registration page. The logic for registration (if there is any) might go in APIHelper (probably not), the VM (maybe), or in a separate class.
While walking through this a second time I misnamed my SalesView as SaleView But my models had a different name.. If you get a message about unable to find view, check the name of the view, you may need to change it in both the Xaml and CS file that accompanies it to get it to load.
Yep, it is the little details that will get you.
Have you ever used the Framework Prism?
I have, but not for a while.
Hi Tim, great series
I am trying to branch out a little and display the First and Lastname from the LoggedInUserModel created in an earlier video as a Singleton
I ask for that model using
_loggedInUserModel = _container.GetInstance();
and I can see this is returned when I set approprate breakpoints.
What I am strugling with is how do you then update the view with that data.
I have used for instance
in the view and have created a
FirstName property in the ShellViewModel
public string FirstName
{
get
{
if(_loggedInUserModel == null)
{
return "Not logged in";
}
else
{
return _loggedInUserModel.FirstName;
}
}
set
{
_loggedInUserModel.FirstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
The property never seems to be set and the view not updated when the Login form closes.
Any hints?
Worked it out!
Whilst I was thinking I was notifying that the property had changed in the property setter, I really wasn't and I was notifying in the wrong spot. I simply needed to add a NotifyOfPropertyChange in the IHandle method so the ShellView knew that the login event had occured.
i.e. I added NotifyOfPropertyChange(nameof(FirstName)); to the IHandle method.
(well at least that what i think is happening, but it is now working 😃 so for now I am going with that.)
I think my confusion came from the fact it is not really that the firstname property had changed that I was interested in, but that an event had occured. i.e. the login event, which had a property of FirstName.
One of the things Tim often points out is to work through the problem.
Whilst for many this is a very simple and fundamental thing, for someone learning I just coulnd't get my head around it. Although I did reach out to Stack Overflow for some direction after about a frustrating week of really not understanding what I was doing, I really learnt a lot on the journey and that learning is now well cemented in my memory. Very valuable wisdom Tim.
Fantastic video series.
Hi Tim,
in the ShellViewModel's constructor how are those objects passed in (or wired up)? It seems more and more items are added to the constructor yet we don't have to manually update any code that is consuming this constructor. How does this work? How does the program automatically know which objects to use to instantiate the ShellViewModel regardless of which classes you put in the constructor?
That is the job of the dependency injection system. We tell the dependency injection system that if we want an ISqlDataAccess class that it should return an instance of SqlDataAccess (just an example - I don't think we use these in the WPF application). Since we told dependency injection that at the beginning, now we can just ask for an ISqlDataAccess in the constructor and DI will take care of the rest. The cool thing is that it will go all the way down the dependency tree, so if SqlDataAccess asks for an ILogger, DI will handle that dependency during instantiation.
I have seen a static class in Caliburn.Micro which is "IoC" and I used it before to get a new view model without getting it in the constructor using the method IoC.Get(); Which would be the good practice to follow?
In general, get it from the constructor. It is a better practice. However, in the cases where you need multiple instances or you want the instance to be as short-lived as possible, use the IoC method.
If you extract an interface from a collection of empty events, can you still pattern match?
Yes.
Can I use this event aggregation also for pushing events from helper classes? Like a method using a 3rd party dll creates a specific event, I will inform the VMs about this event to react to it? Example: I am using tapi3 to grab phone calls, when the tapi_event fires, I want to inform the VMs to change the UserControl.
Hi,
I really like using dependency injection, thus I am asking myself whether it makes sense to also create an instance of LogOnEvent via DI in the LoginViewModel. Does this make any sense or would that would go a little too far as LogOnEvent does not contain any coding?
Thanks for your thoughts.
Events are part of classes, they aren't classes to themselves so they would not go in the dependency injection.
Hi Tim, Is it worth to learn making Windows apps nowadays? Or better start to learn ASP.NET web apps programming? Im starting with coding right now and im so confused what to choose and focus on
Absolutely, for a couple of reasons. First, there is a difficulty progression. Console apps are the easiest. Then WinForms, then WPF and ASP.NET MVC are probably tied, the WebAPI. If you start at ASP.NET MVC, for example, you should know C#, advanced OOP, the MVC UI pattern, HTML, CSS, JavaScript, bundling, NuGet, npm, Bootstrap, and more. If that looks overwhelming it is because it is when you don't know much about any of those. Once you get a few under your belt, adding one more isn't too hard. If you move up the difficulty ladder, that's what you do. If you skip to the end, you can get swamped (or you just write poor code in all of them and struggle a lot).
The second reason why WinForm apps are valuable is because they are the application type of business. I know web apps are really powerful but businesses us a ton of desktop applications. Desktop apps are more powerful, quicker, and easier to develop. Think about your PC for a minute. How many applications do you have installed (Visual Studio, Office, Chrome, Notepad, etc.) Those are all desktop apps. Those aren't getting replaced anytime soon.
@@IAmTimCorey Thanks for your answer! Now, I know what should i do :)
@@leeroy1986 So its now good choice to start with WPF? I know the basics of c#, html, css, a bit java and i want to do smth with it. Console apps are boring hah
In the APIHelper class, I removed the following lines from my GetLoggedInUserInfo method, as it is being done in the initialize method, and it works just fine. Is there a reason that we had to repeat it in the method, or a future video has taken care of it ..?
apiClient.DefaultRequestHeaders.Accept.Clear();
apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Will you ever make an android version of this app? If not, then how would I call the API and use it in Flutter? I'm planning to use Flutter for the mobile version because it's cross platform and has great performance. Will be really helpful if you replied!
Yes, I plan on developing an app along with this project at some point. As for calling the API using Flutter, that's all on the Flutter side. You just need to log in first (the same way our WPF app does) and then attach that bearer token to the header on subsequent calls (like the WPF app does).
@@IAmTimCorey Thanks for the blazingly fast reply! Can't wait for the next video!
make please same for Xamarin forms !
In trying to understand the Button events, I cannot understand how the CanAddToCart is hooked into the AddToCart button. This surfaced when I noticed that the buttons are disabled. But if I return 'true' from CanAddToCart, the AddToCart button is enabled.
This came about as I was trying to find a typo where the AddToCart button was becoming enabled in a future session. Upon reverting my code and reworking this lesson I am now in sync with the expected display of the SalesViewModel. However, when I toggle the return from CanAddToCart the AddToCart button enables and disables appropriately.
I'm just not seeing how that property is wired to that specific button.
On a side note, As a Patreon supporter, should I address this question to you from there?
This video, somewhere around the time code it starts on, should explain it: th-cam.com/video/laPFq3Fhs8k/w-d-xo.html
hi tim so in my shellview i made a menu when i click on a button its shows another tab here's my code is that correct or should i use the event
public ShellViewModel(DashViewModel dashvm, IncidentViewModel InciVM)
{
_dashvm = dashvm;
_inciVM = InciVM;
ActivateItem(_dashvm);
}
public void dash()
{
ActivateItem(_dashvm);
}
public void inci()
{
ActivateItem(_inciVM);
}
The button click is technically an event. I don't see the need to have a different event (unless you need to alert the child forms that are already open).
@@IAmTimCorey thank you a llot ur a life savor
what if from Sales page i want to navigate to other page with same view model ? How this could be accomplished?
One way to do it is how we go from Login to Sales (using the event).
any chance to make Xamarin Forms in this way ?
We will be adding Xamarin Forms to this project soon.
I'm just going to drop this comment here since I'm not sure where this would go. Since we login during this video I figure it's an appropriate spot to mention something strange. When I try to login I have credentials from Windows 10 on a work computer being forced in as the username. My solution to this problem was to change the username and use that to login. Have you ever come up with any bugs like this and how would you suggest fixing something like that?
Weird. If it is happening in the browser, it may be a plugin doing it. Not sure.
@@IAmTimCorey Must have to do with my work environment. They're finally testing out Windows 10 after years of anxiously waiting. I'm willing to bet it's something the IT dept did while setting them up. It seems my way around this was to set the credentials that were getting passed in as the username in the aspnet user table.
Why not just use ActivateItem(IoC.Get()); ?
I like the separation of putting it in a variable first. It makes debugging easier.
Okay, i get it, thanks!
Is it still useful to learn WPF in 2019? Isn’t WPF going away?
Yes it is and no it isn't. WPF will be here for a very long time yet.
IAmTimCorey thanks for doing these tutorials. Awesome tutorials. However, some people might be turned off by the length of your videos like this one (over 40minutes). I wonder if you could do within 10 minutes video per concept instead of per whole application in your future videos. One concept at a time within 10 min is easier to digest and follow than 40-50min long one with many concepts that overwhelm the audience. Just my comment and suggestion. You are wonderful with what u doing still.
@@vacalepic6768 I disagree, maybe to get different individual concepts across this is useful but any useful content creation it would be counter productive and wouldn't flow properly. Most of these videos if broken down into smaller parts may lead to misunderstanding the greater goal for each implementation.
I have a problem with SalesView, when im clicking the button, nothing happen. Login is fine, i got 200 and all data, next events are okay (debug goes throughout the event) but nothing displaying. Even when i change ActiveItem(_salesVM) to ActiveItem(_container.GetInstance
Its even "Building", its going trought SalesViewModel but after that came back to event -> end and i still see login page :/
Im sooooooooo dumb, SalesView was a "window" not "user control", i found this accidentally..
It happens. I'm glad you figured it out.
its getting too complicated. i guess Forms are much straight forward.
Real-world applications always get complex.
I'm getting a runtime error on this line: _events.Subscribe(this); The error message is: "System.NullReferenceException: 'Object reference not set to an instance of an object.'". _events is null. This is in ShellViewModel.cs. The function is "public ShellViewModel(IEventAggregator events, SalesViewModel salesVM, SimpleContainer container)". The events parameter is coming in as null. Anybody have any idea why?
check bootstrapper.cs file => seek for protected override void Configure() method. i think u will probably find cause of ur trouble. u probably forgot to register IEventAggregator as singleton.
does ur code in Configure() method relevant to _container looks like this snippet?
_container.Singleton()
.Singleton()
.Singleton()
.Singleton();
For me I had to pick Async
try
{
ErrorMessage = "";
var result = await _apiHelper.Authenticate(UserName, Password);
// Capture more information about the user
await _apiHelper.GetLoggedInUserInfo(result.Access_Token);
await _events.PublishOnUIThreadAsync(new LogOnEvent());
}
The option you picked wasn't available
And
public async Task HandleAsync(LogOnEvent message, CancellationToken cancellationToken)
{
await ActivateItemAsync(_salesVM);
}
but it worked as expected
I think you are using a different version of Caliburn Micro than I am. I used the 3.x version in the .NET Framework section, because that was what was out. Then, when we upgraded to .NET Core, we upgraded the Caliburn Micro to 4.x as well.
That happens to me also.