6
public class StatisticsViewPresenter 
{ 
    private IStatisticsView view; 
    private Statistics statsModel; 

    public StatisticsViewPresenter(IStatisticsView view, Statistics statsModel) 
    { 
     this.view = view; 
     this.statsModel = statsModel; 
    } 
} 

Non faccio uso di eventi (ma sono disposto a se può risolvere il mio problema), quindi le mie classi di visualizzazione simile a questa:Come applicare l'iniezione delle dipendenze alle viste UserControl mantenendo al contempo la Designer?

public class StatisticsForm : Form, IStatisticsView 
{ 
    public StatisticsForm() 
    { 
     InitializeComponent(); 
    } 

    [Inject] 
    public StatisticsViewPresenter Presenter 
    { 
     private get; 
     set; 
    } 
} 

Con

kernel.Bind<StatisticsPresenter>().ToSelf().InSingletonScope(); 
kernel.Bind<IStatisticsView>().To<StatisticsForm>(); 
kernel.Get<IStatisticsView>(); 

si accumula Form, crea il relatore, quindi inserisce il presentatore nella proprietà Presenter. È tutto peachy. (Ad eccezione di quel presentatore con ambito singleton - qualsiasi idea su un modo migliore per farlo? Forse semplicemente iniettare manualmente il presentatore nella proprietà Presenter della vista all'interno del costruttore del presentatore: this.view.Presenter = this).

Ma se trasformo StatisticsForm in StatisticsUserControl e lo trascino nel mio MainForm, non viene iniettato in MainForm da Ninject, semplicemente è stato creato da Designer. Vedo tre soluzioni qui:

1) Non utilizzare UserControls e utilizzare solo un modulo gigante che implementa queste viste multiple (eww);

2) Iniettare UserControls nel mio modulo e perdere il supporto di Designer;

3) La vostra soluzione! :)

+1

Qual è stata la soluzione finale? Sto anche cercando un modo per utilizzare UserControls nella finestra di progettazione e utilizzare anche l'iniezione delle dipendenze. – Nathan

+0

Fateci sapere se avete trovato una soluzione fattibile. Grazie! –

+0

Fondamentalmente, ho utilizzato la finestra di progettazione per trascinare i controlli utente nel modulo e esposto questi controlli come proprietà del modulo. Quindi vicino al punto di ingresso dell'applicazione lego i valori di queste proprietà alla vista. Quindi, quando il contenitore (io uso Autofac ora) ha bisogno di un oggetto del tipo di vista (per il presentatore della vista), carica semplicemente il valore della proprietà della Forma. Nota a margine: utilizzo anche eventi e non esporre più il Presenter alla vista, per eliminare l'accoppiamento non necessario della vista al presentatore. La vista non dovrebbe sapere che c'è un presentatore. –

risposta

2

Questa è sicuramente un'area interessante, dovrei dire, ricerca. Ci siamo fatti una soluzione dove ospitiamo i controlli utente in una forma generica.

Il nostro modulo generico non è destinato all'uso con il Designer. Attraverso il codice aggiungiamo dinamicamente il controllo utente scelto al modulo.

Per altri framework è necessario consultare Prism/Composite dal gruppo Pratiche Microsoft &. Here's an article discutendo le estensioni per WinForms.

2

Il mio approccio da utilizzare Ninject con forme, usercontrols e il progettista è:

  • Utilizzare fabbriche per creare le forme (anche per i controlli utente se si crea alcuni controlli dinamicamente)
  • per i UserControls e la i moduli mantengono i costruttori senza parametri e usano l'iniezione di proprietà
  • aggiungi un Activation strategy al kernel che verifica se ninject ha appena creato un modulo o un controllo utente. In questo caso, la strategia di attivazione esegue iterazioni sui controlli della proprietà Controls di UserControl (o del Form) e chiama Kernel.Inject (UserControl) per ogni controllo utente. (Una strategia di attivazione è un codice che ninject esegue dopo aver iniettato un oggetto)

È possibile utilizzare il progettista e disporre di moduli e comandi utente con dipendenze iniettate tramite Ninject.

L'unico inconveniente è che si deve utilizzare l'iniezione di proprietà, invece di iniezione costruttore per i UserControls (e le forme)

