2010-04-30 13 views
5

Wir verwenden Microsoft Unity und Abhängigkeitsinjektion und so haben wir parametrisierten Konstruktor für die Benutzersteuerung. Wie kann diese Abhängigkeit in usercontrol mit XAML injiziert werden?Wpf usercontrol mit parametrisiertem Konstruktor

Ich habe die Benutzersteuerung in XAML wie folgt hinzugefügt.

xmlns:usrRefundArrivalProcessor="Ttl.Refunds.Wpf.Dashboad.Application.Usercontrols;assembly=Ttl.Refunds.Wpf.Dashboad.Application" 

Antwort

10

Die Abhängigkeitsinjektion impliziert keine parametrisierten Konstruktoren. Wenn Sie sich die Samples ansehen, die mit Unity geliefert werden, erfolgt der Großteil der Abhängigkeitsinjektion durch Eigenschaften mit dem [Dependency] -Attribut.

Unity funktioniert sehr gut mit XAML, aber nur, wenn Sie keine parametrisierten Konstruktoren verwenden. Konvertieren Sie Ihr UserControl, um seine Abhängigkeiten mithilfe von Eigenschaften mit dem Attribut [Dependency] zu übernehmen, und Sie können XAML problemlos verwenden.

public class MyUserControl : UserControl 
{ 
    [Dependency] 
    public ISomething Something { get; set; } 

