2010-10-26 8 views
1

Ma compréhension est que Silverlight ne prend pas en charge DataTemplates avec un attribut DataType. Comment alors accompliriez-vous ce qui suit dans SL (l'auteur est Josh Smith, lien complet ci-dessous). En un mot, il dit que si vous liez les pages à onglet TabControl à une collection de ViewModels, WPF découvrira comment les afficher à la volée en recherchant un DataTemplate ayant l'ensemble DataType approprié (correspondant). Bien cool, mais je me demande comment vous pourriez (pourriez?) Le faire dans Silverlight.Modèles de données typés dans Silverlight

application en vue d'une ViewModel

MainWindowViewModel ajoute indirectement et supprime les objets WorkspaceViewModel vers et à partir TabControl de la fenêtre principale. En s'appuyant sur la liaison de données , la propriété Content d'un TabItem reçoit un objet dérivé de ViewModelBase à l'affichage . ViewModelBase n'est pas un élément de l'interface utilisateur , donc il n'a pas de support inhérent pour le rendu lui-même. Par défaut, dans WPF un objet non-visuel est rendu par affichant les résultats d'un appel à sa méthode ToString dans un TextBlock. Ce n'est clairement pas ce dont vous avez besoin, sauf si vos utilisateurs ont un désir de gravure de voir le nom de type de nos classes ViewModel !

Vous pouvez facilement indiquer à WPF comment rendre un objet ViewModel en utilisant des DataTemplates typés. Un DataTemplate typé ne possède pas de valeur de clé x: , mais sa propriété DataType est définie sur une instance de la classe de type . Si WPF tente de rendre un de vos objets ViewModel, il vérifier pour voir si le système de ressources a un DataTemplate tapé une portée dont DataType est le même que celui (ou une classe de base de) le type de votre ViewModel objet. S'il en trouve un, il utilise ce modèle pour afficher l'objet ViewModel référencé par la propriété Content de l'onglet . Le fichier MainWindowResources.xaml comporte un ResourceDictionary. Ce dictionnaire est ajouté à la hiérarchie de ressources de la fenêtre principale , ce qui signifie que les ressources qu'il contient se trouvent dans la portée de la fenêtre . Lorsqu'un onglet contenu de l'article est défini sur un objet ViewModel , un DataTemplate typé de ce dictionnaire fournit une vue (qui est un contrôle utilisateur ) pour le rendre, comme le montre la figure 10.in Figure 10.

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx à la figure 10.

Répondre

1

est ici une façon que vous pouvez le faire. J'ai utilisé une technique comme celle-ci dans le passé et j'ai eu beaucoup de succès avec ça.

Tenir compte d'un conteneur très simple qui permettra de créer la vue pour vous comme ceci:

public class ViewMapper : ContentControl 
{ 
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 
    { 
     base.OnPropertyChanged(e); 

     if (e.Property.Name == "DataContext") 
      WhenDataContextChanges(); 
    } 

    private void WhenDataContextChanges() 
    { 
     if (DataContext == null) 
      Content = null; 
     else 
      Content = ViewFactory.GetView(DataContext.GetType()); 
    } 
} 

EDIT

Ainsi, vous pouvez utiliser cette commande pour faire la mise en correspondance pour vous:

<Border DataContext="{Binding MyViewModel}"> 
    <ViewMapper /> 
</Border> 

END EDIT

Notez que ViewMapper attend simplement que le contexte de données change, recherche la vue appropriée pour le type de données et en crée une nouvelle. Elle repose sur ViewFactory, qui est une recherche statique très simple qui associe types de vues:

public class ViewFactory 
{ 
    private static readonly Dictionary<string, Func<UIElement>> _registry = new Dictionary<string, Func<UIElement>>(); 

    private static string Key(Type viewModelType) 
    { 
     return viewModelType.FullName; 
    } 

    public static void RegisterView(Type viewModelType, Func<UIElement> createView) 
    { 
     _registry.Add(Key(viewModelType), createView); 
    } 

    public static UIElement GetView(Type viewModelType) 
    { 
     var key = Key(viewModelType); 
     if (!_registry.ContainsKey(key)) 
      return null; 

     return _registry[key](); 
    } 
} 

, il vous suffit ensuite d'enregistrer les cartographies de vue un endroit:

ViewFactory.RegisterView(typeof(SomeViewModel),() => new SomeView()); 

Notez que ViewFactory pourrait tout aussi facilement utiliser Activator.CreateInstance au lieu d'utiliser le mécanisme Func. Faites un pas de plus, et vous pouvez utiliser un conteneur IoC ... Vous pouvez toujours décider de mapper via une chaîne Name la propriété sur le ViewModel au lieu d'un type ... les possibilités sont infinies et puissantes ici.

+0

C'est vraiment cool. Ai-je raison de supposer que cela fonctionne avec les contrôles utilisateur, et que lorsque vous créez votre contrôle utilisateur, vous devez hériter de ViewMapper pour qu'il sache quoi faire quand son DataContext change? C'est cool en soi, mais l'article ci-dessus vous permet de lier une source d'élément TabControl (ou autre) à une liste de ViewModels, et chacun sait comment afficher en fonction des modèles de données * typed *. Je me demandais juste si cela était possible dans SL, mais peut-être que je suis mal compris votre message –

+0

Notez ma modification. Si vous voulez utiliser 'ViewMapper', mettez-le en place là où vous voulez que le mapping se produise. Remarquez comment je mets un 'ViewMapper' dans un' Border'. Vous pouvez tout aussi bien le mettre à l'intérieur de votre onglet et obtenir le mappage des données. En fait, je préfère une méthode comme celle-ci au lieu de DataTyped DataTemplates car elle extrait le mappage de la vue et laisse la configuration. –

Questions connexes