WPF best practices
-
So, I'm making an app using WPF. I try to adhere to the MVVM model, but I find it increasingly difficult with every line. Some common things seem outright impossible to do. And none of the tutorials I've seen addresses any of these design problems I have - which I find weird considering it's very basic stuff.
-
Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
-
I have a button that should spawn a dialog window. The dialog's purpose is to ask the user about some data needed in main window. Who creates the dialog, how do I pass a parent window to it (because I need a parent window for proper dialog placement), and how do I return the data back? It's all trivial until you remember ViewModel doesn't know about the window it belongs to.
-
Where's the boundary between Model and ViewModel? How strict the boundary should be? What should the interface between ViewModel and Model look like? Does every app need Model objects separate from ViewModel objects? Asking because I've made a couple small apps in WPF and contained most of my logic in ViewModels, and the rest in static methods.
-
-
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
-
@rhywden said in WPF best practices:
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
Only if you've purchased their WPF controls.
-
@parody said in WPF best practices:
Only if you've purchased their WPF controls.
Surprisingly, it seems no one wants to talk about your WPF best practices or my CRUD form design patterns during New Years Eve.
Seriously, what's wrong with people? It's not like there is anything better to do.
-
-
@rhywden said in WPF best practices:
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
You mean, I should create a command that accepts arbitrary Window as parameter, and calls Close() on this arbitrary window? It doesn't sound right. Is it how it's done in professional apps? Also, there's another action that does the important work and also closes the window. Should it receive window as parameger too? This would mean that VM has access to window basically all the time.
@parody said in WPF best practices:
@rhywden said in WPF best practices:
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
https://documentation.devexpress.com/WPF/17353/MVVM-Framework/Commands/Delegate-Commands
Only if you've purchased their WPF controls.
Don't worry, Prism has the exact same thing absolutely for free.
-
@parody said in WPF best practices:
Only if you've purchased their WPF controls.
Wah? That was an example. DelegateCommands (or rather,
ICommand
) are pretty much a stock of MVVM in C#.
-
@gąska said in WPF best practices:
@rhywden said in WPF best practices:
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
You mean, I should create a command that accepts arbitrary Window as parameter, and calls Close() on this arbitrary window? It doesn't sound right. Is it how it's done in professional apps? Also, there's another action that does the important work and also closes the window. Should it receive window as parameger too? This would mean that VM has access to window basically all the time.
I'm not sure why you think that your ViewModel isn't supposed to know about the UI. I'm using Prism and my ViewModel has access to all objects in its corresponding View through its DataContext.
-
@rhywden said in WPF best practices:
@parody said in WPF best practices:
Only if you've purchased their WPF controls.
Wah? That was an example. DelegateCommands (or rather,
ICommand
) are pretty much a stock of MVVM in C#.Then why not link to MSDN somewhere rather than something proprietary? Your response looked to me like a bad search-and-paste.
Unofrtunately, my experience is with WinForms (including a little with the DevExpress WinForms controls) so I don't have good advice for the actual questions.
-
@gąska said in WPF best practices:
It doesn't sound right. Is it how it's done in professional apps?
Only the more competently-written ones.
-
There are ways to handle all of them [I agree many are not obvious].
One (not only) technique is for the VM to have an event. When the V registers for the event and takes the action. Thus the VM knows nothing about the V, but the V knows what the VM "wants"
-
@parody DelegateCommand isn't part of standard WPF library. It's a piece of code that gets copy-pasted everywhere (sometimes named RelayCommand).
-
@dkf said in WPF best practices:
@gąska said in WPF best practices:
It doesn't sound right. Is it how it's done in professional apps?
Only the more competently-written ones.
I'm afraid to ask how it's done in less competently-written ones.
-
@thecpuwizard said in WPF best practices:
There are ways to handle all of them [I agree many are not obvious].
One (not only) technique is for the VM to have an event. When the V registers for the event and takes the action. Thus the VM knows nothing about the V, but the V knows what the VM "wants"
The other idea would be to register the Window instance in the IOC container and get the reference from there anytime you need it. Subscribing to an event only really works if the object is "live".
-
@rhywden said in WPF best practices:
The other idea would be to register the Window instance in the IOC container and get the reference from there anytime you need it.
If the View Model accesses the View (Window Instance) - even via an IOC container, then the pattern is broken [you are not doing MVVM].
The View, always knows about the View Model that it is attached to. It even gets notifications on Change. So it is 100% reliable (and easy) to have the View attach to an event in the View Model.
If necessary, the View Model can determine if "something" is listening, but is completely oblivious (and this is good) as to who/what it is.
-
@thecpuwizard said in WPF best practices:
@rhywden said in WPF best practices:
The other idea would be to register the Window instance in the IOC container and get the reference from there anytime you need it.
If the View Model accesses the View (Window Instance) - even via an IOC container, then the pattern is broken [you are not doing MVVM].
The View, always knows about the View Model that it is attached to. It even gets notifications on Change. So it is 100% reliable (and easy) to have the View attach to an event in the View Model.
If necessary, the View Model can determine if "something" is listening, but is completely oblivious (and this is good) as to who/what it is.
Then you access the ViewModel attached to the Window. Geeze. Because letting the View listen to events from another ViewModel would also be against the pattern...
... but you may want to tell your objections to the authors of the Prism framework, by the way.
-
@thecpuwizard The further I think about this, the less I like "View listens to broadcast events".
Isn't the point of MVVM to separate concerns? I.e. the View is only responsible for displaying the state and the ViewModel is the sole custodian of said state (i.e. business logic). Letting the View listen to events means that it's suddenly also responsible for the state again.
-
@rhywden said in WPF best practices:
@thecpuwizard The further I think about this, the less I like "View listens to broadcast events".
Isn't the point of MVVM to separate concerns? I.e. the View is only responsible for displaying the state and the ViewModel is the sole custodian of said state (i.e. business logic). Letting the View listen to events means that it's suddenly also responsible for the state again.
I hear that concern pretty often, but I look at it as the View is ALWAYS listening to the View Model... What do you think Dependency Properties, or INotifiyChange or .... are?
-
@gąska said in WPF best practices:
@dkf said in WPF best practices:
@gąska said in WPF best practices:
It doesn't sound right. Is it how it's done in professional apps?
Only the more competently-written ones.
I'm afraid to ask how it's done in less competently-written ones.
A common antipattern is to make one gigantic handler that deals with all possible events coming in (with no delegation to components or anything sensible like that). I think it tends to come about because that's how the absolutely base level of GUIs works, but that's not really how anyone sane actually does things (they layer some sort of dispatch delegation mechanism on top to make the systems that you normally see so that you get automatic separation by component and type of event).
-
@dkf said in WPF best practices:
A VERY common antipattern
Fixed!!!!
Seriously, it is out of control (pun intended) in so many UI's. People think (sometimes) about factoring their code, but rarely their UI.
Properly designing a UI seems so rare....
-
@gąska said in WPF best practices:
@rhywden said in WPF best practices:
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
You mean, I should create a command that accepts arbitrary Window as parameter, and calls Close() on this arbitrary window? It doesn't sound right. Is it how it's done in professional apps? Also, there's another action that does the important work and also closes the window. Should it receive window as parameger too? This would mean that VM has access to window basically all the time.
@parody said in WPF best practices:
@rhywden said in WPF best practices:
@gąska said in WPF best practices:
- Since ViewModel isn't supposed to know about window, and all actions should be mapped to ViewModel commands... How do you implement "Close" button?
With DelegateCommands?
https://documentation.devexpress.com/WPF/17353/MVVM-Framework/Commands/Delegate-Commands
Only if you've purchased their WPF controls.
Don't worry, Prism has the exact same thing absolutely for free.
You should create a service for doing stuff with the window and inject it via interface to the view model’s constructor. The same goes for message boxes and other shit.
Basically, the idea is that you want to be able to mock all of the UI shit when testing. If you simply use the instance of a window, you will fail.
Also, for purely UI-related stuff (animation and shit) don’t be afraid to use code behind and event to command pattern.
-
@rhywden said in WPF best practices:
@thecpuwizard The further I think about this, the less I like "View listens to broadcast events".
Isn't the point of MVVM to separate concerns? I.e. the View is only responsible for displaying the state and the ViewModel is the sole custodian of said state (i.e. business logic). Letting the View listen to events means that it's suddenly also responsible for the state again.
It already does listen to PropertyChanged. Sure, it would be better not to do that, but if you have to, you can go for it.
-
@rhywden the problem with "view doesn't manage state, only displays it" is that unless you're doing IMGUI (which only games do), the displaying itself is very stateful. For example, whether a window is open or not is a very important part of state and it's strongly tied to WPF Window class and object's lifetime.
My personal approach to MVVM is that ViewModel all the data that has to be displayed in a form that's easy to display, and View can do whatever it wants with ViewModel, but it cannot interact with anything except ViewModel.
-
@gąska said in WPF best practices:
@rhywden the problem with "view doesn't manage state, only displays it" is that unless you're doing IMGUI (which only games do), the displaying itself is very stateful. For example, whether a window is open or not is a very important part of state and it's strongly tied to WPF Window class and object's lifetime.
Then change a property on the view model when strictly UI-related stuff changes. You can communicate state back to view model.
My personal approach to MVVM is that ViewModel all the data that has to be displayed in a form that's easy to display, and View can do whatever it wants with ViewModel, but it cannot interact with anything except ViewModel.
Yeah, basically.
-
@thecpuwizard said in WPF best practices:
@rhywden said in WPF best practices:
@thecpuwizard The further I think about this, the less I like "View listens to broadcast events".
Isn't the point of MVVM to separate concerns? I.e. the View is only responsible for displaying the state and the ViewModel is the sole custodian of said state (i.e. business logic). Letting the View listen to events means that it's suddenly also responsible for the state again.
I hear that concern pretty often, but I look at it as the View is ALWAYS listening to the View Model... What do you think Dependency Properties, or INotifiyChange or .... are?
Yes, to its own ViewModel. Not to other ViewModels.
-
@gąska said in WPF best practices:
@parody DelegateCommand isn't part of standard WPF library. It's a piece of code that gets copy-pasted everywhere (sometimes named RelayCommand).
Ahh. As you can tell, I saw DevExpress and jumped to proprietary. Now I know!
It'd be nice to learn stuff from this decade one of these days. :/
-
@parody said in WPF best practices:
@gąska said in WPF best practices:
@parody DelegateCommand isn't part of standard WPF library. It's a piece of code that gets copy-pasted everywhere (sometimes named RelayCommand).
Ahh. As you can tell, I saw DevExpress and jumped to proprietary. Now I know!
It'd be nice to learn stuff from this decade one of these days. :/
Well, I hope you’re not taling about WPF, then. It’s sooooo last decade.
-
@kt_ said in WPF best practices:
Well, I hope you’re not taling about WPF, then. It’s sooooo last decade.
Technically, yes - but even if you're willing to drop Windows 7 support and go for a "Universal" app instead, I thought that all of the
fiddleyfun MVVM bits are the same in both?
-
@kt_ said in WPF best practices:
@parody said in WPF best practices:
@gąska said in WPF best practices:
@parody DelegateCommand isn't part of standard WPF library. It's a piece of code that gets copy-pasted everywhere (sometimes named RelayCommand).
Ahh. As you can tell, I saw DevExpress and jumped to proprietary. Now I know!
It'd be nice to learn stuff from this decade one of these days. :/
Well, I hope you’re not taling about WPF, then. It’s sooooo last decade.
My current side project has me writing (pure) JavaScript. I hear it's a cool new thing everyone uses nowadays.
-
@gąska said in WPF best practices:
WPF best practices
I misread this as "WTF best practices", and now I'm disappointed. :-(
-
@rhywden said in WPF best practices:
Yes, to its own ViewModel. Not to other ViewModels.
A ViewModel often has Properties that are of a type that is a View Model!
Consider an InvoiceCollection View Model, I has an IObservable<InvoiceViewModel> Property, which in turn has a InvoiceLineItemViewModel Property.
This is the exact same implementation topology.
-
@thecpuwizard I'm not sure why you're doing this Matryoshka Doll style stacking?
Also, in your example, it's the ViewModel through which other ViewModels are accessed! You said that the View is supposed to listen events.
-
@rhywden said in WPF best practices:
I'm not sure why you're doing this Matryoshka Doll style stacking?
Seems like there are multiple levels to this discussion. Let's start with the lowest, which is reference in your quoted text..
Proper Factoring and SRP [Single Responsibility Principle].
-
An InvoiceLineItemView may be usable in multiple parts of an application. It would be bound (always) to a InvoiceLineItemViewModel
-
An InvoiceView would contrain a "Repeater" (or similar) with each element rendering a InvoiceLineItemView. The InvoiceView is bound to an InvoiceViewModel. There may also be multiple places in an application where an Invoice is Shown...
-
An InvoiceCollectionView (such as an Order History)....
Are we on the same page [alliteration intended] so far?
-
-
@thecpuwizard This is dodging the issue where you said one thing ("View listens directly to broadcast events!") when you just advocated for message passing through the ViewModel.
-
@rhywden said in WPF best practices:
@thecpuwizard This is dodging the issue where you said one thing ("View listens directly to broadcast events!") when you just advocated for message passing through the ViewModel.
Please read again "seems like there are multiple levels to this discussion. Let's start with the lowest, which is reference in your quoted text.."
Without addressing the lowest levels and ensuring common understanding, it is impossible to have a meaningful discussion of the higher levels....
-
This thread isn't recent, but is relevant:
https://what.thedailywtf.com/topic/16660/the-trick-to-mvvm
I did weird navigation things there, that you might be better off ignoring. But it may help, and I'd say it's simpler than PRISM while still giving you all the MVVM things you need.
-
@rhywden in theory, ViewModel should not depend on View at all (but not the other way around; View beiing strongly tied to ViewModel is OK). In practice, there are times where ViewModel needs to tell View to do something specific. The workaround is to make all the calls to View into events; that way, we can pretend it's still independent from View, and testing is still easy without need for mock view.
-
mocking: n. What should be done, quite thoroughly and derisively, to people who advocate turning simple, straightforward source code into a mass of complexity and layers and "decoupled" pasta, in order to make it easier to run automated tests against.
-
@masonwheeler said in WPF best practices:
in order to make it easier to run automated tests against.
Except that what is being discussed here is all about (or at least very largely about) making things EASIER to do automated testing.
-
@thecpuwizard ...which is what I just said, or is there some distinction I'm missing?
-
@masonwheeler said in WPF best practices:
@thecpuwizard ...which is what I just said, or is there some distinction I'm missing?
Perhaps I misread/misunderstood.... "turning simple, straightforward source code into a mass of complexity and layers and "decoupled" pasta,"
Which category do you see the separation I have been talking about into distinct Views and Models for each element..
a) An increase in testability or.
b) Turning two simple, straightforward source code files into a pass of complexity and layers and "decoupled" pasta
-
@thecpuwizard That was mostly a bit of general snark about how automated-testing fanatics always have a zillion ways you have to turn your code inside out in order to make it "properly testable" that invariably end up making it harder to read, harder to debug, and less useful to automated tooling.
-
Anyway, the essence of my advice, which you could probably glean from the thread I posted is this:
- Get a DelegateCommand. The one I posted should be fine. Or you can just wing it. It's easy, and makes everything much easier.
- Inject everything. Preferably the MEF way, where you don't think about it, and where every class can be used just as easily manually.
If you stick to those two things, everything will be fine. I also recommend that your views have no C# code in them aside from viewmodel setup. There are incredibly rare cases where this isn't enough, but I would venture to say that you might need a custom control for those cases. Which means they're probably rarer than you think I mean.
-
@magus said in WPF best practices:
I also recommend that your views have no C# code in them aside from viewmodel setup. There are incredibly rare cases where this isn't enough, but I would venture to say that you might need a custom control for those cases. Which means they're probably rarer than you think I mean.
I generally agree...provided you mean "visible"/"handwritten" code..... For fun, create a view, compile, use reflector...there will be code :) :) :)
Some fun decisions when it comes to "Custom Control", "User Control" and "Partial View"....
-
@doctorjones said in WPF best practices:
@gąska said in WPF best practices:
WPF best practices
I misread this as "WTF best practices", and now I'm disappointed. :-(
Well, even if it doesn’t say that, it is what this thread is about.
-
@gąska said in WPF best practices:
@rhywden in theory, ViewModel should not depend on View at all (but not the other way around; View beiing strongly tied to ViewModel is OK). In practice, there are times where ViewModel needs to tell View to do something specific.
Like what?
The workaround is to make all the calls to View into events; that way, we can pretend it's still independent from View, and testing is still easy without need for mock view.
Well, actually the trick is to use properties for that. And then it’s even easier to test that.
Basically, you use an event in order to tell the view “this happened”. You use property change (which triggers a property changed event) to tell the view “we’re in this state, ie this happened”.
-
@thecpuwizard said in WPF best practices:
@magus said in WPF best practices:
I also recommend that your views have no C# code in them aside from viewmodel setup. There are incredibly rare cases where this isn't enough, but I would venture to say that you might need a custom control for those cases. Which means they're probably rarer than you think I mean.
I generally agree...
I generally do, too. Unless you start doing DevExpress. Their controls quite often require to do some wiring in code behind for a good reason: it’s control (ie implementation)-dependent and view model should know nothing about that.
Also, conditional styling, animations, etc.
-
@kt_ said in WPF best practices:
@gąska said in WPF best practices:
@rhywden in theory, ViewModel should not depend on View at all (but not the other way around; View beiing strongly tied to ViewModel is OK). In practice, there are times where ViewModel needs to tell View to do something specific.
Like what?
Close.
-
@gąska said in WPF best practices:
@kt_ said in WPF best practices:
@gąska said in WPF best practices:
@rhywden in theory, ViewModel should not depend on View at all (but not the other way around; View beiing strongly tied to ViewModel is OK). In practice, there are times where ViewModel needs to tell View to do something specific.
Like what?
Close.
Yeah, this would be best packed into an event.
-
@kt_ said in WPF best practices:
Yeah, this would be best packed into an event.
And after a bit of a detour (that had some very good points)...back to the topic at hand.