I want to pass values from the VIEWBAG as an array of LatLons from my Model in C#. I am not sure how to dynamically loop the Viewbag array and add these cordinates to my javascript array to pass to:
// Create an instance of Google map
var map = new GMap2(document.getElementById("map"));
// Tell the map where to start
map.setCenter(new GLatLng(59.3324, 17.8857), 9);
// Create an array with points
var points = [
new GLatLng(59.6919, 17.8582),
new GLatLng(59.3030, 18.0395),
new GLatLng(58.9789, 17.5341)
];
// Create a new polyline
var polyline = new GPolyline(points, '#ff0000', 5, 0.7);
// Add the polyline to the map using map.addOverlay()
map.addOverlay(polyline);
I want to do something like the above, but without the static array.
Thanks in advance!
EDIT:
currently I have:
var points = [];
#foreach (var item in ViewBag.LatLons)
{
<text>
points.push(new GLatLng(#item.Latitude, #item.Longitude);
</text>
}
But the google map will not show up when this is added, However the points are correctly iterated through from the viewbag data.
Instead of the ViewBag, if you were to use a view model, you could use something like the following:
// Create an array with points
var points = [];
<% foreach (var item in Model.Coords)
{%>
points.push(new GLatLng(<%= item.lat %>, <%= item.lng %>));
<%} %>
which should output into your view as:
var points = [];
points.push(new GLatLng(59.6919, 17.8582));
points.push(new GLatLng(59.3030, 18.0395));
points.push(new GLatLng(58.9789, 17.5341));
//...et cetera
The view data is inherently an object and doesn't support an iterator, so you would have to cast. This saves the need for the cast in the view by having a typed collection. This example could use a simple model like this:
public class CoordsModel
{
public List<CoOrd> Coords {get; set;}
}
public class CoOrd
{
public decimal lat {get; set;}
public decimal lng {get; set;}
}
Related
I have a dynamic list of dynamic lists, which have <input />s that need to be POSTed to an MVC controller/action and bound as a typed object. The crux of my problem is I can't figure out how to manually pick out arbitrary POSTed form values in my custom model binder. Details are below.
I have a list of US States that each have a list of Cities. Both States and Cities can be dynamically added, deleted, and re-ordered. So something like:
public class ConfigureStatesModel
{
public List<State> States { get; set; }
}
public class State
{
public string Name { get; set; }
public List<City> Cities { get; set; }
}
public class City
{
public string Name { get; set; }
public int Population { get; set; }
}
The GET:
public ActionResult Index()
{
var csm = new ConfigureStatesModel(); //... populate model ...
return View("~/Views/ConfigureStates.cshtml", csm);
}
The ConfigureStates.cshtml:
#model Models.ConfigureStatesModel
#foreach (var state in Model.States)
{
<input name="stateName" type="text" value="#state.Name" />
foreach (var city in state.Cities)
{
<input name="cityName" type="text" value="#city.Name" />
<input name="cityPopulation" type="text" value="#city.Population" />
}
}
(There is more markup and javascript, but I leave it out for brevity/simplicity.)
All form inputs are then POSTed to server, as so (parsed by Chrome Dev Tools):
stateName: California
cityName: Sacramento
cityPopulation: 1000000
cityName: San Francisco
cityPopulation: 2000000
stateName: Florida
cityName: Miami
cityPopulation: 3000000
cityName: Orlando
cityPopulation: 4000000
I need to capture the form values, ideally bound as a List<State> (or, equivalently, as a ConfigureStatesModel), as so:
[HttpPost]
public ActionResult Save(List<State> states)
{
//do some stuff
}
A custom model binder seems like the right tool for the job. But I don't know how to know which city names and city populations belong to which state names. That is, I can see all the form keys and values POSTed, but I don't see a way to know their relation:
public class StatesBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
//California, Florida
List<string> stateNames = controllerContext.HttpContext.Request.Form.GetValues("stateName").ToList();
//Sacramento, San Francisco, Miami, Orlando
List<string> cityNames = controllerContext.HttpContext.Request.Form.GetValues("cityName").ToList();
//1000000, 2000000, 3000000, 4000000
List<int> cityPopulations = controllerContext.HttpContext.Request.Form.GetValues("cityPopulation")
.Select(p => int.Parse(p)).ToList();
// ... build List<State> ...
}
}
If I could just know the order all values came in in relation to all other form values, that would be enough. The only way I see to do this is looking at the raw request stream, as so:
Request.InputStream.Seek(0, SeekOrigin.Begin);
string urlEncodedFormData = new StreamReader(Request.InputStream).ReadToEnd();
but I don't want to be messing with manually parsing that.
Also note that the order of the list of states and the order of the lists of cities in each state matter, as I persist the concept of display-order for them. So that would need to be preserved from the form values as well.
I've tried variations of dynamic list binding like this and this. But it feels wrong junking up the html and adding a lot of (error-prone) javascript, just to get the binding to work. The form values are already there; it should just be a matter of capturing them on the server.
The only obvious way I see of building a form that will actually represent which cities belong to which state would require that you use the strongly-typed helpers.
So, I'd use something similar to:
#model Models.ConfigureStatesModel
#for (int outer = 0; outer < Model.States.Count; outer++)
{
<div class="states">
#Html.TextBoxFor(m => m.States[outer].Name, new { #class="state" })
for (int inner = 0; inner < Model.States[outer].Cities.Count; inner++)
{
<div class="cities">
#Html.TextBoxFor(m => m.States[outer].Cities[inner].Name)
#Html.TextBoxFor(m => m.States[outer].Cities[inner].Population)
</div>
}
</div>
}
This will create inputs with form names that the default modelbinder can handle.
The part that requires some additional work is handling the re-ordering. I would use something like this, assuming you are using jQuery already:
// Iterate through each state
$('.states').each(function (i, el) {
var state = $(this);
var input = state.find('input.state');
var nameState = input.attr('name');
if (nameState != null) {
input.attr('name', nameState.replace(new RegExp("States\\[.*\\]", 'gi'), '[' + i + ']'));
}
var idState = input.attr('id');
if (idState != null) {
input.attr('id', idState.replace(new RegExp("States_\\d+"), i));
}
// Iterate through the cities associated with each state
state.find('.cities').each(function (index, elem) {
var inputs = $(this).find('input');
inputs.each(function(){
var cityInput = (this);
var nameCity = cityInput.attr('name');
if (nameCity != null) {
cityInput.attr('name', nameCity.replace(new RegExp("Cities\\[.*\\]", 'gi'), '[' + index + ']'));
}
var idCity = cityInput.attr('id');
if (idCity != null) {
cityInput.attr('id', idCity.replace(new RegExp("Cities_\\d+"), index));
}
});
});
});
This last bit probably requires some tweaking, as it's untested, but it's similar to something I've done before. You would call this whenever the items on your view are added/edited/removed/moved.
I came up with my own solution. It's a little bit of a hack, but I feel it's better than the alternatives. The other solution and suggestions all involved altering the markup and adding javascript to synchronize the added markup -- which I specifically said I did not want to do in the OP. I feel adding indexes to the <input /> names is redundant if said <input />s are already ordered in the DOM the way you want them. And adding javascript is just one more thing to maintain, and unnecessary bits sent through the wire.
Anyways .. My solution involves looping through the raw request body. I hadn't realized before that this is basically just a url-encoded querystring, and it's easy to work with after a simple url-decode:
public class StatesBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
controllerContext.HttpContext.Request.InputStream.Seek(0, SeekOrigin.Begin);
string urlEncodedFormData = new StreamReader(controllerContext.HttpContext.Request.InputStream).ReadToEnd();
var decodedFormKeyValuePairs = urlEncodedFormData
.Split('&')
.Select(s => s.Split('='))
.Where(kv => kv.Length == 2 && !string.IsNullOrEmpty(kv[0]) && !string.IsNullOrEmpty(kv[1]))
.Select(kv => new { key = HttpUtility.UrlDecode(kv[0]), value = HttpUtility.UrlDecode(kv[1]) });
var states = new List<State>();
foreach (var kv in decodedFormKeyValuePairs)
{
if (kv.key == "stateName")
{
states.Add(new State { Name = kv.value, Cities = new List<City>() });
}
else if (kv.key == "cityName")
{
states.Last().Cities.Add(new City { Name = kv.value });
}
else if (kv.key == "cityPopulation")
{
states.Last().Cities.Last().Population = int.Parse(kv.value);
}
else
{
//key-value form field that can be ignored
}
}
return states;
}
}
This assumes that (1) the html elements are ordered on the DOM correctly, (2) are set in the POST request body in the same order, and (3) are received in the request stream on the server in the same order. To my understanding, and in my case, these are valid assumptions.
Again, this feels like a hack, and doesn't seem very MVC-y. But it works for me. If this happens to help someone else out there, cool.
i'm coming from PHP to C#, so please excuse some of my terminology.
Currently i'm working on a small project that requires multiple profiles to be stored in one single file, so i decided to use XML, because INI files (usually my go to guy for text based stuff) are not really supported by C#. At least not in a satisfying way.
Here the basic structure of my XML file:
<profile name="FooBar">
<btnA pressed="actionA" released="actionB" />
<btnB pressed="actionX" released="actionY" />
...
</profile>
<profile name="XYZ">
<btnA pressed="actionA" released="actionB" />
<btnB pressed="actionX" released="actionY" />
...
</profile>
In PHP i would generate an associative array with the following structure:
<?php
foo = array(
'FooBar' => array(
'btnA_pressed' => 'actionA',
'btnA_released' => 'actionB'
// ...
),
'XYZ' => array(
// ...
)
);
EDIT START
Application Class Structure:
Settings (Contains all profiles and a reference to the current profile)
Profile (See below)
The Profile class:
public class Profile
{
private string _name;
public string Name
{
get { return this._name; }
set { this._name = value;}
}
private string _btnA_pressed;
public string BtnA_Pressed { get; set; }
// and so on...
public Profile(var data) { // arg is a placeholder
// associate the data with the properties
}
}
In short, the Settings class holds all profiles and a reference to the selected profile. Access to the profile goes over Settings.CurrentProfile.propertie_Name()
EDIT END
The question is now, how do i achieve the same or a similar thing in C#? Or are there better methods of achieving the same thing?
Thanks for your help in advance!
XML structures can be manipulated very easily with LINQ2XML without the need of typed models (classes).
Reading XML file containing many profile nodes (and i assume your XML file is correct and has one root node), can look like this:
// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
foreach (var profile in profiles)
{
Console.WriteLine(profile.Attribute("name").Value);
// find all button elements
var buttons = profile
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// tag name
var name = button.Name.ToString();
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
}
}
The output is:
FooBar
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
XYZ
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
Reading a single profile XML structure from a string and then creating a new one can look like this:
var xmlCode = #"<profile name=""FooBar""><btnA pressed=""actionA"" released=""actionB"" /><btnB pressed=""actionX"" released=""actionY"" /></profile>";
try
{
// read existing XML structure
var xml = XDocument.Parse(xmlCode); // XDocument.Load("c:\\path\\to\\file.xml");
// find all button elements
var buttons = xml.Root
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// tag name
var name = button.Name.ToString();
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
}
// create xml
// root element
var newXml = new XElement("profile", new XAttribute("name", "FooBaz"),
new XElement("btnA",
new XAttribute("pressed", "actionX"),
new XAttribute("released", "actionXR")),
new XElement("btnB",
new XAttribute("pressed", "actionZ"),
new XAttribute("released", "actionZR")));
Console.WriteLine(newXml.ToString());
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
The output is:
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
<profile name="FooBaz">
<btnA pressed="actionX" released="actionXR" />
<btnB pressed="actionZ" released="actionZR" />
</profile>
You can use LINQ2XML to read the data and fill a list of objects of your Profile type like this:
// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
var listOfProfiles = new List<Profile>();
foreach (var profile in profiles)
{
var profileObject = new Profile("");
profileObject.Name = profile.Attribute("name").Value;
// find all button elements
var buttons = profile
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
profileObject.BtnA_Pressed = pressed;
}
listOfProfiles.Add(profileObject);
}
You can also use XML serialization - you need to describe your XML structure as a class (typed model) and deserialize (read XML file into your class) resp. serialize (write your XML structure to a file). A generic implementation of the methods for serialization resp. deserialization can look like this:
public void SerializeModel<T>(string fqfn, T entity)
{
var xmls = new XmlSerializer(entity.GetType());
var writer = new StreamWriter(fqfn);
xmls.Serialize(writer, entity);
writer.Close();
}
public T DeserializeModel<T>(string fqfn)
{
var fs = new FileStream(fqfn, FileMode.Open);
var xmls = new XmlSerializer(typeof(T));
var r = (T) xmls.Deserialize(fs);
fs.Close();
return r;
}
The typed model that describes your Profile class and the lists contained within, looks like this (note the usage of the different XML serialization attributes):
public class Profiles
{
[XmlElement(ElementName="Profile")]
public List<Profile> Profile { get; set; }
}
public class Profile
{
[XmlArray(ElementName="Buttons")]
public List<Button> Buttons { get; set; }
[XmlAttribute]
public String Name;
}
public class Button
{
[XmlAttribute]
public String Pressed { get; set; }
[XmlAttribute]
public String Released;
}
Creation of an XML file:
var profiles = new Profiles();
var profileA = new Profile();
var profileB = new Profile();
var buttonA = new Button();
var buttonB = new Button();
profileA.Buttons = new List<Button>();
profileB.Buttons = new List<Button>();
profiles.Profile = new List<Profile>();
profileA.Name = "Profile A";
profileB.Name = "Profile B";
buttonA.Pressed = "Pressed A";
buttonA.Released = "Release A";
buttonB.Pressed = "Pressed B";
buttonB.Released = "Release B";
profileA.Buttons.Add(buttonA);
profileB.Buttons.Add(buttonB);
profiles.Profile.Add(profileA);
profiles.Profile.Add(profileB);
var xmlFile = "d:\\temp\\xml\\profile_model.xml";
SerializeModel<Profiles>(xmlFile, profiles);
The new XML file looks like this (note, the structure was slightly modified because of the way XML serialization in .NET handles arrays/lists):
<?xml version="1.0" encoding="utf-8"?>
<Profiles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Profile Name="Profile A">
<Buttons>
<Button Released="Release A" Pressed="Pressed A" />
</Buttons>
</Profile>
<Profile Name="Profile B">
<Buttons>
<Button Released="Release B" Pressed="Pressed B" />
</Buttons>
</Profile>
</Profiles>
The file can be then read like this:
var input = DeserializeModel<Profiles>(xmlFile);
foreach (var profile in input.Profile)
{
var b = profile.Buttons.First();
Console.WriteLine(String.Format("{0} - {1} - {2}", profile.Name, b.Pressed, b.Released));
}
The output is as expected:
Profile A - Pressed A - Release A
Profile B - Pressed B - Release B
Both approaches have advantages and disadvantages.
IMO the answer to your question (changed a bit) Is XML the correct approach for saving structured data to a file? is - definitely yes! Nowadays XML is one of the standards for representing / manipulating / exchanging structured data and data generally (data kept in a string). As someone already mentioned INI files were not really meant to represent complex nested structures.
In contrast to PHP where you might be used to doing a lot of arrays and magic-string based stuff, C# is a Statically Typed language where it's usually recommended to create a proper, formally defined, structured Data Model which allows you to manipulate the Data you're dealing with in a strongly typed manner.
This means, for example, that if you're dealing with personal information related data, and you need to deal with the concepts of last name, first name, and age you will want to create a class containing these pieces of data in the form of Properties, like so:
public class Person
{
public string LastName {get;set;}
public string FirstName {get;set;}
public int Age {get;set;}
//And so on...
}
Notice how each property has an adequate Data Type that allows you to constrain what values it can contain. For example, the fact that the Age property is of type int (integer numbers) automatically means you can never have something like "abc" inside it, and code like this:
var person = new Person();
person.Age = "abc";
will also produce a compile-time error, rather than blowing up at run time, or producing any sorts of inconsistencies in stored data.
Likewise, if your Person objects (in the real world data you're trying to model) have a relation to, say an Address, you're also going to create the Address class:
public class Address
{
public string Line1 {get;set;}
public string Line2 {get;set;}
public string City {get;set;}
//And so on...
}
And then model this Person -> Address relationship by creating an additional property in the Person class:
public class Person
{
//.. All of the above.
public Address Address {get;set;}
}
Which can be illustrated with a diagram like this:
This approach has the following advantages:
It provides Compile Time checking for correctness of the code. Compile-Time errors can be trapped (and need to be corrected) very early in the development cycle. For example, you can't do something like:
person.LatsName where the property name LastName is mispelled LatsName because the compiler "knows" there is no such property in the object model and thus you recieve a compile-time error rather than having the application crash at run-time.
It provides IDE support for features such as AutoComplete because the IDE "knows" the object model you're dealing with and thus it knows what properties/methods/events (Members) every class has, and what are the expected parameters and data types for each of these.
So, to answer the overarching question, once you created a proper object model, you can use .Net's built-in Serialization/Deserialization mechanisms to transform instances of classes in your object model (with actual values and properties) to a data format that can be stored in a file on disk, such as XML.
Assuming you have a class for profile with all the relevant fields/properties, you can do
Profile profile = new Profile();
profile.actionA = "actionA"; // or whatever you have
var file = new System.IO.StreamWriter(#"c:\temp\Profile_as_xml.xml");
var writer = new System.Xml.Serialization.XmlSerializer(typeof(Profile));
writer.Serialize(file, profile);
file.Close();
See http://msdn.microsoft.com/en-us/library/ms172873.aspx
Okay so I'm working with MVC4 in C# and I have to fill a javascript array with elements from the view's model. I'm trying to dynamically populate a Chart.js pie chart. This is my example code:
<script src="~/Scripts/Chart.js"></script>
<script type="text/javascript">
var data = [
{
value: 30,
color: "#F38630"
},
{
value: 50,
color: "#E0E4CC"
},
{
value: 100,
color: "#69D2E7"
}
]
//Get the context of the canvas element we want to select
var ctx = document.getElementById("myChart").getContext("2d");
var myNewChart = new Chart(ctx).Pie(data);
//Get context with jQuery - using jQuery's .get() method.
var ctx = $("#myChart").get(0).getContext("2d");
//This will get the first returned node in the jQuery collection.
var myNewChart = new Chart(ctx);
new Chart(ctx).Pie(data, options);
</script>
I want to be able to add elements to the data array in a for loop. I tried using .push like this
data.push([{
value: 30,
color: "#F38630"
}]);
But it stops the chart from being created entirely. Any idea how I can do something like:
foreach (var item in Model.List) {
data.add(item.value)
}
You can be even more awesome than that.
Create your own basic type to represent you Value/Color pair.
public class MyType
{
public string Value {get;set;}
public string Color {get;set;}
}
In your razor (or code behind) create an array for that type:
#{
var values = new List<MyType>();
// populate with some values.
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(keyValues.ToArray());
}
Then in your Javascript:
<script type="text/javascript">
var data = #json; // TADA!!
//Get the context of the canvas element we want to select
var ctx = document.getElementById("myChart").getContext("2d");
var myNewChart = new Chart(ctx).Pie(data);
//... etc.
</script>
If you come across any problems serializing that list, I recommend using Json.Net to improve C# native serializers.
Your data is an array (see the brackets []).
Now you try to add an array with a single object to the array:
[{ value...
Just change it to an object {} and you will be fine.
{ value ... }
I have a object defined in my C# code behind as
public List<Dictionary<string, string>> attributesList
{
get;
set;
}
now I need to fill this object from Jquery
That is, in my Jquery file I m getting certain values that I need to fill in this object.
I am stuck on how to create a JSON object from the following code
selectedAttributes.each(function (key, value) {
var attributeName = value.attributes.title.value;
var attributeValue = $('#' + attributeName + ' option:selected').text();
});
that can be supplied to the attributesList
I need to put (attributeName, attributeValue) pair in the attributelist object
I know I am not clear enough in asking this question, but if any information is required please comment and I'll reply almost instantly.
A Dictionary would be just an object in JS. You're able to address the items within the dictionary by it's name.
dic['name'] = 'value'; // valid
dic.name = 'value'; // also valid
var attrName = 'name';
dic[attrName] ='value'; // also valid
That should be enough info to let you accomplish your task.
What I am trying to achieve is to split a string into multiple adresses like "NL,VENLO,5928PN" which getLocation will return a "POINT( x y)" string value.
This works. Next I need to create a WayPointDesc object for each location. And each of these objects has to be pushed into the WayPointDesc[]. I have tried various methods but I cannot find a feasable option so far. My last resort is to hardcode a maximum amount of waypoints but I would rather avoid such a thing.
Using a list is unfortunately not an option... I think.
This is the function:
/* tour()
* Input: string route
* Output: string[] [0] DISTANCE [1] TIME [2] MAP
* Edited 21/12/12 - Davide Nguyen
*/
public string[] tour(string route)
{
// EXAMPLE INPUT FROM QUERY
route = "NL,HELMOND,5709EM+NL,BREDA,8249EN+NL,VENLO,5928PN";
string[] waypoints = route.Split('+');
// Do something completly incomprehensible
foreach (string point in waypoints)
{
xRoute.WaypointDesc wpdStart = new xRoute.WaypointDesc();
wpdStart.wrappedCoords = new xRoute.Point[] { new xRoute.Point() };
wpdStart.wrappedCoords[0].wkt = getLocation(point);
}
// Put the strange result in here somehow
xRoute.WaypointDesc[] waypointDesc = new xRoute.WaypointDesc[] { wpdStart };
// Calculate the route information
xRoute.Route route = calculateRoute(waypointDesc);
// Generate the map, travel distance and travel time using the route information
string[] result = createMap(route);
// Return the result
return result;
//WEEKEND?
}
Arrays are fixed-length, if you want to dynamically add elements, you need to use some type of linked list structure. Also, your wpdStart variable was out of scope when you were adding it originally.
List<xRoute.WaypointDesc> waypointDesc = new List<xRoute.WaypointDesc>();
// Do something completly incomprehensible
foreach (string point in waypoints)
{
xRoute.WaypointDesc wpdStart = new xRoute.WaypointDesc();
wpdStart.wrappedCoords = new xRoute.Point[] { new xRoute.Point() };
wpdStart.wrappedCoords[0].wkt = getLocation(point);
// Put the strange result in here somehow
waypointDesc.add(wpdStart);
}
If you really want the list as an array later, use: waypointDesc.ToArray()