绑定ComboBoxes到枚举...在Silverlight! [英] Binding ComboBoxes to enums... in Silverlight!

查看:122
本文介绍了绑定ComboBoxes到枚举...在Silverlight!的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,Web和StackOverflow对于如何将组合框绑定到WPF中的枚举属性有很多好的答案。但是Silverlight缺少所有可能的功能:(例如:


  1. 你不能使用通用的 EnumDisplayer -style IValueConverter 接受类型参数,因为Silverlight不支持 x:键入

  2. 您不能使用 ObjectDataProvider ,例如这种方法,因为它不存在于Silverlight中。

  3. 由于Silverlight中不存在标记扩展名,因此您无法使用#2链接中的注释中的自定义标记扩展名。

  4. 您不能使用泛型而不是键入对象的属性来执行#1的版本,因为XAML中不支持泛型(并且使它们能够全部工作)在

批量失败!



正如我所看到的,唯一的方法是使用这个工作:


  1. 在我的ViewModel中作弊并绑定到一个字符串属性,其设置者/ getter进行转换,使用视图中的代码隐藏方式将值加载到ComboBox中。

  2. 自定义 IValueConverter 我想要绑定的每个枚举。

有没有任何更通用的替代方法,即不涉及编写相同的代码我想要的每一个枚举?我想我可以使用一个接受枚举的泛型类作为一个类型参数来解决#2,然后为每个枚举创建一个新的类,只需要这个简单的

  class MyEnumConverter:GenericEnumConverter< MyEnum>你的想法是什么?



< h2_lin>解决方案

嗯,我说得太久了!有一个完美的解决方案a>,至少在Silverlight 3中(可能只有3个,因为此线程表示在Silverlight 3中修复了与这些东西相关的错误。)



基本上,您需要一个单一的转换器用于 ItemsSource 属性,但只要传递类型为 MyEnum 的属性的名称,它就可以完全通用,而不使用任何禁止的方法。并且数据绑定到 SelectedItem 是完全无痛的;不需要转换器!那么,至少只要你不想通过例如每个枚举值自定义字符串。 DescriptionAttribute ,嗯...可能需要另一个转换器;希望我可以让它通用。



更新:我做了一个转换器,它的工作原理!我必须绑定到 SelectedIndex ,可惜的是,但没关系。使用这些人:

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

命名空间DomenicDenicola.Wpf
{
public class EnumToIntConverter:IValueConverter
{
public object Convert(object value,Type targetType,object parameter,System。全球化文化信息文化)
{
//注意:正如Martin在这个答案的评论中所指出的,这行
//取决于从0开始按顺序排列的枚举值,
//因为combobox指数是这样完成的。一个更一般的解决方案将是
//可能会查找GetValues数组中的值,它的值变量
//出现,然后返回该索引。
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()。其中​​(field => field.IsLiteral);
var values = new List< object>();
foreach(字段中的var字段)
{
描述Attribute [] 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();
}
}
}

使用这种绑定XAML :

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

这种资源声明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>

任何意见都不胜感激,因为我有点像XAML / Silverlight / WPF / etc。新手例如,$ code> EnumToIntConverter.ConvertBack 会缓慢,所以我应该考虑使用缓存?


So, the web, and StackOverflow, have plenty of nice answers for how to bind a combobox to an enum property in WPF. But Silverlight is missing all of the features that make this possible :(. For example:

  1. You can't use a generic EnumDisplayer-style IValueConverter that accepts a type parameter, since Silverlight doesn't support x:Type.
  2. You can't use ObjectDataProvider, like in this approach, since it doesn't exist in Silverlight.
  3. You can't use a custom markup extension like in the comments on the link from #2, since markup extensions don't exist in Silverlight.
  4. You can't do a version of #1 using generics instead of Type properties of the object, since generics aren't supported in XAML (and the hacks to make them work all depend on markup extensions, not supported in Silverlight).

Massive fail!

As I see it, the only way to make this work is to either

  1. Cheat and bind to a string property in my ViewModel, whose setter/getter does the conversion, loading values into the ComboBox using code-behind in the View.
  2. Make a custom IValueConverter for every enum I want to bind to.

Are there any alternatives that are more generic, i.e. don't involve writing the same code over and over for every enum I want? I suppose I could do solution #2 using a generic class accepting the enum as a type parameter, and then create new classes for every enum I want that are simply

class MyEnumConverter : GenericEnumConverter<MyEnum> {}

What are your thoughts, guys?

解决方案

Agh, I spoke too soon! There is a perfectly good solution, at least in Silverlight 3. (It might only be in 3, since this thread indicates that a bug related to this stuff was fixed in Silverlight 3.)

Basically, you need a single converter for the ItemsSource property, but it can be entirely generic without using any of the prohibited methods, as long as you pass it the name of a property whose type is MyEnum. And databinding to SelectedItem is entirely painless; no converter needed! Well, at least it is as long as you don't want custom strings for each enum value via e.g. the DescriptionAttribute, hmm... will probably need another converter for that one; hope I can make it generic.

Update: I made a converter and it works! I have to bind to SelectedIndex now, sadly, but it's OK. Use these guys:

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();
        }
    }
}

With this sort of binding XAML:

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

And this sort of resource-declaration 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>

Any comments would be appreciated, as I'm somewhat of a XAML/Silverlight/WPF/etc. newbie. For example, will the EnumToIntConverter.ConvertBack be slow, so that I should consider using a cache?

这篇关于绑定ComboBoxes到枚举...在Silverlight!的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