C
C#13mo ago
mario12136

✅ 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
Arch Leaders
Arch Leaders13mo ago
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?
mario12136
mario1213613mo ago
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.
Arch Leaders
Arch Leaders13mo ago
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
Arch Leaders
Arch Leaders13mo ago
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.
mario12136
mario1213613mo ago
Thanks for the response, this is interesting.
Klarth
Klarth13mo ago
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.
mario12136
mario1213613mo ago
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:
[RelayCommand]
private void Cut() => _textEditor.Cut()
[RelayCommand]
private void Cut() => _textEditor.Cut()
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
<MenuItem Command={Binding ElementName=TextEditor, Path=SelectLineCommand}
<MenuItem Command={Binding ElementName=TextEditor, Path=SelectLineCommand}
is ok? In case they are both fine, is there a reason to prefer one over the other?
Klarth
Klarth13mo ago
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.
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
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
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.
mario12136
mario1213613mo ago
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
ITextEditor textEditor = new TextEditorService(MainView.TextEditor)
ITextEditor textEditor = new TextEditorService(MainView.TextEditor)
correct? Also maybe thoughts on making the MainViewModel partial and implementing different groups of related commands (File, editing, etc.) in different .cs files.
Klarth
Klarth13mo ago
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.
mario12136
mario1213613mo ago
I understand. Thank you for this great discussion. I think I know what I need to do now.
Accord
Accord13mo ago
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.