2010-04-30 8 views
5

Nous utilisons Microsoft Unity et l'injection de dépendances et nous avons donc un constructeur paramétré pour l'usercontrol. Comment injecter cette dépendance dans usercontrol en utilisant XAML?Wpf usercontrol avec constructeur paramétré

J'ai ajouté la commande usercontrol en XAML comme ci-dessous.

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

Répondre

10

L'injection de dépendances n'implique pas de constructeurs paramétrés. En fait, si vous examinez les exemples fournis avec Unity, la plus grande partie de l'injection de dépendance est effectuée par des propriétés avec l'attribut [Dependency]. L'unité fonctionne très bien avec XAML, mais seulement si vous n'utilisez pas de constructeurs paramétrés. Convertissez votre UserControl pour prendre ses dépendances en utilisant des propriétés avec l'attribut [Dependency], et vous pouvez facilement utiliser XAML.

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)); 

    ... 
} 

Notez qu'une propriété [dépendance] peut être déclarée soit comme DependencyProperty ou comme une propriété CLR plaine, comme indiqué ci-dessus. Cela ressemble à une nomenclature confuse, mais en pratique, c'est très simple.

Pour spécifier le UnityContainer en XAML et obtenir la configuration automatique, il suffit de créer une joint propriété « UnityHelper.Container » héritée dont PropertyChangedCallback appelle simplement colmatages sur le conteneur spécifié et passe dans le type de l'objet et l'objet:

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); 
     } 
    } 
    }); 
} 

maintenant, vous pouvez attribuer un UnityContainer à votre fenêtre racine et votre application entière utiliser, par exemple, vous pouvez le faire dans le constructeur de la fenêtre comme suit:

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

Ou vous pouvez assigner le récipient d'unité à l'aide XAML à tout niveau souhaité de l'arbre:

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

Ayant dit tout cela, je pense que vous trouverez que les données avancées de WPF fonctions de liaison et les dictionnaires de ressources éliminent ensemble 98% des raisons pour lesquelles une personne pourrait vouloir utiliser Unity en premier lieu. Vous trouverez peut-être mieux à long terme pour s'éloigner de l'unité et aller avec MVVM simple. À tout le moins, j'essaierais MVVM pur sur une application de test pour voir comment cela fonctionne avant de développer beaucoup de code en se basant sur Unity pour l'injection de dépendance.

+0

cela fonctionne, mais quand je clique sur le bouton onglet d'un de la zone de texte, il émet une exception Résolution de la dépendance a échoué, type = « System.Windows.Input.KeyboardNavigation + FocusVisualAdorner », name = « ». Le message d'exception est: L'opération de génération en cours (clé de construction Build Key [System.Windows.Input.KeyboardNavigation + FocusVisualAdorner, null]) a échoué: Impossible de charger le type 'System.Windows.Input.KeyboardNavigation' à partir de l'assembly 'PresentationFramework, Version = 3.0 .0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35 '. (Type de stratégie BuildPlanStrategy, index 5) – Miral

+0

Cette réponse m'a énormément aidé. J'aimerais pouvoir donner plus de +1. J'ai ajouté ce fil à "Mes favoris", mais cela ne fait que créditer le gars qui pose la question originale. Alors merci! – Tormod

+0

Si vous créez une interface (par exemple appelée IUnityElement) que vous appliquez à tous vos UserControls ou Contrôles, vous devez exécuter unit sur alors dans la ligne 'if (container! = Null)' vous pouvez vérifier si le paramètre obj est de cette interface par exemple 'if (conteneur! = null && obj est IUnityInject)'. Ceci élimine l'erreur que Miral a commentée plus haut (j'en ai obtenu une similaire d'un objet différent et la question de performance est la réponse de ZeePrime, car BuildUp n'est exécuté que sur FrameworkElements dont vous avez besoin. –

0

Semble plutôt facile. Pourquoi ne pas simplement ajouter "un constructeur public sans paramètre" à votre UserControl? Vous pouvez choisir de ne pas l'utiliser directement dans votre code, mais c'est ce que le Designer recherche. Si vous voulez vous assurer qu'il n'est jamais appelé dans le code, mettez a check for presence of Designer et lancez une exception si Designer n'est pas détecté.

0

Cela fonctionne, mais les performances sont assez mauvaises lorsque vous utilisez des grilles ou des écrans WPF complexes. Inherits = true implique que tous les éléments GUI créés auront le PropertyChangedCallback de la propriété attachée appelé juste après le constructeur, même si cet élément GUI spécifique n'a pas besoin du tout d'Unity. Et quand vous avez des grilles, à chaque fois qu'une nouvelle ligne apparaît, tous les éléments de l'interface graphique sont créés à la volée, avec ce rappel appelé. cela a tué la performance de notre grille, nous avons donc dû supprimer UnityInjection fait de cette façon. Mais il y a néanmoins des endroits où nous avons besoin de UnityInjection dans codebehind, et nous n'avons pas trouvé une bonne solution pour ce problème, même si nous utilisons MVVM. Un contrôle utilisateur complexe est conçu à l'aide de MVVM, où le ViewModel du contrôle est créé par le codebehind du contrôle lui-même, créé par le XAML qui utilise le contrôle. Dans ce cas précis, le ViewModel du contrôle ne sait rien (encore) à propos de Unity, mais nous avons besoin de l'injection d'unité pour accéder aux ressources externes enregistrées.