[WPF] DataGrid и DataGridComboBoxColumn с Emun
От: Аноним  
Дата: 10.12.10 12:49
Оценка:
Добрый день,

Есть проблема с DataGrid.

Нужно построить 2 колонки: в одной текстовое значение, во второй комбобокс с enum System.Data.DbType
Пользователь должен заполнить пару строк и нажать ОК. Программа соотв должна считать заполненые поля.

Примерно концепт я понял: нужно создать класс описывающий структуру:

public class Mapping
{
    public DbType DBType { get; set; }
    public string ColumnName { get; set; }
}


создать коллекцию

public ObservableCollection<Mapping> MappingList = new ObservableCollection<Mapping>();


забиндить ее в грид. Там же включить свойство CanUserAddRows="True"

Остается вопрос как забиндить енум в колонку и собрать это все воедино. Или же существуют более простые способы решения данной задачи?

Помогите пожалуйста разобраться,
Спасибо
Re: [WPF] DataGrid и DataGridComboBoxColumn с Emun
От: Vladek Россия Github
Дата: 10.12.10 15:47
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Нужно построить 2 колонки: в одной текстовое значение, во второй комбобокс с enum System.Data.DbType


Я делаю так:
<DataGridComboBoxColumn Header="Тип"
                        ItemsSource="{Binding Source={my:EnumSource {x:Type System_Data:DbType}}}"
                        SelectedItemBinding="{Binding DBType, Converter={my:EnumConverter}}" />


Всю чёрную работу делают два класса: EnumSourceExtension, который создаёт из заданного перечисления список строк, и EnumConverter, который преобразует выбранную строку списка в значение перечисления.

  Исходные тексты
EnumSourceExtension.cs
namespace Views
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Windows.Markup;

    /// <summary>
    /// Markup extension that provides a list of the members of a given enum.
    /// </summary>
    public class EnumSourceExtension : MarkupExtension
    {
        private Type enumType;

        /// <summary>
        /// Initializes a new <see cref=”EnumListExtension”/>
        /// </summary>
        public EnumSourceExtension()
        {
        }

        /// <summary>
        /// Initializes a new <see cref=”EnumListExtension”/>
        /// </summary>
        /// <param name=”enumType”>The type of enum whose members are to be returned.</param>
        public EnumSourceExtension(Type enumType)
        {
            this.EnumType = enumType;
        }

        /// <summary>
        /// Gets/sets the type of enumeration to return
        /// </summary>
        public Type EnumType
        {
            get { return this.enumType; }
            set
            {
                if (enumType != value) {
                    if (value != null) {
                        var underlyingType = Nullable.GetUnderlyingType(value) ?? value;
                        if (!underlyingType.IsEnum)
                            throw new ArgumentException("Type must be for an Enum.");
                    }

                    enumType = value;
                }
            }
        }


        /// <summary>
        /// Gets/sets a value indicating whether to display the enumeration members as strings using the Description on the member if available.
        /// </summary>
        public bool AsString { get; set; }


        /// <summary>
        /// Returns a list of items for the specified <see cref=”EnumType”/>. Depending on the <see cref=”AsString”/> property, the
        /// items will be returned as the enum member value or as strings.
        /// </summary>
        /// <param name=”serviceProvider”>An object that provides services for the markup extension.</param>
        /// <returns></returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (enumType == null)
                throw new InvalidOperationException("The EnumType must be specified.");

            var actualEnumType = Nullable.GetUnderlyingType(this.enumType) ?? this.enumType;
            var enumValues = Enum.GetValues(actualEnumType);


            // if the object itself is to be returned then just use GetValues
            //
            if (!AsString) {
                if (actualEnumType == enumType)
                    return enumValues;


                var tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1);
                enumValues.CopyTo(tempArray, 1);
                return tempArray;
            }


            var items = new List<string>();

            if (actualEnumType != enumType)
                items.Add(null);

            // otherwise we must process the list
            foreach (var item in Enum.GetValues(enumType)) {
                var itemString = item.ToString();
                var field = this.enumType.GetField(itemString);
                var attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attribs != null && attribs.Length > 0)
                    itemString = ((DescriptionAttribute)attribs[0]).Description;

                items.Add(itemString);
            }

            return items.ToArray();
        }
    }
}


EnumConverter.cs
namespace Views
{
    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Windows.Data;
    using System.Windows.Markup;

    public class EnumConverter : MarkupExtension, IValueConverter
    {
        private static readonly EnumConverter instance = new EnumConverter();

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return instance;
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return null;

            var enumType = value.GetType();
            var valueString = value.ToString();
            var valueField = enumType.GetField(valueString);
            var valueDescription = valueField.GetCustomAttributes(typeof(DescriptionAttribute), false).
                Cast<DescriptionAttribute>().
                Select(attribute => attribute.Description).
                FirstOrDefault();

            return String.IsNullOrEmpty(valueDescription) ? valueString : valueDescription;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return null;

            var valueString = (string)value;

            foreach (var enumValue in Enum.GetValues(targetType)) {
                var enumValueString = enumValue.ToString();
                var enumField = targetType.GetField(enumValueString);
                var enumValueDescription = enumField.GetCustomAttributes(typeof(DescriptionAttribute), false).
                    Cast<DescriptionAttribute>().
                    Select(attribute => attribute.Description).
                    FirstOrDefault();

                if (String.IsNullOrEmpty(enumValueDescription)) {
                    if (enumValueString == valueString)
                        return enumValue;
                }
                else {
                    if (enumValueDescription == valueString)
                        return enumValue;
                }
            }

            throw new InvalidOperationException();
        }
    }
}
Re[2]: [WPF] DataGrid и DataGridComboBoxColumn с Emun
От: Аноним  
Дата: 10.12.10 16:37
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Я делаю так:

V>
V><DataGridComboBoxColumn Header="Тип"
V>                        ItemsSource="{Binding Source={my:EnumSource {x:Type System_Data:DbType}}}"
V>                        SelectedItemBinding="{Binding DBType, Converter={my:EnumConverter}}" />
V>


V>Всю чёрную работу делают два класса: EnumSourceExtension, который создаёт из заданного перечисления список строк, и EnumConverter, который преобразует выбранную строку списка в значение перечисления.


V>
  Исходные тексты


Спасибо, помогло

Необходимо поправить строку кода в методе ConvertBack класса EnumConverter:
вместо:
var valueString = (string)value;

надо:
var valueString =  value.ToString();

так не будет падать ексепшн об ошибке приведения типов и все равно далее он сравнивается с enumValueString, который берется таким же образом

А как насчет общего подхода к заполнению грида. Нельзя его ни к чему не биндить, а потом просто считать поля?
Re[2]: [WPF] DataGrid и DataGridComboBoxColumn с Emun
От: Аноним  
Дата: 10.12.10 16:56
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Я делаю так:


Нет, не помогло. С заполненной ObservableCollection не отображает заданные на входе енумы
Re[3]: [WPF] DataGrid и DataGridComboBoxColumn с Emun
От: Vladek Россия Github
Дата: 11.12.10 18:28
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Vladek, Вы писали:


V>>Я делаю так:


А>Нет, не помогло. С заполненной ObservableCollection не отображает заданные на входе енумы


Либо
SelectedItemBinding="{Binding DBType, Converter={my:EnumConverter}, Mode=TwoWay}"


либо механизм биндинга проглатывает где-то ошибку — попробуйте отлаживать биндинги.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.