Roman
Roman

Reputation: 137

How to get access to the MainWindow viewmodel from Page?

I'm trying to understand binding system in WPF. In my example i need to get access to MainWindow viewmodel from Page in XAML.

I have one solution to implement this. But i want to know more different ways

MainWindow.xaml

<Window x:Class="FunAnkiWPF.MainWindow"
    ...omitted for brevity
    Height="450" Width="800"
    DataContext="{Binding ViewModel, RelativeSource={RelativeSource 
Self}}">

MainWindow.xaml.cs

public partial class MainWindow : Window
{        
    public MainWindowViewModel ViewModel { get; set; }

    public MainWindow()
    {
        ViewModel = new MainWindowViewModel(this);
        InitializeComponent();            
    }    
}

StartPage.xaml (usual page)

StartPage.xaml.cs (One solution that works)

public partial class StartPage : Page
{
    public StartPage()
    {
        InitializeComponent();
        DataContext = App.Current.MainWindow.DataContext;
    }
}

How to get a direct access to the MainWindow ViewModel property (in XAML and in codebehind)? How to get access to another datacontext in XAML (like in my StartPage codebehind)?

Upvotes: 1

Views: 3300

Answers (1)

Andy
Andy

Reputation: 12276

The short answer to binding from a child control to a property in the parent window's datacontext is relativesource eg:

         <TextBlock Text="{Binding Path=DataContext.MainWinVMString, 
          RelativeSource={RelativeSource  AncestorType={x:Type Window}}}"

Here's an example to give you the flavour of what I am suggesting.

The MainWindow markup is a bit quick n dirty. I would put datatemplates in a resource dictionary merged by app.xaml.

    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate DataType="{x:Type local:LoginViewModel}">
        <local:LoginUC/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:UserViewModel}">
        <local:UserUC/>
    </DataTemplate>
</Window.Resources>
<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <ItemsControl ItemsSource="{Binding NavigationViewModelTypes}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding Name}"
                    Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                    CommandParameter="{Binding VMType}"
                />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ContentControl Grid.Column="1"
                    Content="{Binding CurrentViewModel}"
                    />
</Grid>

Viewmodel for that:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public string MainWinVMString { get; set; } = "Hello from MainWindoViewModel";

    public ObservableCollection<TypeAndDisplay> NavigationViewModelTypes { get; set; } = new ObservableCollection<TypeAndDisplay>
        (
        new List<TypeAndDisplay>
        {
           new TypeAndDisplay{ Name="Log In", VMType= typeof(LoginViewModel) },
           new TypeAndDisplay{ Name="User", VMType= typeof(UserViewModel) }
        }
        );

    private object currentViewModel;

    public object CurrentViewModel
    {
        get { return currentViewModel; }
        set { currentViewModel = value; RaisePropertyChanged(); }
    }
    private RelayCommand<Type> navigateCommand;
    public RelayCommand<Type> NavigateCommand
    {
        get
        {
            return navigateCommand
              ?? (navigateCommand = new RelayCommand<Type>(
                vmType =>
                {
                    CurrentViewModel = null;
                    CurrentViewModel = Activator.CreateInstance(vmType);
                }));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Relaycommand is from nuget package mvvmlightlibs.

UserUC:

<Grid Background="pink">
    <TextBlock Text="This is the User module Control"
               VerticalAlignment="Top"
               />
    <TextBlock Text="{Binding Path=DataContext.MainWinVMString, RelativeSource={RelativeSource  AncestorType={x:Type Window}}}"
               VerticalAlignment="Bottom"
               />
</Grid>

Full working sample: https://1drv.ms/u/s!AmPvL3r385QhgqIZUul-ppiIHZ9uyA

Upvotes: 0

Related Questions