robertwojnar
robertwojnar

Reputation: 129

Looks like Interaction.Triggers doesn't work with DataContext set

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:

  1. MainWindow.xaml (my view)
  2. FooUserControl.xaml (view of my usercontrol)
  3. 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?

Upvotes: 0

Views: 1040

Answers (1)

Kylo Ren
Kylo Ren

Reputation: 8823

If you want to assign a new DatContext to your local:FooUserControl you can do that but you'll no longer will be able to bind your controls from MainWindowViewModel directly. You have to change your Binding like below for Command:

<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}},Path=DataContext.ClickedCommand}" PassEventArgsToCommand="True" />

When we set the DataContext of a control it will aotomatically pased down to it's child controls by Property Value Inheritance. And if you change DataContext of a UserControl in middle of a hierarchy/VisualTree the DataContext will be different for Ancestors and Descendants.

Also there is no such rule 1 View -1 ViewModel. It's totally depends your design a complexity/ Requirement how many ViewModels you have to design for your View or one ViewModel for different View.

Upvotes: 1

Related Questions