BindingExpression errors when switching ViewModels in MVVM application

First of all, some context. If you are familiar with the problem, go to the BindingExpression part. This is my first major project in WPF, so I'm still new to the MVVM template. Here is the only other similar question I have found whose dull answer really doesn't really interest me.

I / I am creating a .NET 3.5 WPF application and I am using MVVM (implemented by myself, without a framework). In this case, I have a number of Views and ViewModels . They are within the leading ApplicationView and ApplicationViewModel respectively.

A way to change the views is to use the XAML DataTemplate elements in the ApplicationView , for example:

 <DataTemplate DataType="{x:Type viewmodels:InitViewModel}"> <views:InitView /> </DataTemplate> 

And then in the main part, I have a ContentControl that binds to a property in ApplicationViewModel

 <ContentControl Content="{Binding CurrentPageViewModel}"/> 

When I run the application, it all seems to work fine and does exactly what it is intended to do. However, when I look at the output of Debug after startup, I get a lot of BindingExpression errors.

Here is one example. I have a SplashText property in my InitViewModel . This is due to the text block in the splash screen ( InitView ). When the splash screen ends and I exit the view mode, I get the following:

System.Windows.Data Error: 39 : BindingExpression path error: 'SplashText' property not found on 'object' ''MainMenuViewModel' (HashCode=680171)'. BindingExpression:Path=SplashText; DataItem='MainMenuViewModel' (HashCode=680171); target element is 'TextBox' (Name='FeedBackBox'); target property is 'Text' (type 'String')

I understand that this is because the bindings still exist, but the CurrentPageViewModel DataContext property has changed. So I want to know the following:

  • Is this a fleeting problem, i.e. Are representations that are not used, or are they (and bad bindings) in memory there endlessly?
  • Is there a way to clear or deactivate these bindings while the view is inactive?
  • What kind of performance knock will have in my application if I leave them alone?
  • Is there a better way to switch views that avoids this problem?

Thanks in advance and apologize for the monolithic question.

Edit 03/09/13 - Thanks to Jehof, Francesco De Lisi and Faster Solutions, noting that it is pointless to set the sub-views datacontext as {Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}} , because ContentControl takes care of the datacontext.

+6
source share
4 answers

It looks like your DataContext goes into MainMenuViewModel , and your property belongs to another ViewModel that generates the error.

The value of CurrentPageViewModel before and after the pop-up screen changes its Binding when switching views.

Task dued on DataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

Actually, CurrentPageViewModel = InitViewModel when your application starts, but the problem is that each View has the same DataContext (i.e. InitViewModel at first), but I'm sure ViewModels do not have the entire pool of necessary properties to match view bindings. Example for understanding:

ViewX has PropertyX ViewX managed in ViewModelX . ViewY has a binding to PropertyY managed in ViewModelY . Both have DataContext = CurrentViewModel .

When starting CurrentViewModel = ViewModelX , both ViewX and ViewY have DataContext = ViewModelX . But this is not true ! And probably will create an error.

What I usually do is set in the View DataContext class (cs or XAML, if you want) with the appropriate View Model to make sure it fits. Then, when necessary, I call the refresh method to update my values ​​each time the page is switched. If you have common properties, try using a model to centralize information (and values).

Sample image from http://wildermuth.com/images/mvvm_layout.png

enter image description here

Obviously, views are controls wrapped in your MainWindow.

Hope this is clear.

+1
source

Your specific example does not reproduce in .NET 4.5, which probably means that Microsoft also fixed the problem.

However, a similar problem exists when Content and ContentTemplate are data bound. I am going to solve this problem, which can also solve problems in .NET 3.5, if someone still uses it. For instance:

 <ContentControl Content="{Binding Content}" ContentTemplate="{Binding Template}" /> 

Or when a ContentTemplate is defined by a DataTrigger:

 <ContentControl Content="{Binding Content}"> <ContentControl.Style> <Style TargetType="{x:Type ContentControl}"> <Style.Triggers> <DataTrigger Binding="{Binding Choice}" Value="1"> <Setter Property="ContentTemplate" Value="{StaticResource TemplateA}" /> </DataTrigger> <DataTrigger Binding="{Binding Choice}" Value="2"> <Setter Property="ContentTemplate" Value="{StaticResource TemplateB}" /> </DataTrigger> </Style.Triggers> </Style> </ContentControl.Style> </ContentControl> 

In both cases, binding errors similar to the observed OPs occur.

Here you need to make sure that changes to the Content and ContentTemplate are performed in the correct order to prevent binding errors. I wrote a DelayedContentControl that ensures that the Content and ContentTemplate are changed at the same time and in the correct order.

 <jc:DelayedContentControl Content="{Binding Content}" ContentTemplate="{Binding Template}"> 

Similarly for the DataTrigger case.

You can get DelayedContentControl from my openource JungleControls library .

+1
source

Allows you to answer your questions sequentially:

  • You probably already know the answer to this question. When the .NET collector collects it, it will remove the View object from the heap. But until that time, the View object is still bound to the main DataContext on your page and will respond to changes to the DataContext events.
  • The obvious thing to do is to set the DataContext Views to null. DataContext is a dependency property, so a region with null values ​​will only be your view.
  • As the other / dim answer said, this will slow you down a bit, but not a lot. I would not worry too much about it.
  • Yes. Here's a useful thread for viewing navigation options: Viewing navigation options

I also suggest a look at the structure. Something lightweight like MVVM Light will solve many problems for you with very little integration. The ViewModelLocator template also does what you do, but without side effects and provides a whole bunch of cleaning options.

0
source

You can omit the DataContext binding in your views

 DataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 

calls the DataContext your View - this is the DataContext for the ContentControl and is set by your Content -Property binding.

So, when your CurrentPageViewModel property CurrentPageViewModel set to InitViewModel , ContentControl will use InitViewModel as DataContext and use InitView as ContentTemplate , and it will set its own DataContext as DataContext InitView.

0
source

Source: https://habr.com/ru/post/952761/


All Articles