I have a stored procedure that return the data below:
HEIGHT LENGTH WEIGHT WIDTH
0 0 0 0
The columns will be dynamic, can have up to N columns.
What I want to do is bind this result to Details View then let user key in the value.
But I have the error below when I bind to Details View:
DetailsView with id 'detailsViewProcessParameter' did not have any properties or attributes from which to generate fields. Ensure that your data source has content.
Here is the method that I use to return list of object then bind to details view:
public static List<object> GetProcessParameters(int formulaId)
{
using (var db = new SurfaceTreatment.SurfaceTreatmentEntities())
{
var paramFormulaId = new SqlParameter { ParameterName = "formulaId", Value = formulaId };
var query = db.Database.SqlQuery<object>("exec usp_GetProcessParameters #formulaId", paramFormulaId).ToList();
return query.ToList();
}
}
I suspect that I should use "select new { PropertyName = Value, PropertyName = Value}" in my LINQ in order to get rid of the error.
But how do I select new when PropertyName(column name) are dynamic?
How do I use reflection to solve this?
Or is there a better way to do this?
The problem here is that you are returning and thus binding a list of objects i.e. List<object> to DetailsView. I suspect that your object doesn't have any public property which DetailsView needs to Generate Rows and thus throws the error.
When using a list to bind to DetailsView ( or say a GridView ), the object in context, which is represented by some Class, MUST have public properties . For example suppose we bind a List of User List<User> to DetailsView, then here the User class MUST have public properties which will form the Rows for DetailsView ( or Columns in GridView).
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
}
With this basic concept, check to see what does your query variable looks like and try to map this query variable to a Class with public properties.
Since in your case Columns are dynamic in nature, You must be first able to determine the total number of columns with there values, construct a corresponding DataTable at runtime and bind that data table to Detailsview.
If you set AutoGenerateRows to true for the control, all the rows should generate automatically. I do not see any need to perform reflection.
Note that any property which is set for DataKeyNames will not be editable.
Related
In c# using reflection i am getting the property names of a class and then getting the value of those properties and biding the values to an excel.
var userData = BAL.GetUserData();
foreach(var result in userData)
{
row.cells[0].value = result.UserId;
int index = 1;
foreach(var item in result.userOptions)
{
Option op = new Option;
PropertyInfo[] properties = typeof(Option).GetProperties();
foreach(PropertyInfo property in properties)
{
row.Cells[index].value = porperty.GetValue(item);
index++;
}
}
}
The issue is that the column names is my excel are already defined in an order and i need to bind values to the columns, using the above lines of code i am able achieve what is required as i defined the property names of my Option class in the same order which is defined in the excel.
Public class Option
{
Public string OptionText {get;set;}
public string OptionValue {get;set;}
public string OptionRange {get;set;}
public string OptionStep {get;set;}
}
In future if some other developer tries to change the properties of the Option class in some other order it will mess up my excel data, so i am looking if there is someway i can set an order for the class properties.
Type.GetProperties() documentation:
The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies.
They happen to be returned in order as defined in code since the beginning of .NET, but the documentation always made clear that this was not guaranteed. So you need to sort the obtained PropertyInfo instances in your code anyway.
Sort them either by:
Determining the order using attributes, for example using [Display(Order = n)]
Hardcoding the order in your Option class using a string array
Reading the Excel headers first
I have multiple datagrids and i would like to move data from one to another.
The problem is:
I have their names "made" into string variables:
string gridfrom = "datagrid" + cplist1.SelectedItem.ToString();
string gridto = "datagrid" + cplist2.SelectedItem.ToString();
In this case, i know that gridfrom = datagridMAR and gridto = datagridAPR
But gridfrom and gridto changes according to the listboxes selected items.
How do i call these objects (already existing in the form) to change its properties? For example:
gridto.DataSource = gridfrom;
Thanks in advance
Let me tell you an elegant way of dealing with this situation without getting into dynamic type loading, etc
Create a custom type, say
class GridName {
public DataGridView Grid {get; private set;}
public string Name {get; private set;}
public GridName(DatagridView grid, string name) {
Grid = grid;
Name = name;
}
public string override ToString() {
return Name;
}
}
Now, instead of adding a string into the cplist1 and cplist2, simply add GridName object into it. It will display the name correctly and also when you want the grid associated with it, you can access the .Grid property
There are many other ways of doing it. For example, you can use ValueMember and DisplayMember property.
I would definitely resist against trying to convert the grid name into a DatagridView object.
I'm trying to figure out the easiest way to have my columns start in a default order(left to right) for all users.
I tried setting them in the IList<T> that I use to populate the DataSource but that doesn't work.
Do I have any options besides setting each column manually following the instructions on this page -->Reordering Columns
Take a look at the following documentation.
http://www.telerik.com/help/winforms/gridview-populating-with-data-tips-when-binding-to-custom-collections.html
You can pretty much define the way you want the columns to look like. Here is the code:
radGridView1.MasterTemplate.AutoGenerateColumns = false;
radGridView1.MasterTemplate.Columns.Add(new GridViewTextBoxColumn("Name"));
radGridView1.MasterTemplate.Columns.Add(new GridViewTextBoxColumn("Attributes"));
radGridView1.MasterTemplate.Columns.Add(new GridViewTextBoxColumn("LastAccessTime"));
radGridView1.MasterTemplate.Columns.Add(new GridViewTextBoxColumn("CreationTime"));
DirectoryInfo directory = new DirectoryInfo("C:\\");
FileSystemInfo[] filesInDirectory = directory.GetFileSystemInfos();
radGridView1.DataSource = filesInDirectory;
As you can see, I have set not to autogenerate the columns. Then I define the columns I want. And then just bind the datasource. This will give you the order you want
Do let me know if this works for you.
I'm walking on VB.NET and I did something like this ..
dgv.Columns(Col4).DisplayIndex = 0
dgv.Columns(Col3).DisplayIndex = 1
dgv.Columns(Col2).DisplayIndex = 2
dgv.Columns(Col1).DisplayIndex = 3
in C# you can find it here
The grid will read and create its columns according to the fields in your object:
class MyObj
{
public string Column2 { get; set; }
public int Column1 { get; set; }
}
The above code will first add Column2 and then Column1 in your grid, while the following code will do the opposite:
class MyObj
{
public int Column1 { get; set; }
public string Column2 { get; set; }
}
Same goes for DataTables, it just reads the columns in the order they are.
To reorder them you can use the Move method as you already found out.
I have a class that stores a list of dictionary entries. I want bind that to a datasource for gridview from codebehind.
Code for dictionary type of , representing ErrorMessage and failed field.
public partial class FailedFields
{
private Dictionary<string, string> Code_Error = new Dictionary<string, string>();
public void AddFailedField(string field, string message)
{
Code_Error.Add(field, message);
}
public Dictionary<string, string> GetFailedFields()
{
return Code_Error;
}
}
Code for List of Dictionary entries.
public partial class ErrorFieldsList
{
private static List<Order.FailedFields> ErrorList = new List<Slab.FailedFields>();
public void AddErrorField(Order.FailedFields errs)
{
ErrorList.Add(errs);
}
public List<Order.FailedFields> GetErrorMessages()
{
return ErrorList;
}
}
Running in Visual Studio debug mode, i can see the list has the error list, but i cannot get it to display in the gridview. Bellow is one of the many ways (the one that makes most sense) i tried to set the list as a datasource.
ErrorBoxGridView.DataSource = FailedRecords.GetErrorMessages(). ;
ErrorBoxGridView.DataBind();
Any idea where i am going wrong ?
Also, i don't want to specify a datasource in the aspx page because i only want to display this when the error occurs.
If interested why i am doing this to store error messages, have a look at this:link 1
Solved Here Related Question
I will document a complete project when i finish on the wiki.
This can not be done I think. What I'd do is:
Instead of using Dictionary<string, string> define a class that contains two public properties for field and message
Create an object data source for that class (using Visual Studios "Data Sources" window)
Have GetErrorMessages() return List<ErrorClass> instead of Dictionary
Assign that list to the binding source.
EDIT
This is to clarify things according to the latest comments. What you need is one class that contains the information for one error. For example:
public class ErrorInfo
{
public string Field { get { ... } }
public string Message { get { ... } }
}
After that you place a BindingSource on your form and (in code) set its DataSource property to a list of error message classes. For example:
private List<ErrorInfo> errorList = new List<ErrorInfo>();
errorList.Add(new ErrorInfo() { ... });
errorList.Add(new ErrorInfo() { ... });
errorList.Add(new ErrorInfo() { ... });
bindingSource.DataSource = errorList;
The data grid view is bound to the BindingSource. You should see data now. You can manually create columns and set them to the respective property names of your ErrorInfo class as well, but then you'd have to set dataGridView.AutoCreateColumns to false somewhere in your code.
Databind List of Dictionnary into a GridView
List<Dictionary<string,string>> resultSet = SOME List of Dictionaries...
DataGridView.DataSource = resultSet.Select(x => new {
fieldOne = x["key1"], fieldTwo = x["key2"]
}).ToList();
DataGridView.DataBind();
Now u can Bind fieldOne and fieldTwo in the DataGridView element...
Kindly check the Link for the precise ans...
Thanks
.NET provides a handy KeyValuePair<(Of <(TKey, TValue>)>) structure, that can be used in cases like this. That way you don't have to define your own class. HTH.
Or you could bind to the Value & Key properties of each Dictionary item:
ErrorBoxGridView.DataSource = FailedRecords.GetErrorMessages();
ErrorBoxGridView.DataTextField = "Value";
ErrorBoxGridView.DataValueField = "Key";
ErrorBoxGridView.DataBind();
Is there a way to determine the order of the columns displayed in
a datagridview when binding it to a datasource whitch contains an
underlying IList ?
I thought there was a specific property attribute for this purpose
but can't recall what it actually was.
eg:
public void BindToGrid(IList<CustomClass> list)
{
_bindingSource.DataSource = list;
dataGridView1.DataSource = _bindingSource.DataSource;
}
Type binded should be something like this
class CustomClass
{
bool _selected = false;
//[DisplayOrder(0)]
public bool Selected
{
get { return _selected; }
set { _selected = value; }
}
string _name;
//[DisplayOrder(2)]
public string Name
{
get { return _name; }
set { _name = value; }
}
string _value;
//[DisplayOrder(1)]
public string Value
{
get { return _value; }
set { _value = value; }
}
}
Edit:
I would like to add that I rather not want to add the columns manually to columns list in the designer. I'd like to keep this as dynamic as possible.
In the DataGridView specify an actual list of columns instead of allowing it to auto-databind. You can do this in Design View in Visual Studio by selecting the control and adding the columns. Make sure you specify in each column which property it should bind to. Then you can rearrange the columns any way you want as well as do other customizations.
I think that the DisplayOrder attribute is relatively new and probably not supported in the DataGridView control.
The display order of the columns in the DataGridView is determined by the DisplayIndex properties of the DataGridViewColumn-s. You would have to set these properties on the columns of the grid, in order to change their order.
I also agree with Eilon's answer: you can create the list of the columns yourself, instead of auto-databinding, and that way you can determine the order in which they will be displayed.
The column ordering does not always work. You'll need to turn off AutoColumnCreate to fix inconsistencies:
http://www.internetworkconsulting.net/content/datadridview-displayorder-not-working
I am not sure whether this is a functionality that .Net Offers, but if you just change the order of your properties in the class, the grid renders the columns in the same order.
The below two classes will render in the order they are typed in the class. Strange!!
class CustomClass
{
public bool Selected {get;set;}
public string Name{get;set;}
}
class CustomClass
{
public string Name{get;set;}
public bool Selected {get;set;}
}