Hello,
This is a bit of a complicated questions, so hopefully I will cover all bases:
Take this simple object, that has a string and enum as properties
public class Calculation
{
public string Name {get;set;}
public CalculationType CalculationType {get;set;
}
public enum CalculationType
[Description("<")] Less_Than = 0,
[Description(">")] Greater_Than = 1,
[Description("=")] Equal = 2,
[Description("<=")] Less_Than_Equal_To = 3,
[Description(">=")] Greater_Than_Equal_To = 4,
[Description("<>")] Not_Equal_To = 5
We are going to set a list of these to ultragrid datasource.
In the initialize event of the data grid, we are setting the editor for the CalculationType to be an UltraComboEditor. We want the datasource of the UltraComboEditor to be the enum CalculationType.
We have two extension methods for enum. One that handles straight conversion of an enum to dictionary (so that it can be ToList for combo datasource) and another that handles conversion with the description attribute.
public static IDictionary<int, string> ToDictionaryWithDescriptions(this Type type) { if (!type.IsEnum) { throw new InvalidCastException("'enumValue' is not an Enumeration!"); }
var names = Enum.GetNames(type); var values = Enum.GetValues(type);
return Enumerable.Range(0, names.Length) .Select(index => new { Key = (int)(object)((Enum)values.GetValue(index)), Value = ((Enum)values.GetValue(index)).GetDescription(),
}) .ToDictionary(k => k.Key, k => k.Value); }
public static IDictionary<int, string> ToDictionary<T>(this Type type) { if (!type.IsEnum) { throw new InvalidCastException("'enumValue' is not an Enumeration!"); }
return Enum.GetValues(typeof(T)) .Cast<T>() .ToDictionary(t => (int)(object)t, t => t.ToString()); }
We then have extension methods for combo editor. Note GridKey is simple class with two string, Key/Value. I thought for some reason we could not use anonymous.
public static void SetDataSource<T>(this UltraComboEditor editor) { editor.DataSource = typeof(T).ToDictionary<T>().Select( list => new GridKeyValue { Value = list.Key.ToString(), Key = list.Value }).ToList();
editor.ValueMember = "Value"; editor.DisplayMember = "Key"; }
public static void SetDataSourceWithDescriptions<T>(this UltraComboEditor editor) { editor.DataSource = typeof(T).ToDictionaryWithDescriptions().Select( list => new { list.Key, list.Value }).ToList();
editor.ValueMember = "Key"; editor.DisplayMember = "Value"; }
Here is an example of how we are setting in grid combo editor
...
ColumnsCollection c = e.Layout.Bands[0].Columns;
if (ucmboComputeOperator == null) { ucmboComputeOperator = new UltraComboEditor { DropDownStyle = DropDownStyle.DropDownList, Visible = false }; ucmboComputeOperator.SetDataSource<CalculationType>(); Controls.Add(ucmboComputeOperator); } c[nameof(ApprovalExceptionRead.ComputeOperator)].EditorComponent = ucmboComputeOperator;
So, after all this, it does not work. We get an error when changing the value of the drop down in a row and calling grid.UpdateData
Object of type 'System.DBNull' cannot be converted to type 'CalculationType'
We think we understand why this is happening, so my questions are, in the context of using it with grid:
What is the standard way to set datasource of combo editor to enum?
What if we want to use description as display memeber?
Thanks,M.
Hello Michael,
Great, glad you resolved this. At a more basic level the way to use a drop down in a grid is to use a ValueList and not an editor (which you still can use) but its easier in this situation. Because you want to show a string instead of the underlying enum in the cell (the column can only have 1 datatype), the ValueList has its own DisplayStyle property which you can use to choose whether the grid column will show the CalculationType or it's Description. Hide the column it generates from the enum list and create an Unbound Column which uses the ValueList derived from the calculation types from the adjacent cell.eg.
public Form1() { InitializeComponent(); List<CalculationType> calculationTypes = new List<CalculationType> { CalculationType.Less_Than, CalculationType.Greater_Than, CalculationType.Equal, CalculationType.Less_Than_Equal_To, CalculationType.Greater_Than_Equal_To, CalculationType.Not_Equal_To }; //List<string> calculationTypeDescriptions = new List<string>(); //foreach (CalculationType calculationType in calculationTypes) //{ // Console.WriteLine($"{calculationType}: {GetDescription(calculationType)}"); // calculationTypeDescriptions.Add(GetDescription(calculationType)); //} //this.ultraCombo1.DataSource = calculationTypes; this.ultraComboEditor1.DataSource = calculationTypes; this.ultraGrid1.DataSource = calculationTypes; //this.ultraGrid1.DisplayLayout.Bands[0].Columns[0].EditorComponent = this.ultraComboEditor1; this.ultraGrid1.DisplayLayout.Bands[0].Columns.Add("Description", "Description"); var column1 = this.ultraGrid1.DisplayLayout.Bands[0].Columns[0]; ValueList vl = new ValueList(); vl.ValueListItems.Add(ultraGrid1.Rows[0].GetCellValue(column1).ToString(), "<"); vl.ValueListItems.Add(ultraGrid1.Rows[1].GetCellValue(column1).ToString(), ">"); vl.DisplayStyle = ValueListDisplayStyle.DisplayText; this.ultraGrid1.DisplayLayout.Bands[0].Columns["Description"].DataType = typeof(string); this.ultraGrid1.DisplayLayout.Bands[0].Columns["Description"].ValueList = vl; this.ultraGrid1.DisplayLayout.Bands[0].Columns[0].Hidden = true; } static string GetDescription(Enum value) { var fieldInfo = value.GetType().GetField(value.ToString()); var descriptionAttribute = (DescriptionAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)); return descriptionAttribute.Description; }
We resolved.
Changed extension method to be:
public static List<KeyValuePair<string, Enum>> ToEnumList(this Type type, bool getDescriptions = false) { return Enum.GetValues(type) .Cast<Enum>() .Select(v => new KeyValuePair<string, Enum>(getDescriptions ? v.GetDescription() : v.ToString(), v)) .ToList(); }