Reputation: 3265
I wonder if it is possible to shared a datacontext between a Windows and is UserControl in C#/WPF.
I have a main windows like this (not finished):
MainWindow.xaml:
<Window x:Class="MyProject.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyProject"
xmlns:v="clr-namespace:MyProject.Views"
mc:Ignorable="d"
Title="MyProject" >
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<v:GenerateView/>
<v:ReadView/>
</Grid>
</Window>
MainViewModel.cs:
public class MainViewModel : ViewModelBase
{
#region Properties
#endregion
#region Fields
#endregion
#region Constructor
public MainViewModel()
: base()
{
}
#endregion
#region Methods
#endregion
#region Commands
#endregion
}
Depending on a future parameter, I will display my view GenerateView or ReadView. Actually I'm developing the UserControl GenerateView, but I wonder if I can use the same Datacontext.
According to that post, I started by this :
<UserControl x:Class="MyProject.Views.GenerateView"
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:MyProject.Views"
xmlns:p="clr-namespace:MyProject.Properties"
xmlns:MyProject="clr-namespace:MyProject"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyProject:MainWindow}}}">
<Grid>
</Grid>
</UserControl>
But It doesn't work, when I try to access Datacontext in GenerateView
, It is null.
Edit:
I forgot a part of my code:
public partial class GenerateView : UserControl
{
private MainViewModel Context
{
get
{
return DataContext as MainViewModel;
}
}
public GenerateView()
{
InitializeComponent();
Context.PropertyChanged += Context_PropertyChanged;
}
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//Action to perform
}
}
The line Context.PropertyChanged += Context_PropertyChanged;
throw an Exception because Datacontext is null.
Upvotes: 3
Views: 2332
Reputation: 643
One can use the View first approach. You first define a design context for design purposes.
<UserControl x:Class="MyProject.Views.CustomView"
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:MyProject.Views"
xmlns:p="clr-namespace:MyProject.Properties"
xmlns:MyProject="clr-namespace:MyProject"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:MyViewModel}"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
.....
</Grid>
</UserControl>
then you bind the usercontrol to the datacontext of the mainwindow
<Window x:Class="MyProject.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyProject"
xmlns:v="clr-namespace:MyProject.Views"
mc:Ignorable="d"
Title="MyProject" >
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<v:CustomView/>
</Grid>
</Window>
then the usercontrol will automatically inherit the datacontext of its parent.
Upvotes: 0
Reputation: 5083
I usually set the DataContext
in my Window
:
public class MainWindow : Window
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
}
or sometimes more advanced:
I add a static property to my ViewModel:
public static ViewModel Instance {get; set;}
public class MainWindow : Window
{
InitializeComponent();
if(ViewModel.Instance == null)
{
ViewModel.Instance = new ViewModel();
}
this.DataContext = ViewModel.Instance;
}
Upvotes: 1
Reputation: 4464
What is the main requirement behind reusing a view model for Window and it’s sub user control? it doesn’t make sense. Does both have everything in common?
In my opinion create a MainWindowViewModel
and create SubViewModel
for user control.
Create the instances of sub viewmodels in your MainWindowViewModel and access them using DataContext.SubViewModel
.
By doing so, you could maintain the code and application well as well as preserve coding standards and have a complication free viewmodel. If you mix up everything just for reusability, you may be violating the MVVM pattern. Let different views/windows have its own viewmodel since they are not at all alike.
If both are alike, then you can create reusable controls using Dependency Properties
.
Upvotes: 3
Reputation: 3265
According to Andrew's comment I'have the solution to my problem:
public partial class GenerateView : UserControl { private MainViewModel Context { get { return DataContext as MainViewModel; } }
public GenerateView()
{
InitializeComponent();
DataContextChanged += GenerateView_DataContextChanged;
}
private void GenerateView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (Context != null)
Context.PropertyChanged += Context_PropertyChanged;
}
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//Action to perform
}
}
And I removed DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyProject:MainWindow}}}"
from my UserControl.
Upvotes: 0