ViewModel hierarchy in wpf

1.2k views Asked by At

I have a structure of UI which consists of MainWindowViewModel and MainViewModel which has three tabs ViewModels. I am using MVVMLight for instantiating those Tabs. I need to pass a string from a firstviewmodel to xaml to display it on my mainwindow view. When i dont use MainWindowViewModel it works, but my MainViewModel is part of MainWindowViewModel. So, my question - what is the right way to initialize MainViewModel inside MainWindowViewModel so I could get a Text Property from my tab and display it to my window ?

My MainWindow.xaml

<Window
x:Class="Tabs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Tabs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Grid>
    <TabControl
        Width="340"
        Height="202"
        Margin="21,41,0,0"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"

        >
        <TabItem Header="Page 1">
            <Grid Background="#FFE5E5E5" DataContext="{Binding MainViewModel.FirstViewModel}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="11*"/>
                    <ColumnDefinition Width="24*"/>
                    <ColumnDefinition Width="34*"/>
                    <ColumnDefinition Width="265*"/>
                </Grid.ColumnDefinitions>
                <TextBlock Height="100" Text="{Binding MainViewModel.FirstViewModel.Text}" Grid.ColumnSpan="4" Margin="0,37" />
            </Grid>
        </TabItem>  
        <TabItem Header="Page 2">
            <Grid Background="#FFE5E5E5" DataContext="{Binding MainViewModel.SecondViewModel}">
                <TextBlock Height="100" Text="{Binding Text}" />
            </Grid>
        </TabItem>
        <TabItem Header="Page 3">
            <Grid Background="#FFE5E5E5" DataContext="{Binding MainViewModel.ThirdViewModel}">
                <TextBox Height="100" Text="{Binding Input}" />
            </Grid>
        </TabItem>
    </TabControl>

</Grid>

Code Behind

   public partial class MainWindow : Window
         {
            public MainWindow()
            {
                InitializeComponent();
                DataContext = new MainWindowViewModel();
            }
         }  

My MainWindowViewModel

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tabs.ViewModel
{
    class MainWindowViewModel : INotifyPropertyChanged
    {
        private MainViewModel mainViewModel;

        public MainWindowViewModel()
        {
            mainViewModel = new MainViewModel();

        }

        public MainViewModel MainViewModel
        {
            get
            {
                return mainViewModel;
            }

            set
            {
                mainViewModel = value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

My MainViewModel

using GalaSoft.MvvmLight;
using Tabs.ViewModel.Base;

namespace Tabs.ViewModel
{

    public class MainViewModel : ViewModelBase
    {

        public FirstViewModel FirstViewModel { get; set; }
        public SecondViewModel SecondViewModel { get; set; }
        public ThirdViewModel ThirdViewModel { get; set; }
        public MainViewModel()
        {
            FirstViewModel = new FirstViewModel();
            FirstViewModel.Text = "ehe"; //all i need is to show
            SecondViewModel = new SecondViewModel();
            ThirdViewModel = new ThirdViewModel();
        }
    }
}

My FirstViewModel and The text field I actually want to get

public class FirstViewModel:BaseViewModel
{
    private string _text;

    public string Text
    {
        get { return _text; }
        set { Set(() => Text, ref _text, value); }
    }

    public FirstViewModel()
    {

    }
}
1

There are 1 answers

2
Heinrich On

The problem that your having is that in your Textblock Text binding being as follows:

Text="{Binding MainViewModel.FirstViewModel.Text}"

As well as that fact that your TextBlock is in a Grid that has it datacontext set as:

DataContext="{Binding MainViewModel.FirstViewModel}"

So effectively what is happening is that it is looking for your text at this location:

MainViewModel.FirstViewModel.MainViewModel.FirstViewModel.Text

Because the datacontext of your TextBlock is your FirstViewModel, if you go Text="{Binding DataContext}" you will see this being the case.
What you want to do is simply go:

Text="{Binding Text}"

Additional Notes:

The reason for this is because when you go Text="{Binding Text}" wpf checks the elements current datacontext, which will always be the same as the DataContext of its parent unless it is explicitly set.