    [Dependency] 
    public IWhatever Whatever { get { return (IWhatever)GetValue(WhateverProperty); } set { SetValue(WhateverProperty, value); } 
    public readonly DependencyProperty WhateverProperty = DependencyProperty.Register("Whatever", typeof(IWhatever), typeof(MyUserControl)); 

    ... 
} 

Beachten Sie, dass eine [Abhängigkeit] Eigenschaft kann entweder als DependencyProperty oder als einfache CLR-Eigenschaft deklariert werden, wie oben gezeigt. Das klingt nach einer verwirrenden Nomenklatur, ist aber in der Praxis sehr einfach. PropertyChangedCallback ruft einfach Anhaftungen an dem angegebenen Container

Um die UnityContainer in XAML angeben und die automatische Konfiguration erhalten, nur eine vererbte angefügten Eigenschaft „UnityHelper.Container“, deren erstellen und leitet in dem Objekttyp und das Objekt:

public class UnityHelper 
{ 
    public static IUnityContainer GetContainer(DependencyObject obj) { return (IUnityContainer)obj.GetValue(ContainerProperty); } 
    public static void SetContainer(DependencyObject obj, IUnityContainer value) { obj.SetValue(ContainerProperty, value); } 
    public static readonly DependencyProperty ContainerProperty = DependencyProperty.RegisterAttached("Container", typeof(IUnityContainer), typeof(UnityHelper), new FrameworkPropertyMetadata 
    { 
    Inherits = true, 
    PropertyChangedCallback = (obj, e) => 
    { 
     var container = e.NewValue as IUnityContainer; 
     if(container!=null) 
     { 
     var element = obj as FrameworkElement; 
     container.BuildUp(obj.GetType(), obj, element==null ? null : element.Name); 
     } 
    } 
    }); 
} 

Jetzt können Sie einen UnityContainer zu Ihrem root-Fenster zuweisen und Ihre gesamte Anwendung wird es verwenden, zum Beispiel Sie es in Ihrem Fenster Konstruktor tun könnte wie folgt:

UnityHelper.SetContainer(this, new UnityContainer() ...); 

Oder Sie können zuweisen XAML an einer beliebigen Ebene des Baums der Behälter Einheit mit:

<UserControl ... 
    my:UnityHelper.Container="{DynamicResource MainUnityContainer}" /> 

alles, was gesagt hat, ich glaube, Sie werden feststellen, dass WPF fortschrittliche Datenbindung Features und Ressourcen Wörterbücher zusammen 98% der Gründe zu beseitigen, warum ein Die Person möchte möglicherweise zuerst Unity verwenden. Sie können es auf lange Sicht besser finden, sich von Unity zu entfernen und mit einfachem MVVM zu arbeiten. Zumindest würde ich reines MVVM in einer Testanwendung ausprobieren, um zu sehen, wie es funktioniert, bevor viel Code entwickelt wird, der auf Unity für die Abhängigkeitsinjektion angewiesen ist.

+0

es funktioniert, aber wenn ich auf die Schaltfläche Tab getroffen von einem Textfeld wirft es Ausnahme Auflösung der Abhängigkeit ist fehlgeschlagen, type = „System.Windows.Input.KeyboardNavigation + FocusVisualAdorner“ name = „“. Ausnahmebedingungsnachricht lautet: Die aktuelle Erstellungsoperation (Buildschlüssel Erstellungsschlüssel [System.Windows.Input.KeyboardNavigation + FocusVisualAdorner, null]) ist fehlgeschlagen: Der Typ 'System.Windows.Input.KeyboardNavigation' konnte nicht von der Assembly 'PresentationFramework, Version = 3.0' geladen werden .0.0, Kultur = neutral, PublicKeyToken = 31bf3856ad364e35 '. (Strategie Typ BuildPlanStrategy, Index 5) – Miral

+0

Diese Antwort hat mir enorm geholfen. Ich wünschte, ich könnte mehr als +1 geben. Ich habe diesen Thread zu "Meine Favoriten" hinzugefügt, aber das wird nur dem Typen gutgeschrieben, der die ursprüngliche Frage gestellt hat. Also vielen Dank! – Tormod

+0

Wenn Sie eine Schnittstelle erstellen (IUnityElement genannt), die Sie auf alle Benutzersteuerelemente oder Steuerelemente anwenden, für die Sie Unity ausführen müssen, können Sie in der Zeile 'if (container! = Null)' überprüfen, ob der Parameter obj dieser Schnittstelle entspricht z.B 'if (container! = null && obj ist IUnityInject)'. Dies beseitigt den oben erwähnten Fehler Miral (ich habe einen ähnlichen von einem anderen Objekt und das Performance-Problem von ZeePrime bekommen, da das BuildUp nur auf FrameworkElements ausgeführt wird, für die es benötigt wird. –

0

Scheint ziemlich einfach. Warum fügen Sie Ihrem UserControl nicht einfach einen öffentlichen parameterlosen Konstruktor hinzu? Sie können wählen, es nicht direkt in Ihrem Code zu verwenden, aber das ist, wonach der Designer sucht. Wenn Sie sicherstellen möchten, dass der Code nie aufgerufen wird, geben Sie a check for presence of Designer ein und lösen Sie eine Ausnahme aus, wenn Designer nicht erkannt wird.

0

Es funktioniert, aber die Leistung ist ziemlich schlecht, wenn Sie Raster oder komplexe WPF-Bildschirme verwenden. Inherits = true bedeutet, dass für alle erstellten GUI-Elemente das PropertyChangedCallback der angefügten Eigenschaft unmittelbar nach dem Konstruktor aufgerufen wird, selbst wenn dieses bestimmte GUI-Element überhaupt Unity nicht benötigt. Und wenn Sie Grids haben, werden jedes Mal, wenn eine neue Zeile erscheint, alle GUI-Elemente im laufenden Betrieb erstellt, wobei der Callback aufgerufen wird. Das war die Leistung unseres Gitters, also mussten wir UnityInjection auf diese Weise entfernen.

Aber trotzdem gibt es Orte, wo wir UnityInjection in Codebehind benötigen, und wir haben keine schöne Lösung für dieses Problem gefunden, auch wenn wir MVVM verwenden. Einige komplexe Benutzersteuerelemente wurden mit MVVM entwickelt, wobei das ViewModel des Steuerelements vom Codebehind des Steuerelements selbst erstellt wird, das vom XAML erstellt wird, das das Steuerelement verwendet. In diesem speziellen Fall weiß das ViewModel des Controls (noch) nichts über Unity, aber wir brauchen die Einheit Injection, um auf dort registrierte externe Ressourcen zuzugreifen.