2009-08-02 4 views
10

Je deviens fou de liaison d'une zone de liste déroulante à une propriété typée enum d'une classe, où l'énumération elle-même est déclarée dans cette même classe.Liaison d'une zone de liste déroulante à une énumération imbriquée dans une classe

J'essaie de suivre la réponse fournie ici (wpf combobox binding to enum what i did wrong?) Spécifiquement j'utilise le code MarkupExtension suggéré et le code xaml correspondant.

Mon code de travail est:

Définition du Enum dans un fichier séparé.

namespace EnumTest 
{ 
    public enum TestEnum {one, two, three, four }; 
} 

classe qui utilise le Enum (Notez que le code propertyChanged a été supprimé pour simplifier les choses):

namespace EnumTest 
{ 
    public class Test : INotifyPropertyChanged 
    { 
     private TestEnum _MyVar; 
     public TestEnum MyVar { 
      get { return _MyVar; } 
      set 
      { 
       _MyVar = value; 
       OnPropertyChanged("MyVar"); 
      } 
     } 

     public Test() 
     { 
      _MyVar = TestEnum.three; 
     } 
    } 
} 

fichier programme qui utilise la classe:

namespace EnumTest 
{ 
    public partial class Window1 : Window 
    { 
     Test _oTest = new Test(); 

     public Window1() 
     { 
      InitializeComponent(); 
      cmbBox.DataContext = _oTest; 
     } 
    } 
} 

méthode d'extension pour l'affichage de l'énumération

namespace EnumTest 
{ 
    [MarkupExtensionReturnType(typeof(object[]))] 
    public class EnumValuesExtension : MarkupExtension 
    { 
     public EnumValuesExtension() 
     { 
     } 

     public EnumValuesExtension(Type enumType) 
     { 
      this.EnumType = enumType; 
     } 

     [ConstructorArgument("enumType")] 
     public Type EnumType { get; set; } 

     public override object ProvideValue(IServiceProvider serviceProvider) 
     { 
      if (this.EnumType == null) 
       throw new ArgumentException("The enum type is not set"); 
      return Enum.GetValues(this.EnumType); 
     } 
    } 
} 

Et le code XAML qui est utilisé pour afficher les données: d'être défini

<Window x:Class="EnumTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:w="clr-namespace:EnumTest" 
    Title="Window1" Height="300" Width="300"> 
    <Grid> 
     <ComboBox Name="cmbBox" 
        Height="20" 
        Width="80" 
        ItemsSource="{Binding Source={w:EnumValues EnumType=w:TestEnum}}" 
        SelectedItem="{Binding Path=MyVar}" 
        /> 
    </Grid> 
</Window> 

Ce qui précède est tout bon et dandy, mais je veux définir l'Enum au sein la classe d'essai et fossé ENUM au portée mondiale. Comme si:

namespace EnumTest 
{ 
    public class Test : INotifyPropertyChanged 
    { 
     // Declare Enum **INSIDE** the class 
     public enum TestEnum {one, two, three, four }; 
     private TestEnum _MyVar; 
     public TestEnum MyVar { 
      get { return _MyVar; } 
      set 
      { 
       _MyVar = value; 
       OnPropertyChanged("MyVar"); 
      } 
     } 

     public Test() 
     { 
      _MyVar = TestEnum.three; 
     } 
    } 
} 

La question SO j'ai parlé allusion à la syntaxe XAML correspondant comme étant:

 <ComboBox Name="cmbBox" 
        ... 
        ItemsSource="{Binding Source={w:EnumValues EnumType=w:Test+TestEnum}}" 
        ... 
        /> 

Mais ce (genre de) ne fonctionne pas pour moi. Quand je fais une nouvelle génération, je reçois un message « Build a réussi » dans la barre d'état VS 2008, mais je reçois aussi une erreur étant signalée dans le XAML

Type 'Test+TestEnum' was not found. 

Mais le code est exécuté comme prévu!

Cependant, cela signifie que le concepteur xaml ne chargera pas. Donc, je suis en quelque sorte foutu à faire plus de travail wpf jusqu'à ce que je peux effacer l'erreur xaml.

Je me demande maintenant si c'est un problème VS 2008 SP1, et pas un problème de ma part.

Modifier

  1. fait ma déclaration de problème plus explicite.
  2. Essayé 1ère solution de Joel, mais j'ai fini avec le code en cours d'exécution et 2 erreurs XAML
  3. 2ème solution de Essayé Joel et qui a travaillé tout droit sorti de la boîte - je vais donc avec celui-là!

Remarques La question de sorte que je suis le code MarkupExtension d'utilise ce style de syntaxe pour la XAML:

<ComboBox ItemsSource="{w:EnumValues w:TestEnum}"/> 

Quand j'utilise que je reçois une erreur de compilation disant que aucun constructeur EnumValues ​​prend 1 paramètre. J'ai fait un peu de googling et cela semble être une erreur dans VS. J'utilise VS 2008 SP1. J'ai vu quelques commentaires qui faisaient allusion à la bêta de VS 2010. Quoi qu'il en soit, c'est pourquoi j'utilise la syntaxe xaml de

