Добрый день,
Есть проблема с 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"
Остается вопрос как забиндить енум в колонку и собрать это все воедино. Или же существуют более простые способы решения данной задачи?
Помогите пожалуйста разобраться,
Спасибо
Здравствуйте, Аноним, Вы писали:
А>Нужно построить 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();
}
}
}
|
| |
Здравствуйте, 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, который берется таким же образом
А как насчет общего подхода к заполнению грида. Нельзя его ни к чему не биндить, а потом просто считать поля?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Vladek, Вы писали:
V>>Я делаю так:
А>Нет, не помогло. С заполненной ObservableCollection не отображает заданные на входе енумы
Либо
SelectedItemBinding="{Binding DBType, Converter={my:EnumConverter}, Mode=TwoWay}"
либо механизм биндинга проглатывает где-то ошибку — попробуйте отлаживать биндинги.