✅ Would this be wrong MVVM?
I made my own WPF "text editor" and I will just use the community toolkit's [RelayCommand] source generators to create commands to which I can bind to in my main view model. The reason they are not in the main view model is because execute and can execute for each command is highly dependent on my text editor's methods and properties, and I need to expose these commands somehow.
Was wondering how people feel about this.
12 Replies
Where are your relay commands? You first mention that you are using the relay command attribute in your main view model, and then you say that they're actually not in your main view model. Are you putting the relay commands in your code-behind?
Yeah the relay commands are in code-behind (I mean that's the text editor class anyway). I will bind to them my menu item's in my main view. Think of windows notepad.
Right, well Idk if this is right, but I would instead inject the
View
into the ViewModel
to give View
access to your relay commands in the ViewModel
I have almost this exact scenario in a project I'm working on. In that case I added the view as a generic type into an base class that constructs it with
this
as the DataContext
.Thanks for the response, this is interesting.
It's fine to handle that part in code-behind of the view. You could also create a service wrapper around the View/AvalonEdit and inject that into the VM. Then put the commands in the VM as they'd be able to access the relevant things via indirection (the service). It might be hard to get the service into the VM, so it might be necessary to do that part in code-behind.
Injecting a View into the VM is literally the biggest MVVM-breaking mistake you can make.
However, it's ok to break rules sometimes, especially if you can limit the extent. If you have to replace/recode one VM if you switch to a different GUI framework, it's not a big deal.
If you do it everywhere, then it is a big deal.
Thank you. This is what I understood
1) Create an interface lets call it
ITextEditor
.
2) Make my text editor control implement it
3) Make my MainViewModel
take in as a parameter an object of type ITextEditor
4) Pass the TextEditor
control into the MainViewModel
5) Create my editing commands in the MainViewModel
calling in the object that I passed.
Example:
where _textEditor
is of type ITextEditor
and the implementation is the actual text editor.
Please correct me if I am wrong. And also just to clarify, you think the other approach, which is making the commands in the text editor and binding to them like
is ok? In case they are both fine, is there a reason to prefer one over the other?I wouldn't create a control. I would create a service class that takes your View/AvalonEdit as a parameter. When you call
_textEditor.Cut()
or whatever, the service directly accesses the AvalonEdit control. The rest is correct though.
You mean in the code behind? If so, that's fine. I prefer that being in the VM if possible though. Less sprawl and less stuff happening that the VM doesn't know about.Yes, the code-behind (regarding the last message). Just one more question if you don't mind. Where would I be passing the AvalonEdit as an argument. If I am not mistaken, since I set up things in the
App.xaml.cs
, creating the MainView
and the MainViewModel
, I would do something like
correct?
Also maybe thoughts on making the MainViewModel
partial and implementing different groups of related commands (File, editing, etc.) in different .cs
files.If you only have one instance and it lives for the lifetime of the app, it's probably fine to do that in App.xaml.cs. Otherwise, you'd probably do it in code-behind of the specific view and do parameter injection into the VM.
I don't do that, but my VMs don't really exceed 800-1000 LoC either. So far, I've been able to break down into more VMs or offload code into services.
I have split controls before. Separate source file for dependency properties. Otherwise, I don't really do it.
I understand. Thank you for this great discussion. I think I know what I need to do now.
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.