Ticket T637294
Visible to All Users

Winforms MVVM, Autofac, and ViewModel Management

created 7 years ago

Hello Support,

Well, it's great to be back on Winforms with DevExpress' suite!

I have a question concerning how you intended IoC to work with your Winforms MVVM framework (in my case, I'm using Autofac).  Consider the following code:

C#
var builder = new Autofac.ContainerBuilder(); builder.RegisterType<ContactsViewModel>(); var container = builder.Build(); MVVMContextCompositionRoot.ViewModelCreate += (s, e) => { e.ViewModel = container.ResolveOptional(e.RuntimeViewModelType); }; Application.Run(new ShellView());

The reason I had to go with ResolveOptional is that every time a ViewModel is created, the RuntimeViewModelType is followed by an underscore and a Guid (e.g. "ContactsViewModel_8a190ac3_40c8_4888_9ed0_689a79a8682e"), which of course is not registered.  Should it not be RuntimeViewModelType.BaseType  (because that reports back as just "ContactsViewModel")?

Or am I misunderstanding your design intentions here?  Also, should I be resolving ViewModels, or everything but ViewModels?

Thank you!

Answers

created 7 years ago (modified 7 years ago)

I have figured this out, I believe.

For those of you thinking of using Autofac, it would appear on the surface that, at least with respect to hooking into ViewModels under DevExpress' MVVM, it is not compatible.  Autofac expects the entirety of the container to be configured upon building it.  We don't have that luxury in this case because DevExpress dynamically creates the ViewModel type at runtime (at least appears that this is the case for POCO ViewModels due to the fact that DevExpress subclasses our POCO ViewModels at runtime, thereby providing the features of MVVM).

So I switched to SimpleInjector and had great success.  Consider the following code from Program.cs:

C#
[STAThread] static void Main() { var container = new SimpleInjector.Container(); Bootstrap(container); //We don't register our ViewModels with the container: We simply retrieve a runtime instance based on //the runtime type supplied by DevExpress' MVVVM MVVMContextCompositionRoot.ViewModelCreate += (s, e) => { e.ViewModel = container.GetInstance(e.RuntimeViewModelType); }; Application.Run(new ShellView()); } private static void Bootstrap(SimpleInjector.Container container) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ConfigureDevExpress(); ConfigureIoC(container); } private static void ConfigureDevExpress() { WindowsFormsSettings.ForceDirectXPaint(); WindowsFormsSettings.DefaultFont = new System.Drawing.Font("Segoe UI", 9); UserLookAndFeel.Default.SetSkinStyle(SkinSvgPalette.Bezier.Fireball); } private static void ConfigureIoC(SimpleInjector.Container container) { container.Register<IUiSession, ContactsSession>(Lifestyle.Singleton); }

To my other question considering what, exactly, to register, no, it would appear that we do not need to register the ViewModel types with the container.  Since DevExpress' MVVM framework is dynamically providing that type, we couldn't anyways.  SimpleInjector has a #GetInstance method on its container, which works great for this scenario.  I was unable to match this feature in the Autofac container.

All types other than ViewModel types should be registered with the container in the usual way; hence, the my #ConfigureIoC method.

I would say to DevExpress staff, please let me know if my conclusions and/or approaches are faulty.

Thank you.

[UPDATED BY Sasha (DevExpress Support)]:

This task can be completed using AutoFac as follows:

C#
// Build dependencies var rootBuilder = new ContainerBuilder(); rootBuilder.RegisterType<Sword>().As<IWeapon>(); rootContainer = rootBuilder.Build(); // Configure MVVM Composition MVVMContextCompositionRoot.ViewModelCreate += (s, e) => { using (var scope = rootContainer.BeginLifetimeScope( b => b.RegisterType(e.RuntimeViewModelType).As(e.ViewModelType))) { e.ViewModel = scope.Resolve(e.ViewModelType); } };

Find a small sample that illustrates this approach in the attachment.

    Show previous comments (2)
    Sasha (DevExpress Support) 7 years ago

      Hi,

      Thank you for the clarification. This approach should work correctly for the root context associated with the static event as well. As the Update method is obsolete, you can use approaches described in the Discussion: ContainerBuilder.Update Marked Obsolete thread instead of it:

      C#
      // Build dependencies var rootBuilder = new ContainerBuilder(); rootBuilder.RegisterType<Sword>().As<IWeapon>(); rootContainer = rootBuilder.Build(); // Configure MVVM Composition MVVMContextCompositionRoot.ViewModelCreate += (s, e) => { using (var scope = rootContainer.BeginLifetimeScope( b => b.RegisterType(e.RuntimeViewModelType).As(e.ViewModelType))) { e.ViewModel = scope.Resolve(e.ViewModelType); } };

      Please test this approach and let me know if it helps.

        Hi Sasha,

        Wow! That’s brilliant!  And, yes, this will work for me.  There are no apparent problems.  If anything surfaces, I’ll reopen the ticket.

        Thanks a bunch!

        Sasha (DevExpress Support) 7 years ago

          Hi,

          I am happy to hear that this approach works for you. Thank you for informing us.
          I updated your answer with this information.

          Please feel free to contact us in case of further difficulties.

          Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

          Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.