Changing DataContext, does not change target

Jul 2, 2014 at 1:24 PM
Having a view which uses Action.Target and I change the view's DataContext, then the actions target doesn't always change.

Simple Test-Program:
<Window x:Class="CaliburnMicroTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow"
        Width="525"
        Height="350"
        x:Name="_this">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PreviewKeyDown">
            <cal:ActionMessage MethodName="KeyPressed">
                <cal:Parameter Value="$eventArgs" />
            </cal:ActionMessage>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <StackPanel VerticalAlignment="Center">
        <Button cal:Action.Target="{Binding ElementName=_this}" cal:Message.Attach="NewDataContext">Change DataContext</Button>
        <Button cal:Action.Target="{Binding ElementName=_this}" cal:Message.Attach="CheckCurrentDataContext">Check DataContext</Button>
        <Button cal:Message.Attach="DoSomething">Do something with current DataContext</Button>
    </StackPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Input;

namespace CaliburnMicroTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new DataContext();
        }

        public void NewDataContext()
        {
            Console.WriteLine("Assigning new datacontext");
            DataContext = new DataContext();
        }

        public void CheckCurrentDataContext()
        {
            ((DataContext) DataContext).PrintId();
        }
    }

    public class DataContext
    {
        private Guid _id;

        public DataContext()
        {
            _id = Guid.NewGuid();
            Console.WriteLine(_id + ": New DataContext created");
        }

        public void DoSomething()
        {
            Console.WriteLine(_id + ": Doing something...");
        }

        public void KeyPressed(KeyEventArgs args)
        {
            Console.WriteLine(_id + ": key pressed");
        }

        public void PrintId()
        {
            Console.WriteLine(_id );
        }
    }
}
What happens is then e.g.:

95ee17fe-50e9-4c13-b98d-c27b2dd19d79: New DataContext created
95ee17fe-50e9-4c13-b98d-c27b2dd19d79
95ee17fe-50e9-4c13-b98d-c27b2dd19d79: Doing something...
Assigning new datacontext
dad65fc8-b3c1-4742-ae64-2ae3a09689fa: New DataContext created
dad65fc8-b3c1-4742-ae64-2ae3a09689fa
95ee17fe-50e9-4c13-b98d-c27b2dd19d79: Doing something...

My DataContext changed from 95ee17fe-50e9-4c13-b98d-c27b2dd19d79 to dad65fc8-b3c1-4742-ae64-2ae3a09689fa but DoSomething() is still called on the old DataContext.

But if you try often enough, it will sometimes suddenly work:

6128332a-83ad-47d2-84e3-fec60fdcf957: New DataContext created
6128332a-83ad-47d2-84e3-fec60fdcf957
27eb6a9f-c2e4-4a40-8a5c-45d8fd1d0e4f: Doing something...
Assigning new datacontext
8f5ee04a-ffa8-4705-ae0c-94a01116d5a3: New DataContext created
8f5ee04a-ffa8-4705-ae0c-94a01116d5a3
8f5ee04a-ffa8-4705-ae0c-94a01116d5a3: Doing something...

Any idea what's going wrong here? I've tested this with various versions from 1.0.0 up to 2.0.0 and it behaves exactly the same.
Jul 4, 2014 at 1:59 PM
This is really driving me nuts. I ran into the same issue now with a menu item.
<MenuItem
  Header="{Binding Id}"
  ca:Action.Target="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.KeyboardLayouts.SelectedKeyboardLayout}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
    <ca:ActionMessage MethodName="Paste">
This works until the SelectedKeyboardLayout property changes. The Id-Binding of the MenuItem's Header property correctly changes, but the Action target still points to the previous SelectedKeyboardLayout property value.

Any ideas?
Jul 4, 2014 at 4:30 PM
Here's another example:

Caliburn does not care for the changed target (DataContext.Something). ActionMessage.UpdateContext() does not get called when DataContext.Something changes, so it will always use the old target "cached" in the ActionExecutionContext.
<Window x:Class="CaliburnTestBed.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow"
        Width="525"
        Height="350">
    <StackPanel>
        <Button cal:Message.Attach="ChangeSomething">Change Something</Button>
        <Button Content="{Binding Something.Id, FallbackValue=Foo}">
            <Button.ContextMenu>
                <ContextMenu IsEnabled="True">
                    <MenuItem Header="{Binding Id}" cal:Action.Target="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.Something}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="Click">
                                <cal:ActionMessage MethodName="DoSomething">
                                    <cal:Parameter Value="{Binding Id}"></cal:Parameter>
                                </cal:ActionMessage>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </MenuItem>
                </ContextMenu>
            </Button.ContextMenu>
        </Button>
    </StackPanel>
</Window>
using System;
using System.ComponentModel;
using Caliburn.Micro;

namespace CaliburnTestBed
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : INotifyPropertyChanged {
        private Something something;

        public MainWindow() {
            LogManager.GetLog = t => new DebugLog(t);
            InitializeComponent();
            DataContext = this;
            //Something = new Something();
        }

        public Something Something {
            get { return something; }
            set {
                something = value;
                if (PropertyChanged != null) {
                    PropertyChanged(this, new PropertyChangedEventArgs("Something"));
                }
            }
        }

        public void ChangeSomething() {
            Something = new Something();
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class Something {
        public Something() {
            Id = Guid.NewGuid();
        }

        public Guid Id { get; set; }

        public void DoSomething(Guid id) {
            if (Id != id) {
                Console.WriteLine("DoSomething({0}) was called on Something with Id {1} - this shouldn't happen!!!", id, Id);
            }
        }

        public override string ToString() {
            return "Something:" + Id;
        }
    }
}
Jul 8, 2014 at 3:46 PM
Trivial solution: The bootstrapper wasn't correctly setup causing CM to work in "DesignMode" and not properly binding to the target. I think older CM versions didn't assume DesginMode by default.
Marked as answer by etobi on 7/8/2014 at 7:46 AM