Blog Archive

Tuesday, July 17, 2012

WPF Prism MVVM using Unity and Mef

Summary: We need to Unity/Mef Bootstrap into Shell as Top Window, where two things
happen: the normal WPF Window.Show() and enable Dependency Injection (DI) as early
 as possible using UnityContainer.Resolve<Shell> or 
CompositeContainer.GetExportTypes<>. Note that basic services like
 EventAggregator are constructed by Unity/Mef and will be passed along through 
Container/Aggregate Catalog.
Note that WPF DataContext is set in ShellWindos Constructor for Unity and DInjected
 for Mef through [Import].


bootstrapper.cs

    #region Unity Bootstrapper

    class DemoUnityBootstrapper : UnityBootstrapper
    {

        protected override System.Windows.DependencyObject CreateShell()
        {
            return  Container.Resolve<ShellWindow>();  // must be resolved by Container to do DI
        }
        protected override void InitializeShell()
        {
            (Application.Current.MainWindow =(Window) Shell).Show();
        }

    }

    #endregion

    #region Mef Bootstrapping

    class DemoMefBootstrapper : MefBootstrapper
    {

        protected override System.Windows.DependencyObject CreateShell()
        {
            return new ShellWindow2(Container,AggregateCatalog);  //avoid DI [Export] on ShellWindow2
        }

        protected override void InitializeShell()
        {
            (Application.Current.MainWindow = (Window)Shell).Show();
        }

    }

    #endregion

App Startup

        protected override void OnStartup(StartupEventArgs e)
        {
                DemoUnityBootstrapper dub = new DemoUnityBootstrapper();
                dub.Run(true);

                DemoMefBootstrapper dmb = new DemoMefBootstrapper();
                dmb.Run(true);           
        }


Shell as Window

        public ShellWindow(IUnityContainer container)  //DI
        {
            InitializeComponent();
            // again, DI require Container.Resolve.
            var vm = container.Resolve<PrismDemo.MVVM.ViewModel>();
            var view = container.Resolve<PrismDemo.MVVM.View>();

            view.DataContext = vm;
            cc.Content = view;
        }

        public ShellWindow2(CompositionContainer c, AggregateCatalog a) //not DI
        {
            InitializeComponent();
           
            a.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            IView v2 = c.GetExportedValue<IView>();  // invoke  Import-Export matching on V
            cc.Content = v2;
        }

View for Mef only, Unity has no code

    [Export(typeof(IView))]  // for Container.GetExport
    public partial class View2 : UserControl , IView
    {
        public View2()
        {
            InitializeComponent();
        }

        [Import]  // Import will Match Export of VM2
        public ViewModel2 ViewModel
        {
            set { this.DataContext = value; }
        }

        // declare which view is it for Modularity
        public string ViewName
        {
            get
            {
                return "View2";
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    }

Xaml
    <Grid>
       <StackPanel>
        <TextBlock Background="#FF15ABB7">MVVM View, Data binding</TextBlock> 
        <TextBox Text="{Binding Path=SingleValue, Mode=TwoWay}" />
        </StackPanel>
    </Grid>


ViewMode

    public class ViewModel : INotifyPropertyChanged
    {
        IEventAggregator _eventAggregator;
        public ViewModel(IEventAggregator ea)  //Unity Ctor DI
        {
            _eventAggregator = ea;
        }

        string _SingleValue="test";
        public string SingleValue
        {
            get { return _SingleValue;}
            set 
            {
                _SingleValue=value;
                PropertyChanged(this, new PropertyChangedEventArgs("SingleValue"));
                Debug.Assert(_eventAggregator != null);  // test Event Aggregator get DInjected
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    [Export(typeof(ViewModel2))]
    public class ViewModel2 : NotificationObject 
    {
        [Import]  // Exported by Mef base Aggregate Catalog
        public IEventAggregator ea { get; set; }

        IEventAggregator propEa;
        [ImportingConstructor]
        public ViewModel2(IEventAggregator ea2)
        {
            propEa = ea2;
        }


        string _SingleValue = "test";
        public string SingleValue
        {
            get { return _SingleValue; }
            set
            {
                _SingleValue = value;
                RaisePropertyChanged("SingleValue");
                Debug.Assert(ea != null & propEa != null); // test Event Aggregator get DInjected as Ctor and Prop
            }
        }
    }




No comments: