Get properties values from an object generic list c# - c#

i am trying to get the values from the properties of my generic list but i get an error "T does not contain a definition for...."
var values GetValues(Id);
if (values != null)
{
CreateTable<Object>(values);
}
/////
private void CreateTable<T>(IList<T> array)
{
foreach (item in array)
{
//Problem is here **** when trying to get item.tag
var text = new TextBox(){ Text = item.Tag , ID = item.TagID.ToString() };
}
}
How can make it work with generics? Appreciate any help

Why is it that you expect that an object of some arbitrary T type has a Tag and TagID property? Where are these properties defined? If they are defined on an interface, let's say
public interface IItem
{
string Tag { get; }
int TagID { get; }
}
then you don't need generics, you can redefine CreateTable as
private void CreateTable(IList<IITem> array)
{
foreach (var item in array)
{
//Problem is here **** when trying to get item.tag
var text = new TextBox(){ Text = item.Tag , ID = item.TagID.ToString() };
}
}

Related

How to remove JSON properties with a specific name from an array of objects?

I want to remove JSON properties by a specific "key" but it is not working as I expected. My code hasn't changed a thing.
I did the following
void Start()
{
var foodlist = new List<Food>()
{
new() { name = "Banana", price = 3000 },
new() { name = "Apple", price = 1000}
};
// SerializeObject()
string jasonString = JsonConvert.SerializeObject(foodlist);
JArray jArray = JArray.Parse(jasonString);
// Jarray => String Serialize
string jarrayString2 = JsonConvert.SerializeObject(jArray);
foreach (var jObject in jArray.Children<JObject>())
{
int indexNum = 0;
foreach (var jProperty in jObject.Properties())
{
if(jProperty.Name == "name")
{
jArray.Remove(jArray[indexNum][jProperty.Name]);
indexNum++;
}
}
}
// Check
string jarrayString = JsonConvert.SerializeObject(jArray);
print(jarrayString);
}
public class Food
{
public string name;
public int price;
}
**The result hasn't changed **
Output
[{"name":"Banana","price":3000},{"name":"Apple","price":1000}]
Result that I want
[{"price":3000},{"price":1000}]
So there a couple of issues with your code. First and foremost seems to be a bug where you are counting iterations during the foreach loop of the properties and not during the loop of the objects. i.e. You want to remove the property for each item in the array but the property is always at position 0. So for every loop you always remove the object property called "name" at position 0. This can be illustrated by setting the value to null instead of removing it. You will see that your output has the first object with null name but the following objects will remain unchanged.
I've had an attempt at making it work as is but where I've landed is basically just a for loop that does the same as a foreach would, with extra work.
e.g. We can try to fix your initial code as such:
int indexNum = 0;
foreach (var jObject in jArray.Children<JObject>())
{
foreach (var jProperty in jObject.Properties())
{
if(jProperty.Name == "name")
{
jArray[indexNum][jProperty.Name] = null;
}
}
indexNum++;
}
This should target the correct property in each object, but it could really be simplified down to something like this:
for (int i = 0; i < jArray.Count; i++)
{
var jObj = jArray[i] as JObject;
jObj.Remove("name");
}
And then simplifying that even further we could do it in one simple foreach like this:
foreach (JObject jObj in jArray)
{
jObj.Remove("name");
}
EDIT: I notice that the top foreach method throws an exception that "Collection was modified; enumeration operation may not execute." and so I've just set the value to null instead of try to remove it. But I'll leave the rest of the answer as is for reference.
Just use the JsonIgnore attribute.
Instructs the JsonSerializer not to serialize the public field or public read/write property value. More...
Change:
public class Food
{
public string name;
public int price;
}
...to:
public class Food
{
[JsonIgnore] // <-------- This causes 'name' to be excluded from serialisation
public string name;
public int price;
}
...and use like so:
var foodlist = new List<Food>()
{
new() { name = "Banana", price = 3000 },
new() { name = "Apple", price = 1000}
};
string jasonString = JsonConvert.SerializeObject(foodlist);
See also
JsonIgnoreAttribute, Json.NET
For the sake of information, if your json has useless data, simply ignore them:
{
"name" : "banana",
"price" : 1000
}
[Serializable]
public class Item
{
public int price;
}
void Start()
{
Item itemInstance = JsonUtility.FromJson<Item>(json);
}
in this example Item has no "name".

what is the best way to write a list of items in a text file in tree view form

