2010-12-22 14 views
7

Esto es más un comentario que una pregunta, aunque los comentarios serían agradables. Me han encargado que cree la interfaz de usuario para un nuevo proyecto que estamos haciendo. Queremos usar WPF y quería aprender todas las técnicas de diseño de IU modernas disponibles. Como soy bastante nuevo en WPF, he estado investigando qué hay disponible. Creo que casi me he decidido por utilizar MVVM Light Toolkit (principalmente por su "Blendability" y el comportamiento de EventToCommand), pero también quería incorporar IoC. Entonces, esto es lo que se me ocurrió. He modificado la clase predeterminada ViewModelLocator en un proyecto MVVM Light para usar UnityContainer para manejar inyecciones de dependencia. Teniendo en cuenta que no sabía lo que significaba el 90% de estos términos hace 3 meses, creo que estoy en el camino correcto.Combinación de MVVM Light Toolkit y Unity 2.0

// Example of MVVM Light Toolkit ViewModelLocator class that implements Microsoft 
// Unity 2.0 Inversion of Control container to resolve ViewModel dependencies. 

using Microsoft.Practices.Unity; 

namespace MVVMLightUnityExample 
{ 
    public class ViewModelLocator 
    { 
     public static UnityContainer Container { get; set; } 

     #region Constructors 
     static ViewModelLocator() 
     { 
      if (Container == null) 
      { 
       Container = new UnityContainer(); 

       // register all dependencies required by view models 
       Container 
        .RegisterType<IDialogService, ModalDialogService>(new ContainerControlledLifetimeManager()) 
        .RegisterType<ILoggerService, LogFileService>(new ContainerControlledLifetimeManager()) 
        ; 
      } 
     } 

     /// <summary> 
     /// Initializes a new instance of the ViewModelLocator class. 
     /// </summary> 
     public ViewModelLocator() 
     { 
      ////if (ViewModelBase.IsInDesignModeStatic) 
      ////{ 
      //// // Create design time view models 
      ////} 
      ////else 
      ////{ 
      //// // Create run time view models 
      ////} 

      CreateMain(); 
     } 

     #endregion 

     #region MainViewModel 

     private static MainViewModel _main; 

     /// <summary> 
     /// Gets the Main property. 
     /// </summary> 
     public static MainViewModel MainStatic 
     { 
      get 
      { 
       if (_main == null) 
       { 
        CreateMain(); 
       } 
       return _main; 
      } 
     } 

     /// <summary> 
     /// Gets the Main property. 
     /// </summary> 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", 
      "CA1822:MarkMembersAsStatic", 
      Justification = "This non-static member is needed for data binding purposes.")] 
     public MainViewModel Main 
     { 
      get 
      { 
       return MainStatic; 
      } 
     } 

     /// <summary> 
     /// Provides a deterministic way to delete the Main property. 
     /// </summary> 
     public static void ClearMain() 
     { 
      _main.Cleanup(); 
      _main = null; 
     } 

     /// <summary> 
     /// Provides a deterministic way to create the Main property. 
     /// </summary> 
     public static void CreateMain() 
     { 
      if (_main == null) 
      { 
       // allow Unity to resolve the view model and hold onto reference 
       _main = Container.Resolve<MainViewModel>(); 
      } 
     } 

     #endregion 

     #region OrderViewModel 

     // property to hold the order number (injected into OrderViewModel() constructor when resolved) 
    public static string OrderToView { get; set; } 

     /// <summary> 
     /// Gets the OrderViewModel property. 
     /// </summary> 
     public static OrderViewModel OrderViewModelStatic 
     { 
      get 
      { 
       // allow Unity to resolve the view model 
       // do not keep local reference to the instance resolved because we need a new instance 
       // each time - the corresponding View is a UserControl that can be used multiple times 
       // within a single window/view 
       // pass current value of OrderToView parameter to constructor! 
       return Container.Resolve<OrderViewModel>(new ParameterOverride("orderNumber", OrderToView)); 
      } 
     } 

     /// <summary> 
     /// Gets the OrderViewModel property. 
     /// </summary> 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", 
      "CA1822:MarkMembersAsStatic", 
      Justification = "This non-static member is needed for data binding purposes.")] 
     public OrderViewModel Order 
     { 
      get 
      { 
       return OrderViewModelStatic; 
      } 
     } 
     #endregion 

     /// <summary> 
     /// Cleans up all the resources. 
     /// </summary> 
     public static void Cleanup() 
     { 
      ClearMain(); 
      Container = null; 
     } 
    } 
} 

Y la clase MainViewModel muestre el uso de la inyección de dependencias:

using GalaSoft.MvvmLight; 
using Microsoft.Practices.Unity; 

namespace MVVMLightUnityExample 
{ 
    public class MainViewModel : ViewModelBase 
    { 
     private IDialogService _dialogs; 
     private ILoggerService _logger; 

     /// <summary> 
     /// Initializes a new instance of the MainViewModel class. This default constructor calls the 
     /// non-default constructor resolving the interfaces used by this view model. 
     /// </summary> 
     public MainViewModel() 
      : this(ViewModelLocator.Container.Resolve<IDialogService>(), ViewModelLocator.Container.Resolve<ILoggerService>()) 
     { 
      if (IsInDesignMode) 
      { 
       // Code runs in Blend --> create design time data. 
      } 
      else 
      { 
       // Code runs "for real" 
      } 
     } 