<ComboBox ItemsSource="{w:EnumValues EnumType=w:TestEnum}"/> 

Comme cette syntaxe fonctionne!

+0

En ce qui concerne l'erreur sur le constructeur, il se produit uniquement dans le concepteur.Cela devrait bien fonctionner autrement. Pour éviter cette erreur, définissez l'extension de balisage dans un assembly séparé. –

+0

@Thomas: J'avais oublié comment le concepteur peut être capricieux sur les types construits quand/où et comment ils les instancient. –

+0

@Joel - Et les programmeurs débutants wpf savent ce genre de chose comment? Sachant mettre une classe dans un ensemble séparé afin de satisfaire une partie de l'outil de construction est plutôt obscur à mon humble avis. Mais je continuerai à m'embarrasser jusqu'à ce que j'aie une compréhension raisonnable. Merci encore. –

Répondre

3

Une autre façon d'obtenir les valeurs ENUM pour une utilisation en tant que source de données:

<Window.Resources> 
    <ObjectDataProvider 
     MethodName="GetValues" 
     ObjectType="{x:Type sys:Enum}" 
     x:Key="TestValues"> 
     <ObjectDataProvider.MethodParameters> 
      <w:Type2 
       TypeName="w:Test+TestEnum" /> 
     </ObjectDataProvider.MethodParameters> 
    </ObjectDataProvider> 
</Window.Resources> 

... 

ItemsSource="{Binding Source={StaticResource TestValues}}" 

Notez que vous devez toujours le Type2Extension en raison de bizarreries avec TypeExtension et types imbriqués. Mais vous n'avez pas besoin de l'extension de balisage personnalisé supplémentaire. De cette façon, mieux vaut utiliser la liste à plusieurs endroits, comme vous pouvez le déclarer dans vos ressources App.xaml.

+0

Merci beaucoup pour ça - ça me rend très fou depuis bien trop longtemps! –

+1

Très bien - il me manquait le "+" en déclarant l'énumération dans les MethodParameters, et cela me rendait folle. –

1

Qu'en est-il de l'utilisation de l'extension de balisage x:Type?

{w:EnumValues EnumType={x:Type w:Test+TestEnum}} 

À l'exception de la mise en œuvre INotifyPropertyChanged, je copiais votre code exactement. J'obtiens les erreurs que vous obtenez, mais cela semble aller très bien. Il est très ennuyeux de ne pas pouvoir charger le concepteur, cependant. Rien que j'ai essayé a résolu le problème.

J'ai trouvé this page sur MSDN à propos des types imbriqués, et une suggestion dans ce thread était un MarkupExtension personnalisé pour résoudre le nom de type imbriqué. J'essaye de le faire fonctionner, mais pas de chance jusqu'ici. Je reçois des erreurs similaires sur Type2Extension parfois, et je reçois "Le type enum n'est pas défini" avec d'autres réglages.

Aha! Il y avait un bug dans la façon dont l'auteur original appelait GetType()! Voici le corrigé Type2Extension et comment je l'ai utilisé:

public class Type2Extension : System.Windows.Markup.TypeExtension { 
    public Type2Extension() { 
    } 

    public Type2Extension(string typeName) { 
     base.TypeName = typeName; 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) { 
     IXamlTypeResolver typeResolver = (IXamlTypeResolver) serviceProvider.GetService(typeof(IXamlTypeResolver)); 
     int sepindex = TypeName.IndexOf('+'); 
     if (sepindex < 0) 
      return typeResolver.Resolve(TypeName); 
     else { 
      Type outerType = typeResolver.Resolve(TypeName.Substring(0, sepindex)); 
      return outerType.Assembly.GetType(outerType.FullName + "+" + TypeName.Substring(sepindex + 1)); 
     } 
    } 
} 

Et XAML:

ItemsSource="{Binding Source={w:EnumValues {w:Type2 w:Test+TestEnum}}}" 

Cela semble fonctionner correctement et les charges concepteur. Je vais ajouter Type2Extension à mes propres petites bibliothèques.

Edit: Étrangement, si je change cela en EnumValues:

if (this.EnumType == null) 
    throw new ArgumentException("The enum type is not set"); 

à ceci:

if (this.EnumType == null) 
    return null; 

Ensuite, ces erreurs constructeur disparaissent. C'était l'autre chose que j'ai changé. Cependant, je vais bientôt afficher une autre façon d'obtenir des valeurs enum.

+0

Si vous voulez dire: ItemsSource = "{Source de liaison = {w: EnumValues ​​EnumType = {x: Type w: Test + TestEnum}}}" Ensuite, cela a juste échoué à une grosse compilation –

+0

Intéressant. Après avoir répondu, j'ai commencé à mettre votre code d'exemple ensemble pour recréer (juste pour voir si cela a fonctionné). Je vais éditer si je trouve quelque chose. –

+0

Merci pour cela. Je suis trop fatigué pour m'en préoccuper en ce moment mais je regarderai la première chose le matin. Je suis nouveau à wpf et avoir à sauter par ce genre de cerceaux pour faire ce qui devrait être des trucs de base, c'est m'ennuyer complètement. –

Questions connexes