i have a list of objects , and each object have a list of dependant objects , i want to write the list in a text file in a tree view form .
i tried doing foreach on the list but i can't all dependencies and the correct levels of objects
//the list of all objects
List<Object> objects;
//object Class
class Object {
string name;
List<Object> depandantObj;
}
the expected result must be writen in text file under the form :
object1:
object2
object3:
object5
object1
object6:
object2
etc...
Recursive method to append a new line for each object with indent:
public string GetText(Object obj, int indentLevel)
{
string text = "";
string indentation = new string(' ', indentLevel * 8);
text += indentation + obj.name;
if (obj.depandantObj != null && obj.depandantObj.Count > 0)
{
indentLevel++;
foreach (Object o in obj.depandantObj)
{
text += Environment.NewLine + GetText(o, indentLevel);
}
}
else
return text;
return text;
}
Call the method for each object in the list and write the text into the text file at the end:
make sure both of the fields (name and depandantObj) in Object class are public
List<Object> objects;
//add items to list
...
if(objects != null)
{
string text = "";
foreach (Object obj in objects)
{
text += GetText(obj, 0);
}
File.WriteAllText(Server.MapPath("~/sample.txt"), text);
}
First create your Objects with nested list forms. For example:
public class MyObject{
// some properties here
}
public class MySecondObject{
List<MyObject> {get; set;}
}
public class MythirdObject{
List<MySecondObject> {get; set;}
}
And when you want to save the data to a file just seriliaze them to json , it will already create a readable json file for you.
// I assume you can create your data or fetch them
var data = List<MyThirdObject> ();
string json = JsonConvert.SerializeObject(_data);
//write string to file
System.IO.File.WriteAllText(#"D:\path.txt", json);
If you don't want json than you can create a recusrive method that add each object to under last one.
Have a look this question for how you can this at this way.
Consider using json, download newtonsoft dll from Nuget.
a code example:
public class MyObject
{
public string Name { get; set; }
}
public class MySecondObject
{
public List<MyObject> DepObj { get; set; } = new List<MyObject>();
}
usage example:
MyObject obj = new MyObject
{
Name = "example"
};
MySecondObject mySecond = new MySecondObject();
mySecond.DepObj.Add(obj);
var data = new List<MySecondObject>
{
mySecond
};
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
System.IO.File.WriteAllText(#"D:\file.txt", json);
File content:
[
{
"DepObj": [
{
"Name": "example"
}
]
}
]
First, let's elaborate initial Object class; I've renamed it (in order not to conflict with System.Object), make fields being public, add a constructor:
class MyObject {
public string name = "";
public List<MyObject> depandantObj = new List<MyObject>();
public MyObject(string value, params MyObject[] dependent) {
name = value;
if (dependent != null)
foreach (var item in dependent)
depandantObj.Add(item);
}
}
Then we can implement an iterator, IEnumerable<string>:
private static IEnumerable<string> MyObjectToTree(IEnumerable<MyObject> roots, int shift = 6) {
if (null == roots)
yield break;
foreach (var root in roots) {
// We don't want infinte loop if objects create a cycle
HashSet<MyObject> completed = new HashSet<MyObject>();
Stack<Tuple<int, MyObject>> agenda = new Stack<Tuple<int, MyObject>>();
agenda.Push(Tuple.Create(0, root));
while (agenda.Any()) {
Tuple<int, MyObject> item = agenda.Pop();
if (!completed.Add(item.Item2))
continue;
List<MyObject> children = item.Item2?.depandantObj ?? new List<MyObject>();
children.Reverse();
yield return $"{new string(' ', shift * item.Item1)}{item.Item2?.name}{(children.Any() ? ":" : "")}";
foreach (var child in children)
agenda.Push(Tuple.Create(item.Item1 + 1, child));
}
}
}
Demo:
// I've added the MyObject constructor for this readable creation
List<MyObject> objects = new List<MyObject>() {
new MyObject("object1",
new MyObject("object2"),
new MyObject("object3",
new MyObject("object4"),
new MyObject("object5"))),
new MyObject("object6",
new MyObject("object2")),
};
foreach (string line in MyObjectToTree(objects, 6))
Console.WriteLine(line);
// If you want to write into file:
// File.WriteAllLines(#"c:\MyFile.txt", MyObjectToTree(objects, 6));
Outcome:
object1:
object2
object3:
object4
object5
object6:
object2

Set variables from an object using reflection doesn't work

I want to set variables from an object using Reflection.
For simple object this works. (Properties)
But objects with class variables (Fields) doesn’t work. Here I get always an Exeption with "The object does not agree with the target type."
Has anyone here an idea how it could go?
class Program
{
static void Main(string[] args)
{
var genericDataSet = new GenericDataSet<DataObjekt>();
var returnObjekt = genericDataSet.KeepElementsData();
}
}
public class DataObjekt
{
public string Name { get; set; }
public ObjektData ModelTyp;
public DataObjekt() { ModelTyp = new ObjektData(); }
}
public class ObjektData
{
public string Typ { get; set; }
public string Data { get; set; }
}
public class GenericDataSet<T> where T : class, new()
{
public T KeepElementsData()
{
var item = new T();
//Propertys durchlaufen
foreach (var Property in item.GetType().GetProperties())
{
item.GetType().GetProperty(Property.Name).SetValue(item, "TestData"); //this works
}
//Fields durchlaufen
foreach (var Field in item.GetType().GetFields())
{
foreach (var FieldProperty in item.GetType().GetField(Field.Name).FieldType.GetProperties())
{
var data = item.GetType().GetField(Field.Name).FieldType.GetProperty(FieldProperty.Name);
data.SetValue(item, "TestData not work", null); // this doesent work
}
}
return item;
}
}
The reason it doesn't work is because you are setting the value on the wrong object:
data.SetValue(item, "TestData not work", null);
item doesn't have this property, its the field that has it.
You need to create an instance of that field (if its null), then fill its properties and then set it to the field.
The following will work for you:
public class GenericDataSet<T> where T : class, new()
{
public T KeepElementsData()
{
var item = new T();
//Propertys durchlaufen
foreach (var propertyInfo in item.GetType().GetProperties())
{
item.GetType().GetProperty(propertyInfo.Name).SetValue(item, "TestData"); //this works
}
//Fields durchlaufen
foreach (var fieldInfo in item.GetType().GetFields())
{
object fieldObject = Activator.CreateInstance(fieldInfo.FieldType);
// Or if it's already instantiated:
// object fieldObject = fieldInfo.GetValue(item);
foreach (var fieldProperty in fieldInfo.FieldType.GetProperties())
{
fieldProperty.SetValue(fieldObject, "TestData not work", null); // this doesent work
}
fieldInfo.SetValue(item, fieldObject);
}
return item;
}
}
As far as I can see this shouldn´t even work for properties, as you provide only string-data, while not all of your properties have string-type. Anyway in your fields-loop why do you have a nested loop at all? You´re looping the properties of every fields type, which is quite bpring I guess. So if your field has type string you iterate in the inner loop all the fields from String. You should be able to omit the inner loop and write the same what you´re doing for properties. Furthermore you can directly set the properties value for the current item.
var item = new T();
//Propertys durchlaufen
foreach (var property in item.GetType().GetProperties())
{
property.SetValue(item, "TestData"); //this works
}
//Fields durchlaufen
foreach (var field in item.GetType().GetFields())
{
field.SetValue(item, "TestData");
}
Try this:
public T KeepElementsData()
{
var item = new T();
//Propertys durchlaufen
foreach (var property in item.GetType().GetProperties())
{
property.SetValue(item, "TestData"); //this works
}
//Fields durchlaufen
foreach (var field in item.GetType().GetFields())
{
var value = field.GetValue(item);
var type = value.GetType();
foreach (var fieldProperty in type.GetProperties())
{
fieldProperty.SetValue(value, "TestData works");
}
}
return item;
}
You are going back and forth between the PropertyInfo->name of the PropertyInfo->PropertyInfo... plus you are mixing the item object with its fields...

Populate a TreeView from an object

I am having a problem with a treeview in my WinForm app. I created a TreeViewItem class that holds the data. There are only 5 fields: CaseNoteID, ContactDate, ParentNoteID, InsertUser, ContactDetails.
public class TreeItem
{
public Guid CaseNoteID;
public Guid? ParentNoteID;
public string ContactDate;
public string InsertUser;
public string ContactDetails;
public TreeItem(Guid caseNoteID, Guid? parentNoteID, string contactDate, string contactDetails, string insertUser)
{
CaseNoteID = caseNoteID;
ParentNoteID = parentNoteID;
ContactDate = contactDate;
ContactDetails = contactDetails;
InsertUser = insertUser;
}
}
The plan was to show relationships of the notes by showing a note under it's parent as determined by the ParentNoteID field. Pretty simplistic really. Unfortunately, all my attempts so far have put a "child" note, one with a ParentNoteID, in both positions. The first level AND under it's appropriate Parent.
When I step through my code my data is coming back accurately.
List<TreeItem> items = BLLMatrix.GetTreeViewData(HUD.PersonId);
PopulateTree(tvwCaseNotes,items);
I just don't know how to take that and populate my TreeView accurately with it. This is what I started but now I am stuck.
public static void PopulateTree(TreeView tree, ICollection<TreeItem> items)
I just don't seem able to wrap my head around it. Do I need to split my data call up and first return all entrys with ParentNoteID = null and then go get the rest and somehow join the two?
#Hogan: I apologize for the drastic change in the question. It was evident from your response that I hadn't approached this from a good angle in the first place. In the second place, the original method still did not work.
Maybe i completely misunderstood you, but you have a flat hierarchy where each element knows its parent. Now you have to create each element and afterwards built up the hierarchy. Here is a first quick shot of such an implementation (missing cyclic checks, error handling, etc.):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
PopulateTreeView(treeView, SampleData());
}
private IEnumerable<Item> SampleData()
{
yield return new Item { CaseID = "1" };
yield return new Item { CaseID = "2" };
yield return new Item { CaseID = "3" };
yield return new Item { CaseID = "4", ParentID = "5" };
yield return new Item { CaseID = "5", ParentID = "3" };
yield return new Item { CaseID = "6" };
yield return new Item { CaseID = "7", ParentID = "1" };
yield return new Item { CaseID = "8", ParentID = "1" };
}
private void PopulateTreeView(TreeView tree, IEnumerable<Item> items)
{
Dictionary<string, Tuple<Item, TreeNode>> allNodes = new Dictionary<string, Tuple<Item, TreeNode>>();
foreach (var item in items)
{
var node = CreateTreeNode(item);
allNodes.Add(item.CaseID, Tuple.New(item, node));
}
foreach (var kvp in allNodes)
{
if (kvp.Value.First.ParentID != null)
{
allNodes[kvp.Value.First.ParentID].Second.Nodes.Add(kvp.Value.Second);
}
else
{
tree.Nodes.Add(kvp.Value.Second);
}
}
}
private TreeNode CreateTreeNode(Item item)
{
var node = new TreeNode();
node.Text = item.CaseID;
return node;
}
}
public class Item
{
public string CaseID { get; set; }
public string ParentID { get; set; }
}
public class Tuple<T>
{
public Tuple(T first)
{
First = first;
}
public T First { get; set; }
}
public class Tuple<T, T2> : Tuple<T>
{
public Tuple(T first, T2 second)
: base(first)
{
Second = second;
}
public T2 Second { get; set; }
}
public static class Tuple
{
//Allows Tuple.New(1, "2") instead of new Tuple<int, string>(1, "2")
public static Tuple<T1> New<T1>(T1 t1)
{
return new Tuple<T1>(t1);
}
public static Tuple<T1, T2> New<T1, T2>(T1 t1, T2 t2)
{
return new Tuple<T1, T2>(t1, t2);
}
}
What is a Tuple?
Just to answer the question in the comment:
Take a look at Wikipedia.
Take a look at this StackOverflow question.
It is a simple container object holding two other objects. That's it.
I used it, cause in my Dictionary is a unqiue identifier (string CaseID) which references on two objects (TreeNode and Item). Another approach would be to make two Dictionaries as Dictionary<string, TreeNode> and Dictionary<string, Item>, but because there is a 1:1 relationship i took the tuple approach.
Maybe rename it to ItemTreeNodeContainer and it will get more clearer for you what it means in the concrete situation.
The basic idea of recursion is you use the stack as a temporary store for variables on each call. However, you are referencing a global variable in your recursive call. When you change it (via the filter function) it will invalidate all prior calls in the recursion. You need to remove the recursion or push a new copy (and not a reference like you are doing) of the control variable (the rows) on the stack.
edit based on comment
I hate putting code out there without being able to test it, but I believe something like this should work to solved the problem I described.
Here is the problem area:
// using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
var tmpCNoteID = dr["CaseNoteID"].ToString();
var filter = "ParentNote='" + tmpCNoteID + "'";
DataRow[] childRows = cNoteDT.Select(filter);
if (childRows.Length > 0)
{
// Recursively call this function for all childRows
TreeNode[] childNodes = RecurseRows(childRows);
// Add all childnodes to this node
node.Nodes.AddRange(childNodes);
}
// Mark this noteID as dirty (already added)
nodeList.Add(node);
}
Something like this should fix the problem I see (note: this is not elegant or good code, it is just a fix to the problem I describe above, I would never put my name to this code). Also, without being able to test the code I can't even be sure this is the problem.
// using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
var tmpCNoteID = dr["CaseNoteID"].ToString();
var filter = "ParentNote='" + tmpCNoteID + "'";
DataTable DTCopy = cNoteDT.Copy()
DataRow[] childRows = DTCopy.Select(filter);
if (childRows.Length > 0)
{
// Recursively call this function for all childRows
TreeNode[] childNodes = RecurseRows(childRows);
// Add all childnodes to this node
node.Nodes.AddRange(childNodes);
}
// Mark this noteID as dirty (already added)
nodeList.Add(node);
}
Solved my problem using Oliver's solution. Just refactored it using Tuple that part of .Net 4.0
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
PopulateTreeView(treeView1, SampleData());
}
private IEnumerable<Item> SampleData()
{
yield return new Item { CaseID = "1" };
yield return new Item { CaseID = "2" };
yield return new Item { CaseID = "3" };
yield return new Item { CaseID = "4", ParentID = "5" };
yield return new Item { CaseID = "5", ParentID = "3" };
yield return new Item { CaseID = "6" };
yield return new Item { CaseID = "7", ParentID = "1" };
yield return new Item { CaseID = "8", ParentID = "1" };
}
private void PopulateTreeView(TreeView tree, IEnumerable<Item> items)
{
Dictionary<string, Tuple<Item, TreeNode>> allNodes = new Dictionary<string, Tuple<Item, TreeNode>>();
foreach (var item in items)
{
var node = CreateTreeNode(item);
allNodes.Add(item.CaseID, Tuple.Create(item, node));
}
foreach (var kvp in allNodes)
{
if (kvp.Value.Item1.ParentID != null)
{
allNodes[kvp.Value.Item1.ParentID].Item2.Nodes.Add(kvp.Value.Item2);
}
else
{
tree.Nodes.Add(kvp.Value.Item2);
}
}
}
private TreeNode CreateTreeNode(Item item)
{
var node = new TreeNode();
node.Text = item.CaseID;
return node;
}
}
public class Item
{
public string CaseID { get; set; }
public string ParentID { get; set; }
}
Tuple Help on MSDN:
Tuple Class
In my case I'm passing data source from entity framework: Entities.Categories
and replaced Item class with Category class generated by entity framework.

How do you bind an Enum to a DropDownList control in ASP.NET?

Let's say I have the following simple enum:
enum Response
{
Yes = 1,
No = 2,
Maybe = 3
}
How can I bind this enum to a DropDownList control so that the descriptions are displayed in the list as well as retrieve the associated numeric value (1,2,3) once an option has been selected?
I probably wouldn't bind the data as it's an enum, and it won't change after compile time (unless I'm having one of those stoopid moments).
Better just to iterate through the enum:
Dim itemValues As Array = System.Enum.GetValues(GetType(Response))
Dim itemNames As Array = System.Enum.GetNames(GetType(Response))
For i As Integer = 0 To itemNames.Length - 1
Dim item As New ListItem(itemNames(i), itemValues(i))
dropdownlist.Items.Add(item)
Next
Or the same in C#
Array itemValues = System.Enum.GetValues(typeof(Response));
Array itemNames = System.Enum.GetNames(typeof(Response));
for (int i = 0; i <= itemNames.Length - 1 ; i++) {
ListItem item = new ListItem(itemNames[i], itemValues[i]);
dropdownlist.Items.Add(item);
}
Use the following utility class Enumeration to get an IDictionary<int,string> (Enum value & name pair) from an Enumeration; you then bind the IDictionary to a bindable Control.
public static class Enumeration
{
public static IDictionary<int, string> GetAll<TEnum>() where TEnum: struct
{
var enumerationType = typeof (TEnum);
if (!enumerationType.IsEnum)
throw new ArgumentException("Enumeration type is expected.");
var dictionary = new Dictionary<int, string>();
foreach (int value in Enum.GetValues(enumerationType))
{
var name = Enum.GetName(enumerationType, value);
dictionary.Add(value, name);
}
return dictionary;
}
}
Example: Using the utility class to bind enumeration data to a control
ddlResponse.DataSource = Enumeration.GetAll<Response>();
ddlResponse.DataTextField = "Value";
ddlResponse.DataValueField = "Key";
ddlResponse.DataBind();
I use this for ASP.NET MVC:
Html.DropDownListFor(o => o.EnumProperty, Enum.GetValues(typeof(enumtype)).Cast<enumtype>().Select(x => new SelectListItem { Text = x.ToString(), Value = ((int)x).ToString() }))
My version is just a compressed form of the above:
foreach (Response r in Enum.GetValues(typeof(Response)))
{
ListItem item = new ListItem(Enum.GetName(typeof(Response), r), r.ToString());
DropDownList1.Items.Add(item);
}
public enum Color
{
RED,
GREEN,
BLUE
}
Every Enum type derives from System.Enum. There are two static methods that help bind data to a drop-down list control (and retrieve the value). These are Enum.GetNames and Enum.Parse. Using GetNames, you are able to bind to your drop-down list control as follows:
protected System.Web.UI.WebControls.DropDownList ddColor;
private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
ddColor.DataSource = Enum.GetNames(typeof(Color));
ddColor.DataBind();
}
}
Now if you want the Enum value Back on Selection ....
private void ddColor_SelectedIndexChanged(object sender, System.EventArgs e)
{
Color selectedColor = (Color)Enum.Parse(typeof(Color),ddColor.SelectedValue
}
After reading all posts I came up with a comprehensive solution to support showing enum description in dropdown list as well as selecting proper value from Model in dropdown when displaying in Edit mode:
enum:
using System.ComponentModel;
public enum CompanyType
{
[Description("")]
Null = 1,
[Description("Supplier")]
Supplier = 2,
[Description("Customer")]
Customer = 3
}
enum extension class:
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.Mvc;
public static class EnumExtension
{
public static string ToDescription(this System.Enum value)
{
var attributes = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
public static IEnumerable<SelectListItem> ToSelectList<T>(this System.Enum enumValue)
{
return
System.Enum.GetValues(enumValue.GetType()).Cast<T>()
.Select(
x =>
new SelectListItem
{
Text = ((System.Enum)(object) x).ToDescription(),
Value = x.ToString(),
Selected = (enumValue.Equals(x))
});
}
}
Model class:
public class Company
{
public string CompanyName { get; set; }
public CompanyType Type { get; set; }
}
and View:
#Html.DropDownListFor(m => m.Type,
#Model.Type.ToSelectList<CompanyType>())
and if you are using that dropdown without binding to Model, you can use this instead:
#Html.DropDownList("type",
Enum.GetValues(typeof(CompanyType)).Cast<CompanyType>()
.Select(x => new SelectListItem {Text = x.ToDescription(), Value = x.ToString()}))
So by doing so you can expect your dropdown displays Description instead of enum values. Also when it comes to Edit, your model will be updated by dropdown selected value after posting page.
As others have already said - don't databind to an enum, unless you need to bind to different enums depending on situation. There are several ways to do this, a couple of examples below.
ObjectDataSource
A declarative way of doing it with ObjectDataSource. First, create a BusinessObject class that will return the List to bind the DropDownList to:
public class DropDownData
{
enum Responses { Yes = 1, No = 2, Maybe = 3 }
public String Text { get; set; }
public int Value { get; set; }
public List<DropDownData> GetList()
{
var items = new List<DropDownData>();
foreach (int value in Enum.GetValues(typeof(Responses)))
{
items.Add(new DropDownData
{
Text = Enum.GetName(typeof (Responses), value),
Value = value
});
}
return items;
}
}
Then add some HTML markup to the ASPX page to point to this BO class:
<asp:DropDownList ID="DropDownList1" runat="server"
DataSourceID="ObjectDataSource1" DataTextField="Text" DataValueField="Value">
</asp:DropDownList>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="GetList" TypeName="DropDownData"></asp:ObjectDataSource>
This option requires no code behind.
Code Behind DataBind
To minimize the HTML in the ASPX page and do bind in Code Behind:
enum Responses { Yes = 1, No = 2, Maybe = 3 }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
foreach (int value in Enum.GetValues(typeof(Responses)))
{
DropDownList1.Items.Add(new ListItem(Enum.GetName(typeof(Responses), value), value.ToString()));
}
}
}
Anyway, the trick is to let the Enum type methods of GetValues, GetNames etc. to do work for you.
I am not sure how to do it in ASP.NET but check out this post... it might help?
Enum.GetValues(typeof(Response));
You could use linq:
var responseTypes= Enum.GetNames(typeof(Response)).Select(x => new { text = x, value = (int)Enum.Parse(typeof(Response), x) });
DropDownList.DataSource = responseTypes;
DropDownList.DataTextField = "text";
DropDownList.DataValueField = "value";
DropDownList.DataBind();
Array itemValues = Enum.GetValues(typeof(TaskStatus));
Array itemNames = Enum.GetNames(typeof(TaskStatus));
for (int i = 0; i <= itemNames.Length; i++)
{
ListItem item = new ListItem(itemNames.GetValue(i).ToString(),
itemValues.GetValue(i).ToString());
ddlStatus.Items.Add(item);
}
public enum Color
{
RED,
GREEN,
BLUE
}
ddColor.DataSource = Enum.GetNames(typeof(Color));
ddColor.DataBind();
Generic Code Using Answer six.
public static void BindControlToEnum(DataBoundControl ControlToBind, Type type)
{
//ListControl
if (type == null)
throw new ArgumentNullException("type");
else if (ControlToBind==null )
throw new ArgumentNullException("ControlToBind");
if (!type.IsEnum)
throw new ArgumentException("Only enumeration type is expected.");
Dictionary<int, string> pairs = new Dictionary<int, string>();
foreach (int i in Enum.GetValues(type))
{
pairs.Add(i, Enum.GetName(type, i));
}
ControlToBind.DataSource = pairs;
ListControl lstControl = ControlToBind as ListControl;
if (lstControl != null)
{
lstControl.DataTextField = "Value";
lstControl.DataValueField = "Key";
}
ControlToBind.DataBind();
}
After finding this answer I came up with what I think is a better (at least more elegant) way of doing this, thought I'd come back and share it here.
Page_Load:
DropDownList1.DataSource = Enum.GetValues(typeof(Response));
DropDownList1.DataBind();
LoadValues:
Response rIn = Response.Maybe;
DropDownList1.Text = rIn.ToString();
SaveValues:
Response rOut = (Response) Enum.Parse(typeof(Response), DropDownList1.Text);
This is probably an old question.. but this is how I did mine.
Model:
public class YourEntity
{
public int ID { get; set; }
public string Name{ get; set; }
public string Description { get; set; }
public OptionType Types { get; set; }
}
public enum OptionType
{
Unknown,
Option1,
Option2,
Option3
}
Then in the View: here's how to use populate the dropdown.
#Html.EnumDropDownListFor(model => model.Types, htmlAttributes: new { #class = "form-control" })
This should populate everything in your enum list. Hope this helps..
That's not quite what you're looking for, but might help:
http://blog.jeffhandley.com/archive/2008/01/27/enum-list-dropdown-control.aspx
Why not use like this to be able pass every listControle :
public static void BindToEnum(Type enumType, ListControl lc)
{
// get the names from the enumeration
string[] names = Enum.GetNames(enumType);
// get the values from the enumeration
Array values = Enum.GetValues(enumType);
// turn it into a hash table
Hashtable ht = new Hashtable();
for (int i = 0; i < names.Length; i++)
// note the cast to integer here is important
// otherwise we'll just get the enum string back again
ht.Add(names[i], (int)values.GetValue(i));
// return the dictionary to be bound to
lc.DataSource = ht;
lc.DataTextField = "Key";
lc.DataValueField = "Value";
lc.DataBind();
}
And use is just as simple as :
BindToEnum(typeof(NewsType), DropDownList1);
BindToEnum(typeof(NewsType), CheckBoxList1);
BindToEnum(typeof(NewsType), RadoBuuttonList1);
ASP.NET has since been updated with some more functionality, and you can now use built-in enum to dropdown.
If you want to bind on the Enum itself, use this:
#Html.DropDownList("response", EnumHelper.GetSelectList(typeof(Response)))
If you're binding on an instance of Response, use this:
// Assuming Model.Response is an instance of Response
#Html.EnumDropDownListFor(m => m.Response)
In ASP.NET Core you can use the following Html helper (comes from Microsoft.AspNetCore.Mvc.Rendering):
<select asp-items="Html.GetEnumSelectList<GridReportingStatusFilters>()">
<option value=""></option>
</select>
This is my solution for Order an Enum and DataBind(Text and Value)to Dropdown using LINQ
var mylist = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().ToList<MyEnum>().OrderBy(l => l.ToString());
foreach (MyEnum item in mylist)
ddlDivisao.Items.Add(new ListItem(item.ToString(), ((int)item).ToString()));
Check out my post on creating a custom helper "ASP.NET MVC - Creating a DropDownList helper for enums": http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx
If you would like to have a more user friendly description in your combo box (or other control) you can use the Description attribute with the following function:
public static object GetEnumDescriptions(Type enumType)
{
var list = new List<KeyValuePair<Enum, string>>();
foreach (Enum value in Enum.GetValues(enumType))
{
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
var attribute = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false).First();
if (attribute != null)
{
description = (attribute as DescriptionAttribute).Description;
}
list.Add(new KeyValuePair<Enum, string>(value, description));
}
return list;
}
Here is an example of an enum with Description attributes applied:
enum SampleEnum
{
NormalNoSpaces,
[Description("Description With Spaces")]
DescriptionWithSpaces,
[Description("50%")]
Percent_50,
}
Then Bind to control like so...
m_Combo_Sample.DataSource = GetEnumDescriptions(typeof(SampleEnum));
m_Combo_Sample.DisplayMember = "Value";
m_Combo_Sample.ValueMember = "Key";
This way you can put whatever text you want in the drop down without it having to look like a variable name
You could also use Extension methods. For those not familar with extensions I suggest checking the VB and C# documentation.
VB Extension:
Namespace CustomExtensions
Public Module ListItemCollectionExtension
<Runtime.CompilerServices.Extension()> _
Public Sub AddEnum(Of TEnum As Structure)(items As System.Web.UI.WebControls.ListItemCollection)
Dim enumerationType As System.Type = GetType(TEnum)
Dim enumUnderType As System.Type = System.Enum.GetUnderlyingType(enumType)
If Not enumerationType.IsEnum Then Throw New ArgumentException("Enumeration type is expected.")
Dim enumTypeNames() As String = System.Enum.GetNames(enumerationType)
Dim enumTypeValues() As TEnum = System.Enum.GetValues(enumerationType)
For i = 0 To enumTypeNames.Length - 1
items.Add(New System.Web.UI.WebControls.ListItem(saveResponseTypeNames(i), TryCast(enumTypeValues(i), System.Enum).ToString("d")))
Next
End Sub
End Module
End Namespace
To use the extension:
Imports <projectName>.CustomExtensions.ListItemCollectionExtension
...
yourDropDownList.Items.AddEnum(Of EnumType)()
C# Extension:
namespace CustomExtensions
{
public static class ListItemCollectionExtension
{
public static void AddEnum<TEnum>(this System.Web.UI.WebControls.ListItemCollection items) where TEnum : struct
{
System.Type enumType = typeof(TEnum);
System.Type enumUnderType = System.Enum.GetUnderlyingType(enumType);
if (!enumType.IsEnum) throw new Exception("Enumeration type is expected.");
string[] enumTypeNames = System.Enum.GetNames(enumType);
TEnum[] enumTypeValues = (TEnum[])System.Enum.GetValues(enumType);
for (int i = 0; i < enumTypeValues.Length; i++)
{
items.add(new System.Web.UI.WebControls.ListItem(enumTypeNames[i], (enumTypeValues[i] as System.Enum).ToString("d")));
}
}
}
}
To use the extension:
using CustomExtensions.ListItemCollectionExtension;
...
yourDropDownList.Items.AddEnum<EnumType>()
If you want to set the selected item at the same time replace
items.Add(New System.Web.UI.WebControls.ListItem(saveResponseTypeNames(i), saveResponseTypeValues(i).ToString("d")))
with
Dim newListItem As System.Web.UI.WebControls.ListItem
newListItem = New System.Web.UI.WebControls.ListItem(enumTypeNames(i), Convert.ChangeType(enumTypeValues(i), enumUnderType).ToString())
newListItem.Selected = If(EqualityComparer(Of TEnum).Default.Equals(selected, saveResponseTypeValues(i)), True, False)
items.Add(newListItem)
By converting to System.Enum rather then int size and output issues are avoided. For example 0xFFFF0000 would be 4294901760 as an uint but would be -65536 as an int.
TryCast and as System.Enum are slightly faster than Convert.ChangeType(enumTypeValues[i], enumUnderType).ToString() (12:13 in my speed tests).
Both asp.net and winforms tutorial with combobox and dropdownlist:
How to use Enum with Combobox in C# WinForms and Asp.Net
hope helps
The accepted solution doesn't work, but the code below will help others looking for the shortest solution.
foreach (string value in Enum.GetNames(typeof(Response)))
ddlResponse.Items.Add(new ListItem()
{
Text = value,
Value = ((int)Enum.Parse(typeof(Response), value)).ToString()
});
You can do this a lot shorter
public enum Test
{
Test1 = 1,
Test2 = 2,
Test3 = 3
}
class Program
{
static void Main(string[] args)
{
var items = Enum.GetValues(typeof(Test));
foreach (var item in items)
{
//Gives you the names
Console.WriteLine(item);
}
foreach(var item in (Test[])items)
{
// Gives you the numbers
Console.WriteLine((int)item);
}
}
}
For those of us that want a working C# solution that works with any drop and enum...
private void LoadConsciousnessDrop()
{
string sel_val = this.drp_Consciousness.SelectedValue;
this.drp_Consciousness.Items.Clear();
string[] names = Enum.GetNames(typeof(Consciousness));
for (int i = 0; i < names.Length; i++)
this.drp_Consciousness.Items.Add(new ListItem(names[i], ((int)((Consciousness)Enum.Parse(typeof(Consciousness), names[i]))).ToString()));
this.drp_Consciousness.SelectedValue = String.IsNullOrWhiteSpace(sel_val) ? null : sel_val;
}
I realize this post is older and for Asp.net, but I wanted to provide a solution I used recently for a c# Windows Forms Project. The idea is to build a dictionary where the keys are the names of the Enumerated elements and the values are the Enumerated values. You then bind the dictionary to the combobox. See a generic function that takes a ComboBox and Enum Type as arguments.
private void BuildComboBoxFromEnum(ComboBox box, Type enumType) {
var dict = new Dictionary<string, int>();
foreach (var foo in Enum.GetValues(enumType)) {
dict.Add(foo.ToString(), (int)foo);
}
box.DropDownStyle = ComboBoxStyle.DropDownList; // Forces comboBox to ReadOnly
box.DataSource = new BindingSource(dict, null);
box.DisplayMember = "Key";
box.ValueMember = "Value";
// Register a callback that prints the Name and Value of the
// selected enum. This should be removed after initial testing.
box.SelectedIndexChanged += (o, e) => {
Console.WriteLine("{0} {1}", box.Text, box.SelectedValue);
};
}
This function can be used as follows:
BuildComboBoxFromEnum(comboBox1,typeof(Response));

Categories

Resources