I have a list of news, when a news is clicked it sends the user to a new page with the specific newsitem.
On the news item page I want to manipulate the object sent so I can change values within this object.
My ItemTappedEvent looks like this
public void goToEvent(object sender, ItemTappedEventArgs e)
{
if (e.Item == null)
{
return;
}
var selectedItem = e.Item; // model
Navigation.PushAsync(new eventItem(selectedItem)); // pass the selected whole item from list to DetaiPage 'selectedItem' using constructor
((ListView)sender).SelectedItem = null;
}
My NewsItem page handles this as a c# object like this
public eventItem(object selectedItem)
{
this.BindingContext = selectedItem;
InitializeComponent();
}
Within my "selectedItem" is a value named "product_wheelchair"
If this has the value "true"
Do I wan't to change it to "Ja". How can i convert my object "selectedItem" so this is possible.
Thanks in advance!
You need to cast the object to your class like:
var mySelectedItem = selectedItem as myClass
After that you can access the properties/fields available in myClass. Obviously if those are private you need to make them public to be accessible.
Normally, the .Dump() extension method in LINQPad shows XNode and its derived class instances as a rendered XML fragment. Sometimes while developing code I would prefer to see actual properties of the object, in the same table form that is dumped for other types, like a table that would show the Name, Value, FirstAttribute and whatsnot properties of the node and their .ToString() values, or interactively expandable collections of subobjects. In short, as if XNode were not handled specially at all.
I am working around this by dumping individual properties, but this is tedious.
This answer suggests writing a custom extension code to achieve a similar effect for another type, namely IEnumerable, but it seems a narrower and rarer case than that which I am dealing with.
Is there an out-of-the box way to do what I want?
LINQPad supports customizing Dump for types. Using some extension methods, you can convert the types to ExpandoObjects and then they will be output with properties.
In My Extensions, after the MyExtensions class, add a top level method:
static object ToDump(object obj) {
if (obj is XObject x)
return x.ToExpando();
else
return obj;
}
In the MyExtensions class, add the following extension methods. I already had the object->Dictionary methods for converting to anonymous objects, so I used those, but you could combine them to create a single ToExpando on object:
public static ExpandoObject ToExpando(this object obj) => obj.ToDictionary().ToExpando();
public static IDictionary<string, object> ToDictionary(this object obj) {
if (obj is IDictionary<string, object> id)
return id;
else {
var dictAnsObj = new Dictionary<string, object>();
foreach (var prop in obj.GetType().GetPropertiesOrFields()) {
try {
dictAnsObj.Add(prop.Name, prop.GetValue(obj));
}
catch (Exception ex) {
dictAnsObj.Add(prop.Name, ex);
}
}
return dictAnsObj;
}
}
public static ExpandoObject ToExpando(this IDictionary<string, object> objDict) {
var e = new ExpandoObject();
var di = (IDictionary<string, object>)e;
foreach (var kvp in objDict)
di.Add(kvp);
return e;
}
You will also need this Type extension:
// ***
// *** Type Extensions
// ***
public static List<MemberInfo> GetPropertiesOrFields(this Type t, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
t.GetMembers(bf).Where(mi => mi.MemberType == MemberTypes.Field | mi.MemberType == MemberTypes.Property).ToList();
If you are okay with just displaying the top level object in class format, you could just use this extension method when you need to:
public static T DumpAs<T, NewT>(this T obj, Func<T, NewT> castFn, string description = null) {
if (description != null)
castFn(obj).Dump(description);
else
castFn(obj).Dump();
return obj;
}
For example,
XElement xn;
xn.DumpAs(x => x.ToExpando());
Otherwise, you will have to comment out the ToDump method or do something tricky with fluent methods to turn it on and off.
This answer depends on the previous answer, but extends it to handle dumping XObjects as classes when desired with an alternative extension method and ToDump method. It uses the same extensions as my previous answer otherwise.
In the MyExtensions class, add a new type of dump and a bool to track status:
public static bool bDumpAsClass = false;
public static object DumpAsClass(this object input, string descr = null) {
bDumpAsClass = true;
if (descr != null)
input.Dump(descr);
else
input.Dump();
bDumpAsClass = false;
return input;
}
Outside the MyExtensions class, add a ToDump method that uses the bool:
static object ToDump(object obj) {
if (MyExtensions.bDumpAsClass) {
if (obj is XObject x)
return x.ToExpando();
}
return obj;
}
Then you can just use DumpAsClass instead of Dump when you want to dump an XObject or descendant as a class, expanding any members as well.
Obviously you could expand the types handled when bDumpAsClass is true.
I'm making a Windows Store App that asks for user input then produces a bunch of pushpins based on that input. When a pushpin is tapped the app navigates to a page with more detail.
Now the problem i'm having is this:
My pages all inherit from the automatically generated LayoutAwarePage so I could potentially make use of SaveState and LoadState to save the pushpins so they don't get wiped on navigation. The thing is that i can't get the pins to save into the Dictionary object supplied by SaveState.
The error I get is "Value cannot be null" and it's referring to the _pageKey variable in LayoutAwarePage.OnNavigatedFrom() and i don't know why it's happening.
I've tried serialising them into a JSON string so i can deserialise it in LoadState, but i get the same result using a string or a List of UIelement.
I think this is all due to my lack of understanding of how SaveState, LayoutAwarePAge and SuspensionManager work. I thought what i was doing would work as the Dictionary is only asking for a string and an object.
I'm not using any other methods from LayoutAwarePage so if there is a better way than using SaveState and LoadState, I'm all ears.
These are the two versions of SaveState i've tried:
Using JSON
protected override void SaveState(Dictionary<String, Object> pageState)
{
List<string> pindata = new List<string>();
List<string> serialisedpins = new List<string>();
foreach (Pushpin ele in map.Children)
{
pindata = ele.Tag as List<string>;
serialisedpins.Add(JsonConvert.SerializeObject(pindata));
}
string jasoned = JsonConvert.SerializeObject(serialisedpins);
pageState["pins"] = jasoned;
}
using a List of UIElement
protected override void SaveState(Dictionary<String, Object> pageState)
{
List<UIElement> pins = new List<UIElement>(map.Children);
pageState["pins"] = pins;
}
The error you're getting (_pagekey value cannot be null) is not really related to what you're saving into the Dictionary. The exception is most likely being thrown in OnNavigateFrom() method of LayoutAwarePage:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
var pageState = new Dictionary<String, Object>();
this.SaveState(pageState);
frameState[_pageKey] = pageState; // <-- throws exception because _pageKey is null
}
If you take a look at the rest of the code of LayoutAwarePage you'll find out the value of _pageKey is being set in OnNavigatedTo method of LayoutAwarePage:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Returning to a cached page through navigation shouldn't trigger state loading
if (this._pageKey != null) return;
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
this._pageKey = "Page-" + this.Frame.BackStackDepth; <-- this line sets the _pageKey value
if (e.NavigationMode == NavigationMode.New)
{
// Clear existing state for forward navigation when adding a new page to the
// navigation stack
var nextPageKey = this._pageKey;
int nextPageIndex = this.Frame.BackStackDepth;
while (frameState.Remove(nextPageKey))
{
nextPageIndex++;
nextPageKey = "Page-" + nextPageIndex;
}
// Pass the navigation parameter to the new page
this.LoadState(e.Parameter, null);
}
else
{
// Pass the navigation parameter and preserved page state to the page, using
// the same strategy for loading suspended state and recreating pages discarded
// from cache
this.LoadState(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey]);
}
}
Usually the reason for that is that you're overriding OnNavigatedTo in your own page without calling base.OnNavigatedTo(e) inside it. The basic pattern of overriding it should always be:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// the rest of your own code
}
This will make sure the base implementation will execute and set the _pageKey value as well as call LoadState() to load the previously saved state if there's any.
I'm using a HeaderColumnNameMappingStrategy to map a csv file with a header into a JavaBean. String values parse fine but any "true" or "false" value in csv doesn't map to JavaBean and I get the following exception from the PropertyDescriptor:
java.lang.IllegalArgumentException: argument type mismatch
The code where it occurs is in CsvToBean, line 64:
protected T processLine(MappingStrategy<T> mapper, String[] line) throws
IllegalAccessException, InvocationTargetException, InstantiationException, IntrospectionException {
T bean = mapper.createBean();
for(int col = 0; col < line.length; col++) {
String value = line[col];
PropertyDescriptor prop = mapper.findDescriptor(col);
if (null != prop) {
Object obj = convertValue(value, prop);
// this is where exception is thrown for a "true" value in csv
prop.getWriteMethod().invoke(bean, new Object[] {obj});
}
}
return bean;
}
protected PropertyEditor getPropertyEditor(PropertyDescriptor desc) throws
InstantiationException, IllegalAccessException {
Class<?> cls = desc.getPropertyEditorClass();
if (null != cls) return (PropertyEditor) cls.newInstance();
return getPropertyEditorValue(desc.getPropertyType());
}
I can confirm (via debugger) that the setter method id correctly retrieved at this point.
The problem occurs in desc.getPropertyEditorClass() since it returns null. I assumed primitive types and its wrappers are supported. Are they not?
I've run into this same issue. The cleanest way is probably to override getPropertyEditor like pritam did above and return a custom PropertyEditor for your particular object. The quick and dirty way would be to override convertValue in anonymous class form, like this:
CsvToBean<MyClass> csvToBean = new CsvToBean<MyClass>(){
#Override
protected Object convertValue(String value, PropertyDescriptor prop) throws InstantiationException,IllegalAccessException {
if (prop.getName().equals("myWhatever")) {
// return an custom object based on the incoming value
return new MyWhatever((String)value);
}
return super.convertValue(value, prop);
}
};
This is working fine for me with OpenCSV 2.3. Good luck!
I resolved this by extending CsvToBean and adding my own PropertyEditors. Turns out opencsv just supports primitive types and no wrappers.
Pritam's answer is great and this is a sample for dealing with datetime format.
PropertyEditorManager.registerEditor(java.util.Date.class, DateEditor.class);
You should write your own editor class like this:
public class DateEditor extends PropertyEditorSupport{
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
#Override
public void setAsText(String text){
setValue(sdf.parse(text));}
}
I'm getting the following error:
'object' does not contain a definition for 'RatingName'
When you look at the anonymous dynamic type, it clearly does have RatingName.
I realize I can do this with a Tuple, but I would like to understand why the error message occurs.
Anonymous types having internal properties is a poor .NET framework design decision, in my opinion.
Here is a quick and nice extension to fix this problem i.e. by converting the anonymous object into an ExpandoObject right away.
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
It's very easy to use:
return View("ViewName", someLinq.Select(new { x=1, y=2}.ToExpando());
Of course in your view:
#foreach (var item in Model) {
<div>x = #item.x, y = #item.y</div>
}
I found the answer in a related question. The answer is specified on David Ebbo's blog post Passing anonymous objects to MVC views and accessing them using dynamic
The reason for this is that the
anonymous type being passed in the
controller in internal, so it can only
be accessed from within the assembly
in which it’s declared. Since views
get compiled separately, the dynamic
binder complains that it can’t go over
that assembly boundary.
But if you think about it, this
restriction from the dynamic binder is
actually quite artificial, because if
you use private reflection, nothing is
stopping you from accessing those
internal members (yes, it even work in
Medium trust). So the default dynamic
binder is going out of its way to
enforce C# compilation rules (where
you can’t access internal members),
instead of letting you do what the CLR
runtime allows.
Using ToExpando method is the best solution.
Here is the version that doesn't require System.Web assembly:
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject))
{
var obj = propertyDescriptor.GetValue(anonymousObject);
expando.Add(propertyDescriptor.Name, obj);
}
return (ExpandoObject)expando;
}
Instead of creating a model from an anonymous type and then trying to convert the anonymous object to an ExpandoObject like this ...
var model = new
{
Profile = profile,
Foo = foo
};
return View(model.ToExpando()); // not a framework method (see other answers)
You can just create the ExpandoObject directly:
dynamic model = new ExpandoObject();
model.Profile = profile;
model.Foo = foo;
return View(model);
Then in your view you set the model type as dynamic #model dynamic and you can access the properties directly :
#Model.Profile.Name
#Model.Foo
I'd normally recommend strongly typed view models for most views, but sometimes this flexibility is handy.
You can use the framework impromptu interface to wrap an anonymous type in an interface.
You'd just return an IEnumerable<IMadeUpInterface> and at the end of your Linq use .AllActLike<IMadeUpInterface>(); this works because it calls the anonymous property using the DLR with a context of the assembly that declared the anonymous type.
Wrote a console application and add Mono.Cecil as reference (you can now add it from NuGet), then write the piece of code:
static void Main(string[] args)
{
var asmFile = args[0];
Console.WriteLine("Making anonymous types public for '{0}'.", asmFile);
var asmDef = AssemblyDefinition.ReadAssembly(asmFile, new ReaderParameters
{
ReadSymbols = true
});
var anonymousTypes = asmDef.Modules
.SelectMany(m => m.Types)
.Where(t => t.Name.Contains("<>f__AnonymousType"));
foreach (var type in anonymousTypes)
{
type.IsPublic = true;
}
asmDef.Write(asmFile, new WriterParameters
{
WriteSymbols = true
});
}
The code above would get the assembly file from input args and use Mono.Cecil to change the accessibility from internal to public, and that would resolve the problem.
We can run the program in the Post Build event of the website. I wrote a blog post about this in Chinese but I believe you can just read the code and snapshots. :)
Based on the accepted answer, I have overridden in the controller to make it work in general and behind the scenes.
Here is the code:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
//This is needed to allow the anonymous type as they are intenal to the assembly, while razor compiles .cshtml files into a seperate assembly
if (ViewData != null && ViewData.Model != null && ViewData.Model.GetType().IsNotPublic)
{
try
{
IDictionary<string, object> expando = new ExpandoObject();
(new RouteValueDictionary(ViewData.Model)).ToList().ForEach(item => expando.Add(item));
ViewData.Model = expando;
}
catch
{
throw new Exception("The model provided is not 'public' and therefore not avaialable to the view, and there was no way of handing it over");
}
}
}
Now you can just pass an anonymous object as the model, and it will work as expected.
I'm going to do a little bit of stealing from https://stackoverflow.com/a/7478600/37055
If you install-package dynamitey you can do this:
return View(Build<ExpandoObject>.NewObject(RatingName: name, Comment: comment));
And the peasants rejoice.
The reason of RuntimeBinderException triggered, I think there have good answer in other posts. I just focus to explain how I actually make it work.
By refer to answer #DotNetWise and Binding views with Anonymous type collection in ASP.NET MVC,
Firstly, Create a static class for extension
public static class impFunctions
{
//converting the anonymous object into an ExpandoObject
public static ExpandoObject ToExpando(this object anonymousObject)
{
//IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
}
In controller
public ActionResult VisitCount()
{
dynamic Visitor = db.Visitors
.GroupBy(p => p.NRIC)
.Select(g => new { nric = g.Key, count = g.Count()})
.OrderByDescending(g => g.count)
.AsEnumerable() //important to convert to Enumerable
.Select(c => c.ToExpando()); //convert to ExpandoObject
return View(Visitor);
}
In View, #model IEnumerable (dynamic, not a model class), this is very important as we are going to bind the anonymous type object.
#model IEnumerable<dynamic>
#*#foreach (dynamic item in Model)*#
#foreach (var item in Model)
{
<div>x=#item.nric, y=#item.count</div>
}
The type in foreach, I have no error either using var or dynamic.
By the way, create a new ViewModel that is matching the new fields also can be the way to pass the result to the view.
Now in recursive flavor
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
new RouteValueDictionary(obj).ForEach(o => expandoObject.Add(o.Key, o.Value == null || new[]
{
typeof (Enum),
typeof (String),
typeof (Char),
typeof (Guid),
typeof (Boolean),
typeof (Byte),
typeof (Int16),
typeof (Int32),
typeof (Int64),
typeof (Single),
typeof (Double),
typeof (Decimal),
typeof (SByte),
typeof (UInt16),
typeof (UInt32),
typeof (UInt64),
typeof (DateTime),
typeof (DateTimeOffset),
typeof (TimeSpan),
}.Any(oo => oo.IsInstanceOfType(o.Value))
? o.Value
: o.Value.ToExpando()));
return (ExpandoObject) expandoObject;
}
Using the ExpandoObject Extension works but breaks when using nested anonymous objects.
Such as
var projectInfo = new {
Id = proj.Id,
UserName = user.Name
};
var workitem = WorkBL.Get(id);
return View(new
{
Project = projectInfo,
WorkItem = workitem
}.ToExpando());
To accomplish this I use this.
public static class RazorDynamicExtension
{
/// <summary>
/// Dynamic object that we'll utilize to return anonymous type parameters in Views
/// </summary>
public class RazorDynamicObject : DynamicObject
{
internal object Model { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name.ToUpper() == "ANONVALUE")
{
result = Model;
return true;
}
else
{
PropertyInfo propInfo = Model.GetType().GetProperty(binder.Name);
if (propInfo == null)
{
throw new InvalidOperationException(binder.Name);
}
object returnObject = propInfo.GetValue(Model, null);
Type modelType = returnObject.GetType();
if (modelType != null
&& !modelType.IsPublic
&& modelType.BaseType == typeof(Object)
&& modelType.DeclaringType == null)
{
result = new RazorDynamicObject() { Model = returnObject };
}
else
{
result = returnObject;
}
return true;
}
}
}
public static RazorDynamicObject ToRazorDynamic(this object anonymousObject)
{
return new RazorDynamicObject() { Model = anonymousObject };
}
}
Usage in the controller is the same except you use ToRazorDynamic() instead of ToExpando().
In your view to get the entire anonymous object you just add ".AnonValue" to the end.
var project = #(Html.Raw(JsonConvert.SerializeObject(Model.Project.AnonValue)));
var projectName = #Model.Project.Name;
I tried the ExpandoObject but it didn't work with a nested anonymous complex type like this:
var model = new { value = 1, child = new { value = 2 } };
So my solution was to return a JObject to View model:
return View(JObject.FromObject(model));
and convert to dynamic in .cshtml:
#using Newtonsoft.Json.Linq;
#model JObject
#{
dynamic model = (dynamic)Model;
}
<span>Value of child is: #model.child.value</span>