I'm trying to follow the MVVM pattern in a WPF application as good as I can, mainly to be able to create unit tests for my ViewModel logic.
In most cases data binding between ViewModel properties and properties of visual elements works fine and is easy. But sometimes I encounter situations where I cannot see an obvious and straightforward way while a solution to access and manipulate controls from code-behind is very easy.
Here is an example of what I mean: Inserting a text fragment into a TextBox
at the current caret position
Since CaretIndex
isn't a dependency property it can't be bound directly to a ViewModel's property. Here is a solution to work around this limitation by creating a dependency property. And here is the solution to do this in code-behind. I would prefer the code-behind way in this situation. Another problem I recently had was binding a dynamic collection of columns to a WPF datagrid. It was clear and simple to program in code-behind. But for a MVVM-friendly databinding approach I could only find work arounds in several blogs which all looked quite complex to me and had various limitations in one or the other aspect.
I don't want to keep the MVVM architecture clean of code-behind logic at all costs. If the amount of work arounds is too big, a MVVM-friendly solution requires a lot of code which I don't fully understand (I'm still a WPF beginner) and is too time consuming I prefer a code-behind solution and sacrifice automatic testability of a few parts of my application.
For the mentioned pragmatic reasons I am looking now for "patterns" to make controlled use of code-behind in an application without breaking the MVVM architecture or without breaking it too much.
Up to now I've found and tested two solutions. I will draw rough sketches with the Caret Position example:
Solution 1) Give the ViewModel a reference to the View through an abstract interface
I would have an interface with methods which would be implemented by the view:
public interface IView
{
void InsertTextAtCaretPosition(string text);
}
public partial class View : UserControl, IView
{
public View()
{
InitializeComponent();
}
// Interface implementation
public void InsertTextAtCaretPosition(string text)
{
MyTextBox.Text = MyTextBox.Text.Insert(MyTextBox.CaretIndex, text);
}
}
Inject this interface into the ViewModel
public class ViewModel : ViewModelBase
{
private readonly IView _view;
public ViewModel(IView view)
{
_view = view;
}
}
Execute code-behind from a ViewModel's command handler through the interface methods
public ICommand InsertCommand { get; private set; }
// Bound for instance to a button command
// Command handler
private void InsertText(string text)
{
_view.InsertTextAtCaretPosition(text);
}
To create a View-ViewModel pair I would use dependency injection to instantiate the concrete View and inject it into the ViewModel.
Solution 2) Execute code-behind methods through events
The ViewModel is publisher of special events and command handlers raise those events
public class ViewModel : ViewModelBase
{
public ViewModel()
{
}
public event InsertTextEventHandler InsertTextEvent;
// Command handler
private void InsertText(string text)
{
InsertTextEventHandler handler = InsertTextEvent;
if (handler != null)
handler(this, new InsertTextEventArgs(text));
}
}
The View subscribes to these events
public partial class View : UserControl
{
public View()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ViewModel viewModel = DataContext as ViewModel;
if (viewModel != null)
viewModel.InsertTextEvent += OnInsertTextEvent;
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
ViewModel viewModel = DataContext as ViewModel;
if (viewModel != null)
viewModel.InsertTextEvent -= OnInsertTextEvent;
}
private void OnInsertTextEvent(object sender, InsertTextEventArgs e)
{
MyTextBox.Text = MyTextBox.Text.Insert(MyTextBox.CaretIndex, e.Text);
}
}
I am not sure if the Loaded
and Unloaded
events of the UserControl
are good places to subscribe and unsubscribe to the events but I couldn't find problems during test.
I have tested both approaches in two simple examples and they both seem to work. Now my questions are:
Which approach do you think is preferable? Are there any benefits or downsides of one of the solutions which I possibly don't see?
Do you see (and perhaps practice) other solutions?
Thank you for feedback in advance!
Many of the other UWP app samples also use a basic MVVM architecture, and the Traffic App sample includes both code-behind and MVVM versions, with notes describing the MVVM conversion.
In Android, MVC refers to the default pattern where an Activity acts as a controller and XML files are views. MVVM treats both Activity classes and XML files as views, and ViewModel classes are where you write your business logic. It completely separates an app's UI from its logic.
The model implements your business logic. The view model decorates your business logic for the purpose of displaying it and interacting with it, in a view (UI of some form, eg. web, winform, CLI).
For trivial projects MVVM is unnecessary. Using only the View is sufficient. For simple projects, the ViewModel/Model split may be unnecessary, and just using a Model and a View is good enough. Model and ViewModel do not need to exist from the start and can be introduced when they are needed.
Specifically for this problem
The simplest solution to this specific case is adding an attached property that does it or a behavior. Behaviors can be a silver bullet for most of these rich-gui-not-supported cases in mvvm.
As for the general case
ViewModel should never ever under any circumstance know about the view, and not even about an IView. in MVVM its "always look up", which means a View can look @ the VM, and the VM can look at the Model. never the other way around. This creates much better maintainability, since this way the ViewModel doesn't do two things (incharge of logic AND the gui), but only one thing. This is where MVVM is superior to any prior MV* pattern.
I would also try to refrain from having the View rely on the ViewModel in a coupled way. this creates ugly code, and a breakable dependency between the two classes, but sometimes this is more pragmatic as you said. A prettier way is to send a Loose Message (e.g. Messenger in MVVMLight, or EventAggregator in Prism) from the ViewModel to the View, and thus there is no strong dependency between the two. Some think this is better although IMO this is still a dependency.
Writing code in the View is OK in some situations, and this could be one of those situation. You could achieve a perfect solution using attached-behaviors, but the priniciple is important, like you asked.
MVVM is problematic when you need GUI that is very rich or the UI doens't have the right Properties to bind to. In those situations you would resort to one of three things:
All of those ways are legitimate, but I've ordered them according to what you should resort to first.
To Summarize
The most important thing you have to keep in MVVM is not to keep the codebehind free, but to keep all logic & data to the ViewModel, where as the View must only contain View-related code. The reason architects tell you not to write code behind at all is only because it's a slippery slope. You start writing something small, and you end up doing logical stuff or maintaining application state in the View, which is the big no-no.
Happy MVVMing :)
Developing WPF applications I found both ways useful. If you need just one call from ViewModel to View, the second option, with event handler, looks simpler and good enough. But if you are requiring more complex interface between these layers, then it makes sense to introduce interface.
And my personal preference is to revert your option one and have a IViewAware interface implemented by my ViewModel and inject this ViewModel into View. Looks like an option three.
public interface IViewAware
{
void ViewActivated();
void ViewDeactivated();
event Action CloseView;
}
public class TaskViewModel : ViewModelBase, IViewAware
{
private void FireCloseRequest()
{
var handler = CloseView;
if (handler != null)
handler();
}
#region Implementation of IViewAware
public void ViewActivated()
{
// Do something
}
public void ViewDeactivated()
{
// Do something
}
public event Action CloseView;
#endregion
}
And this a simplified code for your View:
public View(IViewAware viewModel) : this()
{
_viewModel = viewModel;
DataContext = viewModel;
Loaded += ViewLoaded;
}
void ViewLoaded(object sender, RoutedEventArgs e)
{
Activated += (o, v) => _viewModel.ViewActivated();
Deactivated += (o, v) => _viewModel.ViewDeactivated();
_viewModel.CloseView += Close;
}
In real application I usually use an external logic to connect V and VM, for example Attached Behaviors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With