Lacir1971
Lacir1971

Reputation: 23

WPF MVVM calling ViewModel methods from code behind

First off I am very new to WPF MVVM and a bit confused. People say that generally in MVVM the best practice is not to have any code behind. I have found that some methods are way easier to achieve in code behind than in viewmodel (e.g MouseMove) and that led me thinking between the differences of these 2 following examples:

1) Using RelayCommand:

View

<Button Command="{Binding AlertCommand}"></Button>

ViewModel

public RelayCommand AlertCommand { get; set; }

public void Alert()
{
   MessageBox.Show("message");
}

2) calling ViewModel methods from code behind:

View

<Button PreviewMouseLeftButtonUp="OnMouseLeftButtonUp"></Button>

View code behind

private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
     var ctx = (MainViewModel) this.DataContext;
     ctx.Alert();
}

Is using code behind here wrong? What are my benefits of not using code behind?

Upvotes: 2

Views: 9663

Answers (3)

Emperor Eto
Emperor Eto

Reputation: 3520

Adding to the two excellent answers, I'd like to specifically address the question "What are my benefits of not using code behind?" and even more specifically, what are the downsides of invoking view model methods from code-behind? In other words, why do you not want to do this:

private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
     var ctx = (MainViewModel) this.DataContext;
     ctx.Alert();
}

if you can avoid it? While a lot of discussion about MVVM centers around design philosophy, I think an even better way to think of things is in terms of simple economics.

Try to think of all your code by categorizing it into a 2D chart; one dimension being Platform Dependent vs. Platform Agnostic, (and by platform I mean the GUI platform, e.g., WPF, WinUI3, MAUI, etc.) and the other being Application Dependent vs. Application Agnostic:

Categorizing code by cost

Each of the boxes has a different "cost" associated with it, which is defined to be proportional to the level of specialized expertise and training required to create and maintain it, and inversely proportional to its potential for reuse. The assumption is that we want to minimize this cost. Thus -

As to platform-agnostic code:

  • The "cheapest" code is both platform-agnostic and application-agnostic. It can be reused across all your projects and written by any competent .NET developer without any training on your product or the GUI platform you're using. By its nature, though, most code won't fall in this category, or it'll be obtained from third parties.

  • The data model and deep business logic (the first "M" in MVVM), e.g. CRUD operations and the like, is also ignorant of the GUI platform - or anything about the UI for that matter - and there is thus some reusability potential across related applications (e.g. an ASP.NET server and WPF client may share much of this code). But, it is still solution-specific and requires more specialized knowledge to write.

  • The view model (the "VM" in MVVM) is abstract and platform-agnostic, but it is wholly specific to a single client application, and so it is the most "expensive" code in this column.

And in the GUI platform-dependent column:

  • The least "expensive" code includes generic UI customizations (custom controls, animations, etc.), and any other scaffolding/helper code you might write to extned the platform. This code has reusability potential across any frontend application on the same platform.

  • Next is the application-specific View (the first "V" in MVVM), to the extent it's comprised solely of XAML. Here is where you consume the built-in controls offered by the platform, and any GUI customizations you added in the prior category.

  • Finally, the absolute most "expensive" portion of your application is code-behind in the partial classes backing your views. Besides being platform and application-specific, this category requires the developer to know two languages, and adds two additional layers of connection points - one between XAML and code behind, and the other between view model and code-behind - in addition to the binding interface between XAML view and view model, which is a given. And as anyone with experience on a large project knows, it's the connection points that cost the most to debug and maintain.

If you look at things this way, it's obvious that you want as much of your code to fall into the "cheaper" categories, and as little as possible to be in the "expensive" ones. Therefore if you have a choice between binding to a view model command through pure XAML, or writing code-behind, all else being equal it'd be economically irrational not to choose the former.

The harder cases arise when you have platform-agnostic UI logic that fits in the view model but the GUI platform doesn't provide an MVVM-friendly way to connect to it out of the box, without additional scaffolding. For example, it's easy to bind to ListView.SelectedItem, but if you're in a multi-select mode, WPF offers you no simple binding-based mechanism to obtain or alter the selected items. So do you give up and resort to code-behind, or, do you scaffold an MVVM-friendly extension to ListView (maybe a subclass, attached property, etc.) so that you can bind the selection state of the control to an ObservableCollection in the view model?

The final answer will always depend on specifics, but in general, I would do the latter, because the scaffolding would very likely be reusable and thus fall in the less expensive box. In other words, code-behind may be fine in a pinch for a one-off you know you'll never have any use for again, but in many cases, with just a little extra work you can maintain a clean MVVM pattern and also end up with a nice reusable library for future projects.

Thus, the "no code-behind" advice, while obviously not an absolute rule (as if there were such a thing in programming), isn't just a matter of personal preference or aesthetics. It really does lead to a better, more cost efficient and valuable product.

Upvotes: 0

Martin Zikmund
Martin Zikmund

Reputation: 39072

The MVVM pattern is a best practice when building UWP, WPF, and Xamarin.Forms apps. The main advantage is that you are able to decouple the logic from the presentation and potentially could present a single view model by multiple different views and could switch views without having to modify the view model significantly. This is a great advantage when building cross-platform apps with native UI, which MvvmCross framework uses to a great extent for example.

Having an empty code-behind is however definitely just an ideal, which is usually not easy and not always necessary to achieve. Sometimes you need to use code-behind for purely view-related manipulation like changing layout for different window sizes, controlling animations, etc. For such actions, code-behind is a much better fit than trying to force this somehow into the VM.

Comparing your two approaches, using the RelayCommand-based one over the direct method call is preferable, because it has less direct tie to the method itself. If you wanted, you could switch the RelayCommand instance in the VM for a different implementation (calling different method) at runtime, and thanks to binding the button could now perform a different action. This can be used in editor-like apps where some tools may have different functionality based on current context the app is in.

Also, for controls which do not offer a Command you can use EventTrigger hand in hand with InvokeCommandAction (both defined withing Expression Blend SDK) which will allow you to "convert" an event to a command call, even with your defined transformation of EventArgs.

Upvotes: 5

Lennart
Lennart

Reputation: 10324

Both are valid methods. The first is preferable if possible. I generally use the second method on events where no command binding is available. The notion of "absolutely no code-behind in MVVM" is debatable. Any code that belongs directly to the view (and is not business logic) and is not reusable in a VM can be put in the code-behind, such as wiring up events in the second example.

Upvotes: 2

Related Questions