     /// <summary> 
     /// Initializes a new instance of the MainViewModel class. 
     /// Interfaces are automatically resolved by the IoC container. 
     /// </summary> 
     /// <param name="dialogs">Interface to dialog service</param> 
     /// <param name="logger">Interface to logger service</param> 
     public MainViewModel(IDialogService dialogs, ILoggerService logger) 
     { 
      _dialogs = dialogs; 
      _logger = logger; 

      if (IsInDesignMode) 
      { 
       // Code runs in Blend --> create design time data. 
       _dialogs.ShowMessage("Running in design-time mode!", "Injection Constructor", DialogButton.OK, DialogImage.Information); 
       _logger.WriteLine("Running in design-time mode!"); 
      } 
      else 
      { 
       // Code runs "for real" 
       _dialogs.ShowMessage("Running in run-time mode!", "Injection Constructor", DialogButton.OK, DialogImage.Information); 
       _logger.WriteLine("Running in run-time mode!"); 
      } 
     } 

     public override void Cleanup() 
     { 
      // Clean up if needed 
      _dialogs = null; 
      _logger = null; 

      base.Cleanup(); 
     } 
    } 
} 

Y la clase OrderViewModel:

using GalaSoft.MvvmLight; 
using Microsoft.Practices.Unity; 

namespace MVVMLightUnityExample 
{ 
    /// <summary> 
    /// This class contains properties that a View can data bind to. 
    /// <para> 
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel. 
    /// </para> 
    /// <para> 
    /// You can also use Blend to data bind with the tool's support. 
    /// </para> 
    /// <para> 
    /// See http://www.galasoft.ch/mvvm/getstarted 
    /// </para> 
    /// </summary> 
    public class OrderViewModel : ViewModelBase 
    { 

     private const string testOrderNumber = "123456"; 
     private Order _order; 

     /// <summary> 
     /// Initializes a new instance of the OrderViewModel class. 
     /// </summary> 
     public OrderViewModel() 
      : this(testOrderNumber) 
     { 

     } 

     /// <summary> 
     /// Initializes a new instance of the OrderViewModel class. 
     /// </summary> 
     public OrderViewModel(string orderNumber) 
     { 
      if (IsInDesignMode) 
      { 
       // Code runs in Blend --> create design time data. 
       _order = new Order(orderNumber, "My Company", "Our Address"); 
      } 
      else 
      { 
       _order = GetOrder(orderNumber); 
      } 
     } 

     public override void Cleanup() 
     { 
      // Clean own resources if needed 
      _order = null; 

      base.Cleanup(); 
     } 
    } 
} 

Y el código que podría utilizarse para mostrar una vista para que un determinado orden:

public void ShowOrder(string orderNumber) 
{ 
    // pass the order number to show to ViewModelLocator to be injected 
    //into the constructor of the OrderViewModel instance 
    ViewModelLocator.OrderToShow = orderNumber; 

    View.OrderView orderView = new View.OrderView(); 
} 

Estos ejemplos se han eliminado para mostrar solo las ideas de IoC. Tomó muchas pruebas y errores, buscando ejemplos en Internet y descubriendo que falta la documentación de Unity 2.0 (en el código de muestra para las extensiones) para llegar a esta solución. Avísame si crees que podría mejorarse.

+0

Me encantaría saber dónde cree que hay agujeros en la documentación de Unity. ¿Qué más necesitabas que no estuviera allí? –

+0

La documentación de Unity es bastante buena, solo me costaba encontrar ejemplos de fragmentos de código para 2.0, especialmente para usar las extensiones. Había muchos códigos de ejemplo para 1.x, pero muchos estaban desactualizados (seguí recibiendo mensajes para no hacer cosas con esa sintaxis porque había una nueva sintaxis disponible con las extensiones). – acordner

+0

Ha pasado casi un año después de que publicaste esto por primera vez, acordner. Estoy empezando a luchar con las ideas que eras en ese entonces. Me gustaría saber dónde están las cosas, ya sabes, en lo que respecta a MVVM Light/Unity. – Rod

Respuesta

1

En primer lugar, debe evitar el localizador de servicio anti-patrón.

En segundo lugar, debe establecer una variable estática cada vez que desea obtener un OrderView? Es un diseño muy feo.

+0

Como dije, soy muy nuevo en esto y trato de abrirme camino. Estoy abierto no solo a la crítica, sino también a sugerencias sobre cómo mejorarla. Nunca antes había armado una aplicación completa que utilizara un patrón MVVM y me gustaría encontrar la mejor solución posible antes de llegar demasiado lejos. Me parece que dejar que otros desarrolladores más experimentados lo separen es una buena forma de encontrar oportunidades de mejora. Investigaré el "anti-patrón de localizador de servicio" (ya que no tengo idea de lo que eso significa en este momento) :) – acordner

+0

Acepto establecer una variable estática antes de obtener un OrderView no es una gran solución, pero hasta que descubra todo de las técnicas de anulación de parámetros y DI, eso es lo que se me ocurrió. – acordner

+0

Una mejor solución sería un patrón abstracto de fábrica que podría inyectarse. – onof

Cuestiones relacionadas