In MVVM is it acceptable to access the ViewModel in the view's code behind?
Asked Answered
J

3

4

IN an MVVM pattern is it acceptable or even possible to access ViewModel properties in the views code behind?

I have an observable collection which is populated in the ViewModel. I need to use it in the view to bind to an endless ticker with a linked list. i.e.

    private LinkedList<Border> tickerForex = new LinkedList<Border>();

    public ForexBuy()
    {
        InitializeComponent();
        DataContext = new ForexViewModel();
    }

    private void InitializeForexTicker()
    {
        CanvasForexBuyTicker.Children.Clear();
        foreach (var currency in DataContext.Currencies) //Is this possible/allowable???
        {
           AddTickerItem(currency);
        }

        CanvasForexBuyTicker.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate
        { var node = tickerForex.First;

            while (node != null)
            {
                if (node.Previous != null)
                {
                    Canvas.SetLeft(node.Value, Canvas.GetLeft(node.Previous.Value) + node.Previous.Value.ActualWidth + gap);
                }
                else
                {
                    Canvas.SetLeft(node.Value, CanvasForexBuyTicker.Width + gap);
                }

                node = node.Next;
            }

            return null;

        }), null);

}

void AddTickerItem(Currency currency)
    {
        Border border = new Border();
        border.Background = new SolidColorBrush(Color.FromArgb(255, 0, 99, 99));

        if (currency.IsUpward == 0)
        {
            border.Background = new SolidColorBrush(Color.FromArgb(255, 255, 153, 0));
        }

        border.BorderThickness = new Thickness(3);
        border.BorderBrush = new SolidColorBrush(Colors.White);
        border.CornerRadius = new CornerRadius(10);
        border.Width = Double.NaN;
        border.Height = 35;

        UIHelper.CanvasAutoSize canvas = new UIHelper.CanvasAutoSize();
        canvas.Background = Brushes.Green;  
        canvas.Tag = currency;
        canvas.Height = Double.NaN;

        TextBlock tb = new TextBlock
        {
            Text = currency.Code + " " + currency.Sell + " ",
            FontSize = 22,
            FontWeight = FontWeights.Bold,
            Foreground = Brushes.Black
        };

        tb.SetValue(Canvas.LeftProperty, 8d);
        tb.SetValue(Canvas.TopProperty, 2d);
        canvas.Children.Add(tb);

        tb.TouchDown += TouchTickerItem;

        border.Child = canvas;

        CanvasForexBuyTicker.Children.Add(border);
        Canvas.SetTop(CanvasForexBuyTicker, 3);
        Canvas.SetLeft(CanvasForexBuyTicker, 0);

        tickerForex.AddLast(border);
    }

I'm a little lost as to whether the dispatcher should fire from the ViewModel or whether to use it in the views code behind.

Jarietta answered 6/12, 2012 at 21:48 Comment(2)
Why can't you just bind straight to the list in the ViewModel?Semiannual
Can't see how that helps. 'CanvasForexBuyTicker' needs the dispatcher attached and the ViewModel doesn't (shouldn't) know about the canvas.Jarietta
S
10

If the question is just about how to access the ViewModel from code behind, you could simply cast the DataContext to the proper type:

var viewModel = (MyViewModel)DataContext;

foreach (var currency in viewModel.Currencies)
{
    ...
}

If this is acceptable or not is a matter of taste. I do not see any fundamental difference in accessing the view model by bindings in the view's XAML or by a piece of code-behind.

Sharleensharlene answered 6/12, 2012 at 23:23 Comment(0)
W
-1

Short answer: NO.

you're not supposed to place business or Model specific code in the code behind. Only VIEW-specific code should be placed in code behind.

Why are you declaring a List<Border>. Sounds weird to me. What exactly are you trying to do?

Warlock answered 6/12, 2012 at 21:56 Comment(9)
It's list border because the items on the ticker are TextBlocks set in a Canvas set in a border. I understand that you shouldn't place business logic in the code behind. Wouldn't a dispatcher fired like this be part of the view logic? e.g. because it's endless I have to keep checking if the last one is coming into view and append the first node again.Jarietta
@JohnSourcer that's what an ItemsControl is meant for. I'm trying to get my head around in order to understand what you're trying to do here, but I can'tWarlock
thanks for looking. I've edited to the code above to try clarify.Jarietta
@HighCore Could you please provide any reference for that strict no? Personally i think you're not right here, and i tend to say the exact opposite: Yes, it is absolutely OK to access viewmodel properties in the view's code-behind, in the same sense as it is perfectly OK to access viewmodel properties in the view's bindings. But maybe you tell us where you get your opinion from. And we're not talking about model properties here, but about viewmodel properties.Sharleensharlene
@Sharleensharlene maybe i'm wrong. Its just that I don't completely understand what the OP is doing here, by declaring some List<Border> in code... maybe I misinterpreted the question.Warlock
@HighCore But the fact that you do not fully understand the question does in no way justify that "Short answer: NO". You should either edit or remove this answer.Sharleensharlene
I agree, it is not unreasonable to have to have code in the code behind for a view. As long as it deals with how to display the data, and not how to calculate or load the data etc. I've had to do this when dealing with hosted windows forms controls that do not support data binding, or when significant conversion has to take place in order to display it on a graph, and that conversion was specific to that control.Norvan
Conceptually speaking, the viewmodel exists to act as an intersection of responsibilities between the view's physical construction and the abstract model data it is displaying. Therefore of course it makes sense that it be accessed from the view, as the only reason it exists is to support that view. Simply accessing it does not constitute a pollution of the view's responsibility with business logic—the actual business logic should happen in the model anyway, not the viewmodel.Westlund
The viewmodel is really only there to offload some of the responsibility of the view into another file; since its logic is uniquely tied to the view, it does not really represent an abstraction per se.Westlund
M
-1

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 (ICommands 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.

Matson answered 19/6 at 23:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.