Camron B
Camron B

Reputation: 1830

Castle Windsor interceptor blocking PropertyChanged events

I have created a test project as a POC for this problem.

I have a WPF app, that when we use interceptors around the view models, it's stopping the propagation of events. If I disable all interceptors, it works fine.

Here is the code:

MyInterceptor.cs

public class MyInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
    }
}

IoCTestViewModel.cs

public interface IIoCTestViewModel : INotifyPropertyChanged
{
    int Number { get; }
}

public class IoCTestViewModel : IIoCTestViewModel
{
    public IoCTestViewModel()
    {
        var timer = new Timer(200);
        timer.Elapsed += (a, b) => {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("Number"));
            }
        };
        timer.Start();
    }

    public int Number
    {
        get
        {
            return new Random().Next(1, 100);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

IoCTest.xaml.cs

public partial class IoCTest : UserControl
{
    public IIoCTestViewModel ViewModel { get; set; }

    public IoCTest(IIoCTestViewModel viewModel)
    {
        InitializeComponent();

        DataContext = viewModel;
    }
}

App.xaml (fragment)

        Container = new WindsorContainer();
        Container.Register(Component.For<MyInterceptor>().ImplementedBy<MyInterceptor>());
        Container.Register(Component.For<IIoCTestViewModel>().ImplementedBy<IoCTestViewModel>().Interceptors<MyInterceptor>());
        Container.Register(Component.For<IoCPage>().ImplementedBy<IoCTest>()); //IoCTest is a usercontrol

OK. So once I get an instance of IoCTest and add it to a page, I don't see any changes, even though I am sending PropertyChanged every 200ms. If I remove the interceptor, everything works fine.

So how do I fix this?

Upvotes: 2

Views: 249

Answers (1)

Phil Degenhardt
Phil Degenhardt

Reputation: 7264

The issue here is that because you declare your service to be IIoCTestViewModel, when you add an interceptor Windsor simply creates a dynamic proxy that delegates all calls to your implementation type. However, the interception is done using composition - one object delegating to another. Hence, when you raise your property changed event with a sender of this, it is a different object to the one that WPF thinks it is watching.

You should instead register your view model like this:

Container.Register(Component.For<IIoCTestViewModel,IoCTestViewModel>().Implemen‌​tedBy<IoCTestViewModel>().Interceptors<MyInterceptor>())

By specifying multiple services, one of which is actually your implementation class, Windsor will instead generate a class proxy - i.e. the interception will be done using inheritance, with the generated proxy inheriting from IoCTestViewModel. (This is known as type forwarding in Windsor). Now when you raise your event with a sender of this it correctly refers to the same instance that WPF is watching.

See here for a more detailed explanation of type forwarding and its implications for proxies

Upvotes: 1

Related Questions