No. In WPF and other XAML-based languages, a view's backing code-behind should try never to directly access view model members or really have any dependency on view model specifics at all whenever possible. In WPF and its progeny, a view model should generally be a DataContext
for a UserControl
or the content of a ContentControl
. The only direct connections between the view and view model should be bindings (ICommand
s and other public properties). Code in the view layer - be it the code-behind of a UserControl
or of a subclassed Control
- should only access other UI elements whenever possible.
This style of writing UIs was one of the original fundamental benefits of MVVM and XAML - a nearly entirely declarative UI definition that replaced the old system of event handlers and procedural logic. While separating concerns is the ultimate motivator, limiting the connections to declarative bindings was and is the preferred means by which this is achieved in WPF and other XAML-based platforms.
There are many sources from the 2000s and 2010s supporting this view.
From Microsoft:
The View in Model/View/ViewModel consists of the visual elements, the buttons, windows, graphics and more complex controls of a GUI.... The View is almost always defined declaratively, very often with a tool.
John Gossman - Microsoft - Oct. 2005.
Every view in the [example MVVM] app has an empty codebehind file, except for the standard boilerplate code that calls InitializeComponent in the class's constructor. In fact, you could remove the views' codebehind files from the project and the application would still compile and run correctly.
In a well-designed MVVM architecture, the code behind for most Views should be empty, or, at most, only contain code that manipulates the controls and resources contained within that view. Sometimes it is also necessary to write code in a View's codebehind that interacts with a ViewModel object,such as hooking an event or calling a method that would otherwise be very difficult to invoke from the ViewModel itself.
Josh Smith - Microsoft - Feb. 2009
(Note how Smith acknowledges accessing the view model from code-behind may be necessary but should be an exception for the "very difficult" cases. With more advanced MVVM libraries that bring us things like Behaviors, and the attached command pattern, the situations where direct view model access from view code behind is necessary are now few and far between.)
From Stack Overflow:
The Model-View-ViewModel (MVVM) design pattern aims at achieving exactly that goal [of eliminating code behind].
WPF Commands - Doing it with no code-behind (accepted answer 10/9/2009 by Mark Seemann (230k) - 12 upvotes as of 7/7/2024
Having a code-behind file which consists solely of a call to InitializeComponent() in its constructor means you have achieved purity - you have absolutely zero logic in your codebehind. You have not polluted your view with any code that rightfully belongs in the viewmodel or model.
Why to avoid the codebehind in WPF MVVM pattern? (accepted answer 6/21/2011 - 91 upvotes, 1 downvote as of 6/19/2024).
The view's code-behind may define UI logic to implement visual behavior that is difficult to express in XAML or that requires direct references to the specific UI controls defined in the view.
Basic concepts of MVVM-- what should a ViewModel do? (accepted answer 3/24/2011 - 193 upvotes, 0 downvotes as of 6/19/2024).
In MVVM you shouldn't be accessing your view model from code behind, the view model and view are ignorant of each other....
How can I access my ViewModel from code behind (highest voted answer, 7/20/2014 - 39 upvotes, 1 downvote as of 6/19/2024).
Why this makes sense
- It tightens and minimizes connection points between the layers, making for a cleaner and more easily debugged codebase
- It enables a developer to work on the view layer with minimal C#/procedural coding experience, if any
- It makes changing either the view or view model easier since
ICommand
is universal, whereas if a view invokes a view model method, changing the view model's method signature requires also refactoring the view.
- It improves portability to other platforms, given that XAML is generally easier to port among WPF/WinUI/MAUI than view code behind, and view model code should always be 100% portable, ideally in an assembly referencing only .NET Standard or the "pure" .NET SDK (e.g., "net8.0" and not "net8.0-windows"
- When used with Behaviors or attached commands, the practice allows for more logic to be handled by the abstract, cross-platform view model than the platform-dependent view
The last point is where some people here will object and argue this is a bad practice because view models shouldn't get involved in UI logic. But I say this is nonsense. View models are by definition a platform-agnostic abstraction of the UI. They are to the UI what a script is to a finished movie, or a 2-staff piano score and chord sheet is to the fully orchestrated final arrangement. Of course the view model has a "role" in the UI.
If there is application-specific abstract UI logic that would apply universally on any conceivable GUI platform irrespective of design specifics, and provided this can be done without making the view model dependent on platform-specific types, then there is no reason not to put that logic in the platform-agnostic view model since it is by definition abstract. By contrast, general UI logic that would apply to any application using similar controls or design patterns should be considered for a platform-specific application-agnostic helper library.
But view code-behind (which is both application- and platform-specific) is not suitable for anything other than the rare case of procedural code that is specific to both the application and the platform which can't be expressed declaratively without undue effort. Again, modern MVVM toolkits make this situation quite rare.
If you think of all components of your application in terms of how much they cost, this becomes even more obvious. Code cost is a function of the actual time it takes the developer to write, the required expertise, and the ability to reuse the work (reusable code "costs" less here). Accordingly:
The "cheapest" code to write is code that's platform agnostic and application agnostic (e.g. helper libraries). It requires no knowledge of any specific UI platform or of the specific application, can be reused across all platforms and applications, and new team members can generally work on it right away.
In the middle is the view model - code that's application-specific but platform-agnostic. The developer needs knowledge of the application but still needs no special expertise in, e.g., WPF. Moreover, while it may have little use in other applications, it can when implemented well be reused without modification in other .NET platforms like MAUI when and if the time comes to port, or if a multi-platform solution is needed.
View code-behind is among the most expensive code you can write. It's application-specific and platform-specific, so it has the least reusability potential. It requires both knowledge of the application and experience in two languages (typically C# and XAML) and the specific GUI platform, so it requires the most experienced coders. If you agree that maximizing value and minimizing cost are worthy goals, then it would be irrational not to want to minimize the amount of effort being spent at this level.
Where others get it wrong
While the prevailing view was (and I'm sure still is, in real world circles anyway) overwhelmingly against using code-behind to access view models, for some reason the concept is now controversial on Stack Overflow and consistently challenged by a small but very vocal minority of users here. Here's what some of the detractors get wrong:
Myth MVVM is just a pattern for separating UI code from application logic. It's only about separation and doesn't care about the specifics.
Reality MVVM isn't just about separating concerns. Separating concerns is the goal, but there are plenty of patterns that promote separation of concerns that are not MVVM. MVVM is a specific type of pattern, heavily tied to the concept of a declaratively-defined UI that was first introduced with WPF/XAML and its binding system. If you use code-behind to manipulate view models without a care, you are throwing away the most important tool XAML-based platforms give you toward achieving the ideal level of separation, and thus ultimately making life harder for yourself.
Myth MVVM can't care about code-behind because it's language agnostic. It doesn't care whether you use C#/JavaScript/XAML/HTML to define your UI.
Reality This is a specious argument. It's not a question of language vs. language, it's a question of whether it's better to implement UI logic procedurally or declaratively. It wouldn't matter if it were Javascript vs. HTML, C++ vs. XML, or C# vs. XAML. In each case the question is how much of the UI definition should be done declaratively instead of procedurally. The answer MVVM gives is: as much as possible.
Myth All WPF controls use code-behind, so it can't be bad.
Reality WPF controls do not use code-behind as that term is commonly used. Code-behind usually means a .xaml.cs
file subclassing UserControl
, Window
, or Application
, associated with a like-named .xaml
containing event handler subscriptions and named elements, with the .xaml.cs
file containing the actual handlers and usually referencing the named elements as private
members. The result is code that is both platform- and application-dependent, and a UI defined with a mix of declarative markup and procedural code.
By contrast, almost anything that derives from Control
utilizes a ControlTemplate
in combination with a backing class. The backing class for a control is not the same as the code-behind of a UserControl
. If anything it's more analogous to a view model, with the ControlTemplate
serving as the view and the backing class serving as an abstraction of the control's functionality, and the two connecting via TemplateBinding
. Control templates never include event subscriptions the way UserControl
definitions do; GetTemplateChild
must be explicitly called, and then named elements can be manipulated directly or subscribed to, which is closer in principle to code behind (and just as inelegant and generally avoided when possible, as it ties the control to specific templates with specific named elements).
Most importantly, both the backing class for a lookless Control
and its ControlTemplate
, though platform-specific, are general purpose and application-agnostic. This makes them fundamentally different from view code behind which is both application- and platform-specific. Justifying view code-behind by pointing to Control
backing classes is thus comparing apples and oranges.
Myth Using view code-behind to manipulate the view model is preferable to putting that same logic in the view model itself (e.g. via a Behavior or attached command), because UI logic never belongs in the view model.
Reality The question of whether abstract UI logic - assuming it is independent from any platform-specific types or other code - in principle "belongs" or "doesn't belong" in the view model is a philosophical distraction.
When someone tells you something should or shouldn't be handled in the view model, often they are the only ones who know what their personal criteria are for making that determination. This approach won't help you the next time a close question arises. Some users on this site nonetheless serially answer these kinds of questions according to their personal taste, the justification usually being only that "MVVM [or, more accurately, 'I'] say(s) so". Unlike the guidance cited above, these one-off answers, to say nothing of unsolicited off-topic comments to unrelated questions, rarely help the reader understand the reason for and the benefit to following the advice given, let alone make the advice right.
The most important, if not only relevant question is, what is the best way to implement my application consistent with my goals? If you agree with the bullet points above ("Why this makes sense") and the goals of MVVM generally, and you understand how all the tools in front of you (view models, XAML, bindings, Behaviors/attached commands, and occasional code-behind when needed) relate to those goals, then the answer to any "does this code belong in view or view model?" question answers itself. Platform-dependent code and and markup belong in the view; application-dependent platform-independent logic belongs in the view model; and the two should connect exclusively via bindings to the extent feasible.
ViewModel
? – Semiannual