Castle Windsor 拦截器阻止 PropertyChanged 事件
Castle Windsor interceptor blocking PropertyChanged events
我已经创建了一个测试项目作为这个问题的 POC。
我有一个 WPF 应用程序,当我们在视图模型周围使用拦截器时,它会停止事件的传播。如果我禁用所有拦截器,它工作正常。
代码如下:
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
好的。因此,一旦我获得 IoCTest 的实例并将其添加到页面,我就看不到任何更改,即使我每 200 毫秒发送一次 PropertyChanged
。如果我删除拦截器,一切正常。
那么我该如何解决这个问题?
这里的问题是,因为您将服务声明为 IIoCTestViewModel
,当您添加拦截器时,Windsor 只是创建一个动态代理,将所有调用委托给您的实现类型。但是,拦截是使用组合完成的——一个对象委托给另一个对象。因此,当您使用 this
的发件人引发 属性 更改事件时,它与 WPF 认为它正在监视的对象是不同的对象。
您应该像这样注册您的视图模型:
Container.Register(Component.For<IIoCTestViewModel,IoCTestViewModel>().ImplementedBy<IoCTestViewModel>().Interceptors<MyInterceptor>())
通过指定多个服务,其中一个实际上是您的实现 class,Windsor 将生成一个 class 代理 - 即拦截将使用继承完成,生成的代理继承自IoCTestViewModel
。 (这在 Windsor 中称为类型转发)。现在,当您使用 this
的发件人引发事件时,它正确地引用了 WPF 正在监视的同一实例。
有关类型转发及其对代理的影响的更详细说明,请参阅 here
我已经创建了一个测试项目作为这个问题的 POC。
我有一个 WPF 应用程序,当我们在视图模型周围使用拦截器时,它会停止事件的传播。如果我禁用所有拦截器,它工作正常。
代码如下:
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
好的。因此,一旦我获得 IoCTest 的实例并将其添加到页面,我就看不到任何更改,即使我每 200 毫秒发送一次 PropertyChanged
。如果我删除拦截器,一切正常。
那么我该如何解决这个问题?
这里的问题是,因为您将服务声明为 IIoCTestViewModel
,当您添加拦截器时,Windsor 只是创建一个动态代理,将所有调用委托给您的实现类型。但是,拦截是使用组合完成的——一个对象委托给另一个对象。因此,当您使用 this
的发件人引发 属性 更改事件时,它与 WPF 认为它正在监视的对象是不同的对象。
您应该像这样注册您的视图模型:
Container.Register(Component.For<IIoCTestViewModel,IoCTestViewModel>().ImplementedBy<IoCTestViewModel>().Interceptors<MyInterceptor>())
通过指定多个服务,其中一个实际上是您的实现 class,Windsor 将生成一个 class 代理 - 即拦截将使用继承完成,生成的代理继承自IoCTestViewModel
。 (这在 Windsor 中称为类型转发)。现在,当您使用 this
的发件人引发事件时,它正确地引用了 WPF 正在监视的同一实例。
有关类型转发及其对代理的影响的更详细说明,请参阅 here