Ainsi, le Web et Stackoverflow, ont de nombreuses réponses de bonnes réponses pour la manière de lier une combinaison à une propriété Enum dans WPF. Mais Silverlight manque toutes les fonctionnalités qui rendent cela possible :(. Par exemple:
EnumDisplayer
- style IValueConverter
qui accepte un paramètre de type, car Silverlight ne prend pas en charge x:Type
.ObjectDataProvider
, comme dans cette approche , puisqu'il n'existe pas en Silverlight.Type
propriétés de l'objet, car les génériques ne sont pas pris en charge dans XAML (et les hacks pour les faire fonctionnerent toutes dépendent des extensions de balisage, non prises en charge dans Silverlight).FAUX MASSIVE!
Comme je le vois, le seul moyen de faire ce travail est de soit
IValueConverter
pour chaque enum que je veux lier.Y a-t-il des alternatives plus génériques, c'est-à-dire que vous n'impliquez pas d'écrire le même code encore et plus pour chaque énumé que je veux? Je suppose que je puisse faire la solution n ° 2 à l'aide d'une classe générique acceptant l'ENUM en tant que paramètre de type, puis créez de nouvelles classes pour chaque énum que je veux simplement
class MyEnumConverter : GenericEnumConverter<MyEnum> {}
Quelles sont vos pensées, les gars?
Agh, j'ai parlé trop tôt! Il y a ne solution parfaitement bonne , au moins dans Silverlight 3. (il ne peut être que dans 3, car - ce fil indique qu'un bug lié à cette substance a été fixé à Silverlight 3.)
Fondamentalement, vous avez besoin d'un seul convertisseur pour la propriété ItemsSource
, mais il peut être entièrement générique sans utiliser les méthodes interdites, tant que vous le transmettez le nom d'une propriété dont le type est MyEnum
. Et databinding à SelectedItem
est totalement indolore; Aucun convertisseur nécessaire! Eh bien, au moins, il est aussi long que vous ne voulez pas de chaînes personnalisées pour chaque valeur Enum via E.G. Le DescriptionAttribute
, hmm ... aura probablement besoin d'un autre convertisseur pour celui-ci; J'espère que je peux le rendre générique.
Mise à jour: J'ai fait un convertisseur et ça marche! Je dois me lier à SelectedIndex
maintenant, malheureusement, mais ça va. Utilisez ces gars:
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 type de liaison xaml:
<ComboBox x:Name="MonsterGroupRole"
ItemsSource="{Binding MonsterGroupRole,
Mode=OneTime,
Converter={StaticResource EnumToIEnumerableConverter}}"
SelectedIndex="{Binding MonsterGroupRole,
Mode=TwoWay,
Converter={StaticResource EnumToIntConverter}}" />
Et ce type de déclaration de ressources 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>
Toutes les commentaires seraient appréciés, comme je suis un peu XAML/Silverlight/WPF/etc. débutant. Par exemple, le EnumToIntConverter.ConvertBack
Soyez lent, de sorte que je devrais envisager d'utiliser un cache?
Il existe une autre façon de lier ComboBox à Enums 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-a-a-combobbox-in-silverlight/
Il n'utilise pas la descriptionAttributes .... mais cela fonctionne parfaitement pour moi, alors je suppose que cela dépend du scénario qu'il sera utilisé.
Je trouve qu'une simple encapsulation de données ENUM est la solution 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 ... :)
Voici la même configuration pour une application universelle Windows 8.1/Windows Phone, les principaux changements sont les suivants: -
Il semble que l'ordre du XAML soit également important, je devais mettre des objets à la sélection suivante, sinon cela n'a pas appelé l'objet de la liaison E.g.
<ComboBox
ItemsSource="{Binding Path=MyProperty,Mode=OneWay, Converter={StaticResource EnumToIEnumerableConverter}}"
SelectedIndex="{Binding Path=MyProperty, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}"
/>
Code ci-dessous
namespace MyApp.Converters
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Windows.UI.Xaml.Data;
public class EnumToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// 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, string language)
{
return value;
}
}
public class EnumToIEnumerableConverter : IValueConverter
{
private readonly Dictionary<TypeInfo, List<object>> _cache = new Dictionary<TypeInfo, List<object>>();
public object Convert(object value, Type targetType, object parameter, string language)
{
var type = value.GetType().GetTypeInfo();
if (!_cache.ContainsKey(type))
{
var fields = type.DeclaredFields.Where(field => field.IsLiteral);
var values = new List<object>();
foreach (var field in fields)
{
var a = (DescriptionAttribute[]) field.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (a != null && a.Length > 0)
{
values.Add(a[0].Description);
}
else
{
values.Add(field.GetValue(value));
}
}
_cache[type] = values;
}
return _cache[type];
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
[AttributeUsage(AttributeTargets.Field)]
public class DescriptionAttribute : Attribute
{
public string Description { get; private set; }
public DescriptionAttribute(string description)
{
Description = description;
}
}
}