Search code examples
c#caliburn.microninject-interception

Add ViewModel to View type mapping for intercepted types


I'm using Caliburn Micro and Ninject together in a WPF app. I'm using Ninject.Extensions.Interception to intercept and log calls to my ViewModels, however this is preventing Caliburn to locate the view for ViewModels because Caliburn is being handed the intercepted viewModel (proxy) which is of a different namespace and name.

For instance, instead of getting:

MyApp.ViewModels.OrdersViewModel

it is getting:

Castle.Proxies.IOrdersProxy

To complicate things even further, ViewModels and Views are located in different assemblies, so namespace cannot be assumed.

Since I need to interrogate the proxy at runtime I need a way to hook into the View resolution programatically rather than by adding string mappings. I do not see any way to do this - is it possible with Calliburn Micro?


Solution

  • OK I got it. Instead of fighting the mappings, I am unwrapping the proxy in the WindowManager instead:

    using System;
    using System.Collections.Generic;
    using Caliburn.Micro;
    using Castle.DynamicProxy;
    
    namespace CAM.Utility
    {
        public class WindowManagerEx : WindowManager, IWindowManagerEx
        {
            private readonly List<object> _trayIcons;
    
            public WindowManagerEx()
            {
                _trayIcons = new List<object>();
            }
    
            public override bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null)
            {
                rootModel = rootModel.UnwrapProxy();
    
                return base.ShowDialog(rootModel, context, settings);
            }
    
            public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
            {
                rootModel = rootModel.UnwrapProxy();
    
                base.ShowWindow(rootModel, context, settings);
            }
    
            public override void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null)
            {
                rootModel = rootModel.UnwrapProxy();
    
                base.ShowPopup(rootModel, context, settings);
            }
    
            public bool? ShowDialog<TViewModel>(object context = null, IDictionary<string, object> settings = null)
            {
                var viewModelInstance = IoC.Get<TViewModel>();
    
                return ShowDialog(viewModelInstance, context, settings);
            }
    
            public void ShowWindow<TViewModel>(object context = null, IDictionary<string, object> settings = null)
            {
                var viewModelInstance = IoC.Get<TViewModel>();
    
                ShowWindow(viewModelInstance, context, settings);
            }
    
            public void ShowPopup<TViewModel>(object context = null, IDictionary<string, object> settings = null)
            {
                var viewModelInstance = IoC.Get<TViewModel>();
    
                ShowPopup(viewModelInstance, context, settings);
            }
    
            public void ShowTrayIcon<TViewModel>(object context = null)
            {
                var viewModelInstance = IoC.Get<TViewModel>();
    
                _trayIcons.Add(viewModelInstance);
            }
    
            public void Dispose()
            {
                foreach (var trayIcon in _trayIcons)
                {
                    var disposable = trayIcon as IDisposable;
                    if (disposable != null)
                    {
                        disposable.Dispose();
                    }
                }
            }
        }
    
        public static class CastleProxyExtensions
        {
            public static object UnwrapProxy(this object target)
            {
                var proxy = target as IProxyTargetAccessor;
    
                if (proxy != null)
                {
                    dynamic dynamicProxy = proxy;
    
                    dynamic interceptors = dynamicProxy.__interceptors;
    
                    dynamic interceptor = interceptors[0];
    
                    target = interceptor.Instance;
                }
    
                return target;
            }
        }
    }
    

    Also in case you are wondering what IWindowManagerEx is:

    public interface IWindowManagerEx : IWindowManager, IDisposable
    {
        bool? ShowDialog<TViewModel>(object context = null, IDictionary<string, object> settings = null);
    
        void ShowWindow<TViewModel>(object context = null, IDictionary<string, object> settings = null);
    
        void ShowPopup<TViewModel>(object context = null, IDictionary<string, object> settings = null);
    
        void ShowTrayIcon<TViewModel>(object context = null);
    }