I am currently working on a small project that includes an abstract baseclass of sorts, which defines some methods that require a dictionary. Yet the dicitionary is individual for each derived class.
Therefore I defined the dictionary in my baseclass as follows:
abstract internal Dictionary<string, column> columnNames { get; }
and in each inheriting class I set that dicitonary like so:
internal override Dictionary<string, column> columnNames
{
get
{
return new Dictionary<string, column>{
{"partID", new column{ name = "ID", show = false, type = typeof(int)}},//false
{"partName", new column{ name = "Name", show = true, type = typeof(string)}}
};
}
}
Now if I want to change properties of the dicitonary I tried the following:
columnNames["partID"].show = true;
Which does not work. There is no exception, the value in the dicitionary simply does not chance.
I also wrote a set-Function within my base-class:
public void setDictEntry(string key, column value)
{
columnNames[key] = value;
}
But it still did not work. I was wondering: Is it because my dictionary does not have a set property?
For completion, here the definition of the column class:
class column
{
public string name { get; set; }
public bool show { get; set; }
public string format { get; set; }
public System.Type type { get; set; }
}
As always, sorry for my bad english and thanks for your help!
Of course it doesn't change - you're always returning a different Dictonary! You'll have to return an existing dictionary (for example, by storing the dictionary in a field) to have it (effectively) mutable.
Dictionary<string, column> _columnNames =
new Dictionary<string, column>{
{"partID", new column{ name = "ID", show = false, type = typeof(int)}},//false
{"partName", new column{ name = "Name", show = true, type = typeof(string)}}
};
internal override Dictionary<string, column> columnNames
{
get
{
return _columnNames;
}
}
Related
When I want to add a subscriber to a interest, setting the boolean value to true, it remains false. I have no problems updating the merge-fields values using a similar request. When debugging I noticed one difference between the two and that is that the interests have a "key" and "value" in the request while the merge-fields do not:
The response concerning interests looks like this and it doesn't either have the "key" and "value".
What am I doing incorrectly?
The relevant part of my updateMember function looks like this:
static void updateMember(){
Dictionary<string, object> mergefieldsDic = new Dictionary<string, object>();
mergefieldsDic.Add("POSTCODE","4242");
Dictionary<string, bool> interestDic = new Dictionary<object, bool>();
interestDic.Add("cb0b422bf8", true);
request.RequestFormat = DataFormat.Json;
request.AddBody(new MemberRequest(mergefieldsDic, interestDic));
IRestResponse<Member> response = client.Execute<Member>(request);
foreach (var key in response.Data.merge_fields.Keys)
{
string value = response.Data.merge_fields[key].ToString();
Console.WriteLine(value);
}
foreach (var key in response.Data.interests.Keys)
{
string value = response.Data.interests[key].ToString();
Console.WriteLine(value);
}
}
My MemberRequest class looks like this
public class MemberRequest
{
public MemberRequest(){}
public MemberRequest(Dictionary<string, object> merge_fields, Dictionary<object, bool> interests)
{
this.merge_fields = merge_fields;
this.interests = interests;
}
public string email_address { get; set; }
public string status { get; set; }
public Dictionary<string,object> merge_fields { get; set; }
public Dictionary<object, bool> interests { get; set; }
}
And my Member class looks like this
public class Member
{
public string id { get; set; }
public string email_address { get; set; }
public string status { get; set; }
public Dictionary<string,object> merge_fields { get; set; }
public Dictionary<object,bool> interests { get; set; }
}
I've tried to change the both interests dictionaries to <bool,object>,\<string,bool> and <string,string> but nothing worked.
I manage to get to it to work if I don't use the MemberRequest class and just do request.AddBody(new { interests = new { cb0b422bf8 = true} });
For some reason I did not get an error when I updated the member but when I tried to create a new member with the aforementioned classes I got a bad request from the API that the interests were an array and not an object as it has to be. To solve this I changed my interest Dictionary<string,bool>to Dictionary<string,object>.
Initially when I to changed the Dictionary I ended up having two nearly identical constructors and therefore my error was "The call is ambiguous between the following methods or properties".
[Note: Edited to include requirement to create a collection of FollowUpEvent]
I'm writing a calendaring system. I have a base type of CalendarEvent, and a moderately complex hierarchy of descendant types (AppointmentEvent, ReminderEvent, AppointmentInOfficeEvent, etc). I also want to create a schema structure that specifies for each event a list of follow-up events that should be created:
public class FollowUpEvent
{
public Type EventType { get; set; };
public int OffsetDays { get; set; };
public bool IsRequired { get; set; };
}
static List<FollowUpEvent> EventsToAdd = new FollowUpEvent()
{
new FollowUpEvent() { EventType = typeof(ReminderEvent), OffsetDays = 7, IsRequired = true },
new FollowUpEvent() { EventType = typeof(ReminderEvent), OffsetDays = 14, IsRequired = false }
}
The problem is that the EventType property will accept any object type, I would like to decoratively restrict it to CalendarEvent and its descendants. I realize I could do this in the setter, but ideally I'd like to express it in the declaration of the property.
Is this possible?
(I come from a Delphi background, and I think what I'm looking for is the equivalent of the MyType = class of CalendarEvent declaration.)
I do not think the following makes sense but if you do not want to store the actual CalendarEvent object it will work:
public class FollowUpEvent<T> where T: CalendarEvent
{
public Type EventType { get
{
return this.GetType().GetGenericArguments()[0]
// OR
// return typeof(T)
}
}
public int OffsetDays { get; set; };
public bool IsRequired { get; set; };
}
Have EventType of type BaseEvent from which all the other event types can inherit so that you can restrict the accpeted types.
I have an entity called Invoice that I am extending for data annotations
[MetadataType(typeof(InvoiceMetadata))]
public partial class Invoice
{
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class InvoiceMetadata
{
// Name the field the same as EF named the property - "FirstName" for example.
// Also, the type needs to match. Basically just redeclare it.
// Note that this is a field. I think it can be a property too, but fields definitely should work.
[HiddenInput]
public int ID { get; set; }
[Required]
[UIHint("InvoiceType")]
[Display(Name = "Invoice Type")]
public string Status { get; set; }
[DisplayFormat(NullDisplayText = "(null value)")]
public Supplier Supplier { get; set; }
}
The Uhint[InvoiceType] causes the InvoiceType Editor Template to be loaded for this element.This templates is defined as
#model System.String
#{
IDictionary<string, string> myDictionary = new Dictionary<string, string> {
{ "N", "New" }
, { "A", "Approved" },
{"H","On Hold"}
};
SelectList projecttypes= new SelectList(myDictionary,"Key","Value");
#Html.DropDownListFor(model=>model,projecttypes)
}
i have many such hardcoded status lists in my program.I say hardcoded because they are not fetched from the database. Is there any other way to create templates for drop downs? How do I declare an enum in the model and have the drop down load the enum without having to pass it through a view model ?
Rather than "hard coding" your statuses I would either create an Enum or a Type Safe Enum. For your example I would use the latter.
For each of your required "status lists" create a separate class with your desired settings:
public sealed class Status
{
private readonly string _name;
private readonly string _value;
public static readonly Status New = new Status("N", "New");
public static readonly Status Approved = new Status("A", "Approved");
public static readonly Status OnHold = new Status("H", "On Hold");
private Status(string value, string name)
{
_name = name;
_value = value;
}
public string GetValue()
{
return _value;
}
public override string ToString()
{
return _name;
}
}
Utilizing reflection you can now get the fields of this class to create your required drop down lists. It would be beneficial to your project to either create an extension method or a helper class:
var type = typeof(Status);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
Dictionary<string, string> dictionary = fields.ToDictionary(
kvp => ((Status)kvp.GetValue(kvp)).GetValue(),
kvp => kvp.GetValue(kvp).ToString()
);
You can now create your select list much like you are currently doing:
var list = new SelectList(dictionary,"Key","Value");
Which will create a drop down list with the following html:
<select>
<option value="N">New</option>
<option value="A">Approved</option>
<option value="H">On Hold</option>
</select>
Context :
Since we are developing in C# MVC3, we wanted to have some classes designed to handle the tables on a web page. (Pagination / search / etc...).
So we finally found that it could be the best to have the following classes :
The table object that will hold all other object and knows the current page / current search etc... (misc informations)
public class Table<T> where T : IPrivateObject
{
...
public ICollection<Column<T>> Columns { get; set; }
public ICollection<Row<T>> Rows { get; set; }
public ICollection<RowMenu<T>> Menus { get; set; }
public ICollection<T> Items { get; set; }
public Table(
ICollection<T> inputItems,
ICollection<Column<T>> columns,
ICollection<RowMenuItem<T>> rowMenuItems,
...)
{
...
this.Columns = columns;
}
The column object that knows which property should be displayed and and a header value
public class Column<T> where T : IPrivateObject
{
public string Value { get; set; }
public Expression<Func<T, object>> Property { get; set; }
public Column(Expression<Func<T, object>> property, string value)
{
this.Property = property;
this.Value = value;
}
}
The other classes are not really interesting so i won't post them here.
In the controller, we use these classes like that :
public ActionResult Index(string search = null, string sort = null, int order = 1, int take = 10, int page = 1)
{
ICollection<Person> people = prismaManager.PersonManager.Search(search);
ICollection<Column<Person>> columns= new List<Column<Person>>();
columns.Add(new Column<Person>(Person => Person, "Person"));
columns.Add(new Column<Person>(Person => Person.LastMembershipApproval, "Last Membership approval"));
Table<Person> table = people.ToTable(columns);
}
We are now writing a helper that will display the table correctly.
It works well for the header but we face a problem with the Expressions when we want to use the #Html.DisplayFor() helper.
This is what we currently have for the content :
private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject
{
StringBuilder sb = new StringBuilder();
foreach (var item in table.Items)
{
sb.AppendLine("<tr>");
foreach (var column in table.Columns)
{
sb.AppendLine("<td>");
sb.AppendLine(helper.DisplayFor(obj => ??? ).ToString()); // How should I use the Expression that is stored in the column but for the current element ?
sb.AppendLine("</td>");
}
sb.AppendLine("</tr>");
}
return sb.ToString();
}
For this to work, we should set the value of the "Person" parameter from the Expression stored in the column to the current item.
new Column<Person>(Person => Person, "Person"));
How are we supposed to do that ?
Should we (if it is possible) modify the expression to set the value ?
Should we recreate a new Expression using the old one as a basic expression ?
I've been searching for 3 days now and I can't find any answers.
Thanks for your help.
UPDATE :
The problem is (as #Groo & #Darin Dimitrov said) that the Helper is of type HtmlHelper> and not HtmlHelper.
Any idea how I could get an HtmlHelper from a HtmlHelper> ?
UPDATE :
Person class is as following :
public class Person : IPrivateObject
{
public int Id { get; set; }
public int? AddrId { get; set; }
[DisplayName("First Name")]
[StringLength(100)]
[Required]
public string FirstName { get; set; }
[DisplayName("Last Name")]
[StringLength(100)]
[Required]
public string LastName { get; set; }
[DisplayName("Initials")]
[StringLength(6)]
public string Initials { get; set; }
[DisplayName("Last membership approval")]
public Nullable<DateTime> LastMembershipApproval { get; set; }
[DisplayName("Full name")]
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
public override string ToString()
{
return FullName;
}
}
Here's how you could proceed. Start by writing a custom view data container implementation which could be as simple as:
public class ViewDataContainer : IViewDataContainer
{
public ViewDataContainer(ViewDataDictionary viewData)
{
ViewData = viewData;
}
public ViewDataDictionary ViewData { get; set; }
}
and then just instantiate a HtmlHelper<T> which is what you need:
private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject
{
var sb = new StringBuilder();
sb.AppendLine("<table>");
foreach (var item in table.Items)
{
sb.AppendLine("<tr>");
foreach (var column in table.Columns)
{
var viewData = new ViewDataDictionary<T>(item);
var viewContext = new ViewContext(
helper.ViewContext.Controller.ControllerContext,
helper.ViewContext.View,
new ViewDataDictionary<T>(item),
helper.ViewContext.Controller.TempData,
helper.ViewContext.Writer
);
var viewDataContainer = new ViewDataContainer(viewData);
var itemHelper = new HtmlHelper<T>(viewContext, viewDataContainer);
sb.AppendLine("<td>");
sb.AppendLine(itemHelper.DisplayFor(column.Property));
sb.AppendLine("</td>");
}
sb.AppendLine("</tr>");
}
sb.AppendLine("</table>");
return sb.ToString();
}
UPDATE:
The previous example doesn't handle value types because the expression in the column is of type Expression<Func<T, object>> and when you are pointing to a value type property the value will be boxed and ASP.NET MVC doesn't allow such expressions to be used with the template helpers. To remedy this problem one possibility is to test whether the value was boxed and extract the actual type:
sb.AppendLine("<td>");
var unary = column.Property.Body as UnaryExpression;
if (unary != null && unary.NodeType == ExpressionType.Convert)
{
var lambda = Expression.Lambda(unary.Operand, column.Property.Parameters[0]);
sb.AppendLine(itemHelper.Display(ExpressionHelper.GetExpressionText(lambda)).ToHtmlString());
}
else
{
sb.AppendLine(itemHelper.DisplayFor(column.Property).ToHtmlString());
}
sb.AppendLine("</td>");
There are several things you should change.
The thing that first surprised me is that your table both has a list of Columns and Rows. You should change the design to something like: a Table has a list of Rows, and each Row has a list of Columns (or vice versa).
But this remark is less relevant. I guess a "Column" is something like a "Column definition" and contains no data, but in that case I see no point in having ICollection<Row<T>> instead of just ICollection<T>.
Next, you probably want to store a delegate, like Func<T, object>, instead of an Expression<Func<T, object>>.
Property should at least have private setters (or, even better, readonly backing fields). This is not something that you would want other parts of your code to change.
Naming is very confusing IMHO. I would choose better property names. If I got you right, Value and Property should actually be called HeaderName and GetValue, respectively.
Having said all that, I would change Column to something like this:
public class Column<T> where T : IPrivateObject
{
private readonly string _name;
private readonly Func<T, object> _valueGetter;
/// <summary>
/// Gets the column name.
/// </summary>
public string Name
{
get { return _name; }
}
/// <summary>
/// Gets the value of this column from the
/// specified object.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
public object GetValueFrom(T obj)
{
return _valueGetter(obj);
}
public Column(string columnName, Func<T, object> valueGetter)
{
_name = columnName;
_valueGetter = valueGetter;
}
}
And then simply use this in your loop:
sb.AppendLine(column.GetValueFrom(item).ToString());
You need to compile the expression using expression.Compile() (you have the property expression in your column.Property). This will give you a delegate. You can pass the object there and get the value. You also will need to pass the person, or the T, to the helper method.
This question (along with its answer) explains why you can't easily bind a DataGridView to an interface type and get columns for properties inherited from a base interface.
The suggested solution is to implement a custom TypeConverter. My attempt is below. However, creating a DataSource and DataGridView bound to ICamel still only results in one column (Humps). I don't think that my converter is being used by .NET to decide which properties it can see for ICamel. What am I doing wrong?
[TypeConverter(typeof(MyConverter))]
public interface IAnimal
{
string Name { get; set; }
int Legs { get; set; }
}
[TypeConverter(typeof(MyConverter))]
public interface ICamel : IAnimal
{
int Humps { get; set; }
}
public class MyConverter : TypeConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
if(value is Type && (Type)value == typeof(ICamel))
{
List<PropertyDescriptor> propertyDescriptors = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(ICamel)))
{
propertyDescriptors.Add(pd);
}
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(IAnimal)))
{
propertyDescriptors.Add(pd);
}
return new PropertyDescriptorCollection(propertyDescriptors.ToArray());
}
return base.GetProperties(context, value, attributes);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
DataGridView does not use TypeConverter; PropertyGrid uses TypeConverter.
If it relates to list-controls like DataGridView, then the other answer is wrong.
To provide custom properties on a list, you need one of:
ITypedList on the data-source
TypeDescriptionProvider on the type
Both are non-trivial.
My Workaround happens in the binding of the dgv.
I do need that the base interfaces and the inheriting interfaces remain in the same structure, just because i do other things width the final concerete class, not only show the data on a DataGridView. So, for example:
interface IGenericPerson
{
int ID { get; set; }
string Name { get; set; }
}
interface IOperator : IGenericPerson
{
bool IsAdmin { get; set; }
}
the concrete class:
class Operator : IOperator
{
public Operator(){}
public Operator(int id, string name, bool isAdmin)
{
this.ID = id;
this.Name = name;
thsi.IsAdmin = isAdmin;
}
public int ID { get; set; }
public string name { get; set; }
public bool IsAdmin { get; set; }
}
and in a Gateway Class:
public IList<IOperator> GetOperators()
{
IList<IOperator> list = new List<IOperator>();
list.add(new Operator(112, "Mark Twain", false);
list.add(new Operator(112, "Charles Manson", false);
list.add(new Operator(112, "Richard Nixon", true);
return list;
}
Now, if i try to bind a datagridView like this:
Gateway gt = new Gateway();
dgv.DataSource = gt.GetOperators();
I get a DataGridView with the only bool IsAdmin column from the IOperator Interface, not the ID, neither the Name propertys from its base interface.
but if I do this:
Gateway gt = new Gateway();
IList<IOperator> list = gt.GetOperators();
IList<Operator> ds = new List<Operator>();
foreach(IOperator op in list)
ds.add((Operator)op);
dgv.DataSource = ds;
Everything works in the right way.
In this way i don't need to change the structure of the intarfaces chain, useful for other purposes, and only qhen displaying data i just insert the snippet above.
My Suggestion would be to create a Interface that "reimplements" the propertys you want:
Let's say you have two interfaces:
public interface IHasName1
{
String Name1 { get; set; }
}
public interface IHasName2 : IHasName1
{
String Name2 { get; set; }
}
And a class that implements IHasName2:
public class HasTwoNames : IHasName2
{
#region IHasName1 Member
public string Name1 { get; set; }
#endregion
#region IHasName2 Member
public string Name2 {get; set; }
#endregion
}
Now, thx for figuring that out btw., if you have a List with objects of concrete type HasTwoNames and you bind that list to a dgv, it only displays the member (Name2) of IHasName2.
A "workaround" is to create a new interface "IHasEverything" that inherits from IHasName2 and therefore from IHasName1 and reimplements the Propertys you need in your binding (you can do that with the new statement
public interface IHasEverything : IHasName2
{
new String Name1 { get; set; }
new String Name2 { get; set; }
}
Now your concrete class "HasTwoNames" needs to implement IHasEverything, too:
public class HasTwoNames : IHasName2, IHasEverything
{
...
}
You can bind this List to a datagridview:
public List<IHasEverything> elements = new List<IHasEverything> {
new HasTwoNames { Name1 = "Name1", Name2 = "Name2"},
new HasTwoNames { Name1 = "Name3", Name2 = "Name4"},
};
I know that this is just a workaround and only possible if you can modify the implementing class. But it works.
(If you remove a property from IHasName2, the code will still compile but you get a warning that IHasEverything does not need the new keyword.