简体   繁体   中英

Changing the ContentControl binding results in a null DataContext

Perhaps one of you could enlighten me on the following problem. I'm using a list box as a way to navigate between 2 viewmodels.

<Window x:Class="MyWPFApp.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:cal="http://www.caliburnproject.org">

  <Grid Background="White">
     <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="8*"/>
     </Grid.ColumnDefinitions>
     <ListBox Grid.Column="0" cal:Message.Attach="[Event SelectionChanged] 
= [Action onViewSelectionChange($this.SelectedItem.Text)]">
        <TextBlock Text="FirstView"/>
        <TextBlock Text="SecondView"/>
     </ListBox>
        <ContentControl Grid.Column="1" Content="{Binding MyViewModel}"/>
  </Grid>

namespace MyWPFApp
{
    public class ShellViewModel : Caliburn.Micro.PropertyChangedBase, IShell
   {
      private object _vm;

      public object MyViewModel
      {
         get { return _vm; }
         set { _vm = value; NotifyOfPropertyChange(() => MyViewModel); }
      }

      public FirstViewModel vm1;
      public SecondViewModel vm2;



      public ShellViewModel()
      {
         vm1 = new FirstViewModel();
         vm2 = new SecondViewModel();
         MyViewModel = vm1;

      }

      public void onViewSelectionChange(string str)
      {

         switch (str)
         {
            case "FirstView":
               MyViewModel = vm1;
               break;
            case "SecondView":
               MyViewModel = vm2;
               break;
            default:
               break;

         }
      }
   }
}

I've create 2 datatemplates to associate the viewmodel with its view

<Application x:Class="MyWPFApp.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-MyWPFApp">
  <Application.Resources>
  <ResourceDictionary>
     <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
           <local:AppBootstrapper x:Key="bootstrapper" />
        </ResourceDictionary>
     </ResourceDictionary.MergedDictionaries>
     <DataTemplate DataType="{x:Type local:FirstViewModel}">
        <Grid>
           <local:FirstView></local:FirstView>
        </Grid>
     </DataTemplate>
     <DataTemplate DataType="{x:Type local:SecondViewModel}">
        <Grid>
           <local:SecondView></local:SecondView>
        </Grid>
     </DataTemplate>
  </ResourceDictionary>
  </Application.Resources>
  </Application>

FirstView actually has controls which have bindings to properties in FirstViewModel

   <UserControl x:Class="MyWPFApp.FirstView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:local="clr-namespace:MyWPFApp"
            xmlns:cal="http://www.caliburnproject.org"
            mc:Ignorable="d" 
            d:DesignHeight="300" d:DesignWidth="300">
  <Grid Background="Aquamarine">
     <ListBox SelectedIndex="{Binding SelectedIndex}" 
              SelectedItem="{Binding SelectedItem}"
              cal:Message.Attach="[Event SelectionChanged]=[Action OnLBSelectionChanged($source)]">
        <ListBoxItem>apple</ListBoxItem>
        <ListBoxItem>orange</ListBoxItem>
        <ListBoxItem>pear</ListBoxItem>
     </ListBox>
  </Grid>

using Caliburn.Micro;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyWPFApp
{
   public class FirstViewModel : PropertyChangedBase
   {

      private int _selectedindex;

      public int SelectedIndex
      {
         get { return _selectedindex; }
         set { _selectedindex = value; NotifyOfPropertyChange(() => SelectedIndex); }
      }


      private string _selecteditem;

      public string SelectedItem
      {
         get { return _selecteditem; }
         set { _selecteditem = value; NotifyOfPropertyChange(() => SelectedItem); }
      }



      public FirstViewModel()
      {
      }


      public void OnLBSelectionChanged(System.Windows.Controls.ListBox lb)
      {
         if(lb.DataContext == null)
         {
            Debug.WriteLine("The DataContext is null");
         }

      }

   }
}

The SecondView contains no controls.

When the ShellView list box selection changes from FirstView to SecondView, I've noticed that the FirstView ListBox DataContext becomes null.

  public void OnLBSelectionChanged(System.Windows.Controls.ListBox lb)
  {
     if(lb.DataContext == null)
     {
        Debug.WriteLine("The DataContext is null");
     }

  }

I would prefer not to have to handle selection changes looking for a null DataContext. Instead, I'd like to know what's actually happening. Thanks.

The reason why this is happening is that the DataContext for FirstView is implicitly wired up by WPF when the ContentControl 's Content property is set to the FirstViewModel via data binding.

Once the binding changes to be SecondViewModel the FirstViewModel goes out of scope and thus WPF cleans up after itself, removing FirstViewModel , FirstView , and its implicit DataContext binding.

I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM