Entwickler-Ecke

WPF / Silverlight - Silverlight: Type als Template-Parameter verwenden?


FrEEzE2046 - Di 11.05.10 10:50
Titel: Silverlight: Type als Template-Parameter verwenden?
Hallo,

ich möchte ein Enum an eine ComboBox binden. Dafür habe ich mir folgende Klassen erstellt:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
internal static class EnumHelper<EnumType> where EnumType : struct
{
    public static IEnumerable<EnumType> GetEnumValues()
    {
        EnumType e = (EnumType)Enum.ToObject(typeof(EnumType), 0);
        return from f in e.GetType().GetFields(BindingFlags.Static | BindingFlags.Public)
                select (EnumType)f.GetValue(e);
    }
}

/* Converts Enum and IEnumerable */
public class EnumValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException(); 
    }

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


Nun funktioniert EnumHelper<MeinEnum>.GetEnumValues() ganz wunderbar und ich kann auch in der ComboBox die EnumElemente sehen. Nur möchte ich wie gesagt ein Binding haben. Durch den Converter müsste mir das eigentlich gelingen. Ich frage mich jedoch, wie ich meine TemplateKlasse EnumHelper innerhalb von EnumValueConverter.Convert aufrufen kann, da der Typ ja hier durch die Klasse Type übergeben wird.

Ja, ich könnte EnumHelper als nicht generische Klasse deklarieren, aber ich ziehe Fehler zur Kompilierzeit denen zur Laufzeit vor.


Kha - Di 11.05.10 11:10

Enum.GetValues ;) .

Wenn du außerhalb des Value-Converters doch lieber Typsicherheit haben willst, genügt ja schon ein kleiner Wrapper:

C#-Quelltext
1:
2:
3:
4:
5:
public static IEnumerable<T> GetValues()
  where T : struct
{
  return Enum.GetValues(typeof(T)).Cast<T>();
}


FrEEzE2046 - Di 11.05.10 11:12

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Enum.GetValues ;) .

Wenn du außerhalb des Value-Converters doch lieber Typsicherheit haben willst, genügt ja schon ein kleiner Wrapper:

C#-Quelltext
1:
2:
3:
4:
5:
public static IEnumerable<T> GetValues()
  where T : struct
{
  return Enum.GetValues(typeof(T)).Cast<T>();
}


Würde ich wohl genau so machen, nur gibt es in Silverlight kein GetValues, was ich wohl mal hätte erwähnen sollen ;-)


Kha - Di 11.05.10 11:15

Ah, Silverlight, schade ;) .
Das Prinzip bleibt trotzdem das gleiche: Baue zuerst eine untypisierte Methode und darüber dann einen typisierten Wrapper.


FrEEzE2046 - Di 11.05.10 11:17

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Ah, Silverlight, schade ;) .
Das Prinzip bleibt trotzdem das gleiche: Baue zuerst eine untypisierte Methode und darüber dann einen typisierten Wrapper.


Verständnisproblem: Ich muss die Methode aber doch im Converter aufrufen und der hat nur einen untypisierten Parameter, den ich dann nicht an eine typisierte Methode übergeben kann.

Du siehst ja anhand meines Codes was ich meine. Wie willst du das machen?


Kha - Di 11.05.10 11:41

Deshalb
user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Wenn du außerhalb des Value-Converters doch lieber Typsicherheit haben willst

:)
Im Value-Converter brauchst du die untypisierte Methode, das ist schon richtig.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
  static class EnumHelper
  {
    public static IEnumerable GetValues(Type enumType)
    {
      return enumType.GetFields(BindingFlags.Static | BindingFlags.Public).Select(f => f.GetValue(null));
    }

    public static IEnumerable<T> GetValues<T>()
      where T : struct
    {
      return GetValues(typeof(T)).Cast<T>();
    }
  }


FrEEzE2046 - Di 11.05.10 11:46

Also muss ich die untypisierte GetValue() Variante im Converter verwenden.

Das hier:

C#-Quelltext
1:
f => f.GetValue(null)                    


kapier ich allein syntaktisch überhaupt nicht. Hast du eine Erklärung dazu?


Kha - Di 11.05.10 11:55

user profile iconFrEEzE2046 hat folgendes geschrieben Zum zitierten Posting springen:
Hast du eine Erklärung dazu?
Das ist ein Lambda-Ausdruck [http://msdn.microsoft.com/en-us/library/bb397687.aspx], die Grundlage von LINQ. Die Syntax ist meine eigene Vorliebe und semantisch äquivalent zu deinem from-select; ich wollte einfach mal testen, ob dir das bewusst ist *g* .


FrEEzE2046 - Di 11.05.10 12:14

Okay,

ist also eine Lambda-Funktion dessen Rückgabemenge selektiert wird. Okay, soweit klar.
Leider bekomme ich das ganze immer noch nicht zum laufen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
// EnumHelper.cs

internal static class EnumHelper
{
    public static IEnumerable GetValues(Type EnumType)
    {
        if (EnumType.IsEnum )
            return EnumType.GetFields(BindingFlags.Static | BindingFlags.Public).Select(f => f.GetValue(null));
        else
            throw new ArgumentException("Type '" + EnumType.Name + "' is not an enum");
            
    }

    public static IEnumerable<EnumType> GetValues<EnumType>()
    {
        EnumType e = (EnumType)Enum.ToObject(typeof(EnumType), 0);
        return from f in e.GetType().GetFields(BindingFlags.Static | BindingFlags.Public)
                select (EnumType)f.GetValue(e);
        // or: return GetValues(typeof(EnumType)).Cast<EnumType>();
    }
}


/* Converts Enum and IEnumerable */
public class EnumValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return EnumHelper.GetValues(targetType);
    }

  public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
  {
        return value;
  }
}



C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
// Survey.xaml.cs

public partial class Survey : UserControl
{
    public enum SurveyCategory
    {
        test1,
        test2,
        test3,
        test4
    }

    public SurveyCategory Category { get; set; }
}



XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
<UserControl x:Class="WebSite.Views.Survey">
    <UserControl.Resources>
        <local:EnumValueConverter x:Name="EnumConverter"/>
    </UserControl.Resources>

    <ComboBox x:Name="cbCategory"
        ItemsSource="{Binding Path=Category, Mode=OneTime, Converter={StaticResource EnumConverter}}"
        SelectedItem="{Binding Path=Category, Mode=TwoWay}" />
</UserControl>



Leider ist der Inhalt der ComboBox weiterhin leer. Was mache ich falsch?


Kha - Di 11.05.10 12:41

Habe es mal unter WPF getestet, die könnten langsam ruhig mal mit der Final der SL4-Tools rausrücken...
Dem Converter habe ich statt x:Name ein x:Key verpasst und im Konstruktor DataContext = this; gesetzt. Wenn das Data Binding dann irgendwann mal anspringt, solltest du wie ich eine ArgumentException bekommen, denn im Converter muss es EnumHelper.GetValues(value.GetType()) heißen. Danach sah es ganz gut aus :zustimm: .


FrEEzE2046 - Di 11.05.10 13:28

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
im Konstruktor DataContext = this; gesetzt.


Im Konstruktor von EnumValueConverter oder welchen meinst du?


Kha - Di 11.05.10 13:47

Im Konstruktor des UserControls, irgendwo muss das Binding seine Daten herbekommen.


FrEEzE2046 - Di 11.05.10 14:16

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Im Konstruktor des UserControls, irgendwo muss das Binding seine Daten herbekommen.


Ja, hat sich schon geklärt. Trotzdem danke!