I will start with my code (entire sample is here: https://github.com/robertwojnar/MvvmDemo1) In my little demo I have single view application with usercontrol inside. So I have:
- MainWindow.xaml (my view)
- FooUserControl.xaml (view of my usercontrol)
- MainWindowViewModel.cs (viewmodel for my view)
and that's basically it. Very simple. Here is the code:
FooUserControl.xaml
<UserControl x:Class="MvvmDemo1.WPF.Views.FooUserControl"
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:MvvmDemo1.WPF.Views"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100">
<Grid MouseDown="UIElement_OnMouseDown">
<Rectangle Fill="BlueViolet" />
</Grid>
</UserControl>
FooUserControl (code-behind)
public partial class FooUserControl : UserControl
{
public FooUserControl()
{
InitializeComponent();
}
public event EventHandler<BarEventArgs> BarClick;
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
double x = e.GetPosition(this).X;
double y = e.GetPosition(this).Y;
string value_to_pass = "[" + x + "," + y + "]";
BarEventArgs bar = new BarEventArgs() { Bar = 2, Foo = value_to_pass };
BarClick?.Invoke(sender, bar);
}
}
MainWindow.xaml (no code behind)
<Window x:Class="MvvmDemo1.WPF.Views.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:MvvmDemo1.WPF.Views"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
xmlns:viewModels="clr-namespace:MvvmDemo1.WPF.ViewModels">
<Window.DataContext>
<viewModels:MainWindowViewModel />
</Window.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<local:FooUserControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="BarClick">
<cmd:EventToCommand Command="{Binding ClickedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local:FooUserControl>
</Grid>
</Window>
MainWindowViewModel.cs
public class MainWindowViewModel : ObservableObject
{
public string Title => "Main window";
public ICommand LoadedCommand => new RelayCommand(Loaded);
private void Loaded()
{
Debug.WriteLine("Loaded");
}
public ICommand ClickedCommand => new RelayCommand<BarEventArgs>(o => Clicked(o.Foo));
private void Clicked(string a)
{
Debug.WriteLine("Clicked " + a);
}
}
As you can see the app is just a purple rectange which (on click event) sends click coordinates to MainWindowViewModel (via ICommand, using MVVM Light's EventToCommand
and Interaction.Triggers
).
Here is my problem and question:
I want to add a ViewModel for my UserControl. Want to add FooUserControlViewModel
to FooUserControl's DataContext
. The problem is, ClickedCommand is not fired when I set DataContext of FooUserControl. The question is Why?
When you clone my repo and change FooUserControl (code-behind) constructor to this, you will get my point:
public FooUserControl()
{
InitializeComponent();
DataContext = new FooUserControlViewModel();
}
EDIT:
It looks like, MainWindowViewModel is assigned to FooUserControl's DataContext (I think, because Iteraction.Triggers
or EventToCommand
). Why is that happening? Isn't this violating the rule that view-viewmodel connection should be 1<->1. One View - one ViewModel?