Reputation: 3218
I have a MainWindow.xaml
with the DataContext
set as MainWindowViewModel.cs
. This window has nothing in it until the user clicks a button and adds a MyUserControl.xaml
to the window. That MyUserControl.xaml
has a MyUserControlViewModel.cs
set as the DataContext
.
I want to trigger an event in the MyUserControlViewModel.cs
that gets caught by the MainWindowViewModel.cs
.
So far I have this. In my MyUserControlViewModel.cs
public event EventHandler<LoadingEventArgs> LoadingEvent;
(...)
public void MyFunction() {
LoadingEvent(this, EventLibrary.LoadingEvent(2));
}
And in my MainWindowViewModel.cs
private MyUserControlViewModel _myUserControlViewModel;
public MyUserControlViewModel _MyUserControlViewModel {
get { return _myUserControlViewModel; }
private set { _myUserControlViewModel = value; OnPropertyChangedEvent("MyUserControlViewModel"); }
}
public MainWindowViewModel() {
// Can't attach event here
}
public void AddEventToUserControl() {
_MyUserControlViewModel = new MyUserControlViewModel();
_MyUserControlViewModel.LoadingEvent += MyUserControlViewModel_LoadingEvent;
}
private void MyUserControlViewModel_LoadingEvent(object sender, LoadingEventArgs e) {
MessageBox.Show("I got here!");
}
The message "I got here"
never shows because the LoadingEvent
in the MyUserControlViewModel.cs
is null
.
The reason I'm only attaching the MyUserControlViewModel_LoadingEvent
outside the MainWindowViewModel()
function is because the MyUserControl.xaml
is not added when the windows loads, but only after, when the user clicks the button.
I think the problem here is that the event is not added as the same time as the creation of the MyUserControlViewModel.cs
.
I need to know how can I trigger bubbling events through ViewModels when they are created dynamically through code.
UPDATE - MORE INFO ADDED
Given that my above situation may be to simple and lacks important details, I'm going to describe my exact situation.
I have three views - a grandfather, a father and a son. For each one I have a ViewModel class. In my father I have the son declared in the XAML because it is created at the same time, so when I have SonViewModel = new SonViewModel()
in my FatherViewModel
, it points to the right place and I can access everything I need in the SonViewModel
. Consequently, any event triggered in SonViewModel
that is being listened by FatherViewModel
gets caught.
The two most important facts here are that both father and son are created at the same time and the son is created in the XAML like so:
<View:SonView DataContext="{Binding SonViewModel, Mode=OneWay}" />
This works fine does exactly what I want.
The problem comes with the grandfather and the father because they're not created at the same time. I first create the GrandfatherView.xaml
and in it's associated GrandfatherView.cs
(I know it's not MVVM practice, but I'm trying to implement some MVVM features in a project that's not with MVVM pattern) I create and add the FatherView
to a Grid
, like so:
FatherView _fatherView = new FatherView();
_fatherView.DataContext = new FatherViewModel();
mainGrid.Children.Add(_fatherView);
Because I'm creating the FatherView
after I created the GrandfatherView
and adding it through code and not having a specific binding, when I create the FatherViewModel
object in my GrandfatherViewModel
class, it's not pointing to the actual ViewModel of the FatherView
I see in my app.
I'm not sure how to create the binding <View:SonView DataContext="{Binding SonViewModel, Mode=OneWay}" />
in code, so I can make sure the attributes and bindings are the same, so the ViewModel object I have in my GrandfatherViewModel
is actually the one I see in my app and not a completely new object.
So, any suggestions?
Upvotes: 2
Views: 2356
Reputation: 2358
Complete solution to problem (with use of Mvvm Light):
GrandFatherView.xaml
<Window x:Class="FunWithDynamicallyCreatedViewModels.GrandFatherView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Name="MainGrid" />
</Window>
GrandFatherView.xaml.cs
public sealed partial class GrandFatherView
{
public GrandFatherView()
{
InitializeComponent();
var fatherView = new FatherView
{
DataContext = new FatherViewModel
{
SonViewModel = new SonViewModel()
}
};
MainGrid.Children.Add(fatherView);
}
}
FatherView.xaml
<UserControl x:Class="FunWithDynamicallyCreatedViewModels.FatherView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FunWithDynamicallyCreatedViewModels">
<local:SonView DataContext="{Binding SonViewModel}" />
</UserControl>
FatherViewModel.cs
public sealed class FatherViewModel : ViewModelBase
{
private SonViewModel _sonViewModel;
public SonViewModel SonViewModel
{
get { return _sonViewModel; }
set
{
if (SonViewModel != null)
SonViewModel.Event -= OnUserControlViewModelEvent;
Set(() => SonViewModel, ref _sonViewModel, value);
if (SonViewModel != null)
SonViewModel.Event += OnUserControlViewModelEvent;
}
}
private void OnUserControlViewModelEvent(object sender, EventArgs args)
{
// violation of MVVM for the sake of simplicity
MessageBox.Show("I got here!");
}
}
SonView.xaml
<UserControl x:Class="FunWithDynamicallyCreatedViewModels.SonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Command="{Binding InvokeEventCommand}"
Content="Invoke SonViewModel Event" />
</UserControl>
SonViewModel.cs
public sealed class SonViewModel
{
public SonViewModel()
{
InvokeEventCommand = new RelayCommand(() => Event(this, new EventArgs()));
}
public event EventHandler Event;
public ICommand InvokeEventCommand { get; private set; }
}
Upvotes: 2