Reputation: 137
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
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