namespace Majiic.Ninject 
{ 
public class WindowsFormsStrategy : ActivationStrategy 
{ 
    // Activate is called after Kernel.Inject 
    //even for objects not created by Ninject 
    //To avoid multiple "injections" in the same nested controls 
    //we put this flag to false. 
    private bool _activatingControls = false; 
    public override void Activate(IContext context, InstanceReference reference) 
    { 
     reference.IfInstanceIs<UserControl>(uc => 
     { 
      if (!_activatingControls) 
      { 
       Trace.TraceInformation("Activate. Injecting dependencies in User control of type {0}", uc.GetType()); 
       _activatingControls = true; 
       context.Kernel.InjectDescendantOf(uc); 
       _activatingControls = false; 
      } 
     }); 
     reference.IfInstanceIs<Form>(form => 
     { 
      if (!_activatingControls) 
      { 
       Trace.TraceInformation("Activate. Injecting dependencies in Form of type {0}", form.GetType()); 
       _activatingControls = true; 
       context.Kernel.InjectDescendantOf(form); 
       _activatingControls = false; 
      } 
     }); 
    } 


} 
} 

Crea il kernel e aggiungere la strategia di attivazione

var kernel=new StandardKernel(new CommonMajiicNinjectModule()); 
kernel.Components.Add<IActivationStrategy, WindowsFormsStrategy>(); 

kernel estensioni per iterare su controlli discendenti

namespace Majiic.Ninject 
{ 
static public class WinFormsInstanceProviderAux 
{ 
    static public void InjectDescendantOf(this IKernel kernel, ContainerControl containerControl) 
    { 
     var childrenControls = containerControl.Controls.Cast<Control>(); 
     foreach (var control in childrenControls) 
     { 
      InjectUserControlsOf(kernel, control); 
     } 
    } 

    static private void InjectUserControlsOf(this IKernel kernel, Control control) 
    { 
     //only user controls can have properties defined as n-inject-able 
     if (control is UserControl) 
     { 
      Trace.TraceInformation("Injecting dependencies in User Control of type {0}", control.GetType()); 
      kernel.Inject(control); 
     } 
     //A non user control can have children that are user controls and should be n-injected 
     var childrenControls = control.Controls.Cast<Control>(); 
     foreach (var childControl in childrenControls) 
     { 
      InjectUserControlsOf(kernel, childControl); 
     } 
    } 
} 
} 
1

I r ecly creato alcuni riutilizzabili UserControls con la necessità di Dipendenze da iniettare. Siccome lo IoC Container non viene utilizzato per creare quelli UserControl s, ovviamente non può iniettare automaticamente le dipendenze.

La mia soluzione è una classe base per abilitare almeno l'iniezione di proprietà. Costruttore Iniezione non è supportato, in quanto un Costruttore senza parametri viene utilizzato per creare tali istanze.

public class NinjectUserControl : UserControl 
{ 

    // Generally this is considered to be a bad practice, 
    //however I didn't find any better way. If you do, please share :) 
    public static IKernel Kernel { private get; set; } 

    protected override void OnInitialized(EventArgs e) 
    { 
     base.OnInitialized(e); 
     RequestActivation(Kernel); 
    } 

    protected virtual void RequestActivation(IKernel kernel) 
    { 
     kernel?.Inject(this); 
    } 
} 

Per farlo funzionare è necessario impostare il kernel una volta. In genere questo è da qualche parte all'interno del vostro program.cs (WinForms) o App.xaml.cs (WPF)

IocKernel = new StandardKernel(); // typically a static member 
NinjectUserControl.Kernel = IocKernel; 
IocKernel.Load(new Module()); // loading modules 
// .. Create MainForm or whatever 

da usare, basta ereditare NinjectUserControl e poi lasciare che il kernel Iniettare le dipendenze tramite iniezione Proprietà:

[Inject] 
public IService Service { private get; set; } 

prega nota che tali dipendenze non sono accessibili all'interno del Costruttore.

+0

Si noti che il Framework I utilizzato per implementare questa soluzione è 'WPF', tuttavia questo dovrebbe funzionare in modo abbastanza simile usando' WinForms' – LuckyLikey