38 votes

Reliez les ComboBoxes aux enums ... dans Silverlight!

Donc, le web, et StackOverflow, ont beaucoup de belles réponses pour comment lier une zone de liste déroulante à une énumération des biens en WPF. Mais Silverlight est absent de toutes les fonctionnalités qui rendent cela possible :(. Par exemple:

  1. Vous ne pouvez pas utiliser un générique EnumDisplayer-style IValueConverter qui accepte un paramètre de type, depuis Silverlight ne prend pas en charge x:Type.
  2. Vous ne pouvez pas utiliser ObjectDataProvider, comme dans cette approche, car il n'existe pas dans Silverlight.
  3. Vous ne pouvez pas utiliser une coutume extension de balisage comme dans les commentaires sur le lien de #2, car les extensions de balisage n'existent pas dans Silverlight.
  4. Vous ne pouvez pas faire une version de #1 à l'aide de médicaments génériques au lieu de Type propriétés de l'objet, puisque les génériques ne sont pas pris en charge dans le code XAML (et les hacks de leur faire le travail, dépendent toutes les extensions de balisage, pas pris en charge dans Silverlight).

Énorme fail!

Comme je le vois, la seule façon de faire ce travail consiste à

  1. Tricher et de se lier à une propriété de type chaîne dans mon ViewModel, dont le setter/getter fait la conversion, le chargement des valeurs dans la zone de liste déroulante à l'aide de code-behind de la Vue.
  2. Faire une coutume IValueConverter pour chaque enum je veux lier.

Existe-il des solutions de rechange qui sont plus génériques, c'est à dire n'impliquant pas d'écrire le même code pour tous les enum je veux? Je suppose que j'ai pu faire solution n ° 2 à l'aide d'une classe générique d'accepter l'enum comme un paramètre de type, et ensuite de créer de nouvelles classes pour chaque enum je veux tout simplement

class MyEnumConverter : GenericEnumConverter<MyEnum> {}

Quelles sont vos pensées, les gars?

34voto

Domenic Points 40761

Agh, j'ai parlé trop vite!!! Il est une très bonne solution, au moins dans Silverlight 3. (Elle sera peut-être dans 3, puisque ce fil indique que d'un bug lié à ce genre de choses a été corrigé dans Silverlight 3.)

Fondamentalement, vous avez besoin d'un seul convertisseur pour l' ItemsSource de la propriété, mais il peut être tout à fait générique sans utiliser les méthodes interdites, aussi longtemps que vous le passer le nom d'une propriété dont le type est - MyEnum. Et de liaison de données pour SelectedItem est tout à fait indolore; aucun convertisseur n'est nécessaire! Eh bien, au moins il est aussi long que vous ne voulez pas de chaînes personnalisées pour chaque valeur d'énumération, par exemple, à l' DescriptionAttribute, hmm... ne sera probablement besoin d'un autre convertisseur pour celle-là; espère que je peux le rendre générique.

Mise à jour: j'ai fait un convertisseur et ça marche!!! Je lier SelectedIndex maintenant, malheureusement, mais c'est OK. L'utilisation de ces gars-là:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;

namespace DomenicDenicola.Wpf
{
    public class EnumToIntConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Note: as pointed out by Martin in the comments on this answer, this line
            // depends on the enum values being sequentially ordered from 0 onward,
            // since combobox indices are done that way. A more general solution would
            // probably look up where in the GetValues array our value variable
            // appears, then return that index.
            return (int)value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Enum.Parse(targetType, value.ToString(), true);
        }
    }
    public class EnumToIEnumerableConverter : IValueConverter
    {
        private Dictionary<Type, List<object>> cache = new Dictionary<Type, List<object>>();

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var type = value.GetType();
            if (!this.cache.ContainsKey(type))
            {
                var fields = type.GetFields().Where(field => field.IsLiteral);
                var values = new List<object>();
                foreach (var field in fields)
                {
                    DescriptionAttribute[] a = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    if (a != null && a.Length > 0)
                    {
                        values.Add(a[0].Description);
                    }
                    else
                    {
                        values.Add(field.GetValue(value));
                    }
                }
                this.cache[type] = values;
            }

            return this.cache[type];
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Avec ce genre de binding XAML:

<ComboBox x:Name="MonsterGroupRole"
          ItemsSource="{Binding MonsterGroupRole,
                                Mode=OneTime,
                                Converter={StaticResource EnumToIEnumerableConverter}}"
          SelectedIndex="{Binding MonsterGroupRole,
                                  Mode=TwoWay,
                                  Converter={StaticResource EnumToIntConverter}}" />

Et ce genre de ressources-déclaration XAML:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ddwpf="clr-namespace:DomenicDenicola.Wpf">
    <Application.Resources>
        <ddwpf:EnumToIEnumerableConverter x:Key="EnumToIEnumerableConverter" />
        <ddwpf:EnumToIntConverter x:Key="EnumToIntConverter" />
    </Application.Resources>
</Application>

Tout commentaire serait apprécié, comme je suis un peu de XAML/Silverlight/WPF/etc. newbie. Par exemple, l' EnumToIntConverter.ConvertBack être lent, de sorte que je devrais envisager d'utiliser un cache?

4voto

André Matos Points 31

Il existe un autre moyen de lier ComboBox à des énumérations sans avoir besoin d'un convertisseur personnalisé pour l'élément sélectionné. Vous pouvez le vérifier à

http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/

Il n'utilise pas les attributs de description .... mais cela fonctionne parfaitement pour moi, donc je suppose que cela dépend du scénario dans lequel il sera utilisé

4voto

Micael Levesque Points 31

Je trouve qu'une simple encapsulation de données enum est beaucoup plus facile à utiliser.

 public ReadOnly property MonsterGroupRole as list(of string)
  get
    return [Enum].GetNames(GetType(GroupRoleEnum)).Tolist
  End get
End Property

private _monsterEnum as GroupRoleEnum
Public Property MonsterGroupRoleValue as Integer
  get
    return _monsterEnum
  End get
  set(value as integer)
    _monsterEnum=value
  End set
End Property
 

...

 <ComboBox x:Name="MonsterGroupRole"
      ItemsSource="{Binding MonsterGroupRole,
                            Mode=OneTime}"
      SelectedIndex="{Binding MonsterGroupRoleValue ,
                              Mode=TwoWay}" />
 

Et cela éliminera complètement le besoin d'un convertisseur ... :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X