Advanced ASP.NET WebGrid - Dynamic Columns and Rows - asp.net-mvc

I'm trying to create a WebGrid which has to be very dynamic. The columns are defined in a list, which I've done like so:
#{
List<WebGridColumn> columns = new List<WebGridColumn>();
foreach (var column in Model.Columns)
{
columns.Add(new WebGridColumn() { ColumnName = column.Name, Header = column.Name });
}
}
#grid.GetHtml(
columns: columns)
All well and good, but the problem I have is with the rows. I'll try and explain...
For this question let's say we have two columns for Name and Address.
I have a collection of row objects, lets say SearchResult objects. A SearchResult contains a Dictionary of any number of attributes, such as Name, Address, Phone, Height, Bra Size, or anything (think of the EAV pattern). I need to access the attributes based on Column Name.
I figured I could do this using format, but I can't seem to figure it out. I want something like this:
columns.Add(new WebGridColumn() { ColumnName = column.Name, Header =
column.Header, Format = #<text>#item.Attributes[column.Name]</text> });
This sort of works but despite creating the format for the separate columns, the rows get populated with only the last column's format. i.e.:
Name Address
1 Main Street 1 Main Street
45 Paradise Av 45 Paradise Av
etc

I think it should work if you leave out the "ColumnName" (superfluous anyway), and also make the dynamic expression a bit more explicit:
columns.Add(
new WebGridColumn() {
Header = column.Header,
Format = (item) => #Html.Raw("<text>" + #item.Attributes[column.Name] + "</text>")
}
);

This issue is related to reference variables. You need to have the Format property in terms of the other properties of the WebGridColumn. This is how I would do it:
#{
List<WebGridColumn> columns = new List<WebGridColumn>();
foreach (var column in Model.Columns)
{
var col = new WebGridColumn();
col.Header = column.Name;
col.Format = (item) => #Html.Raw("<text>" + #item.Attributes[col.Header] + "</text>");
columns.Add(col);
}
}

Related

how to concatinate two string from single column in mvc razor

I have a database called news and have a column called description, I have two take the Last two entries from table (description column) and display the result as a single string string , ie, want to append the data in the Second last column to the last data. But I am not able to append the text,and deleted the code.
controller
public ActionResult Index()
{
var news = db.News.OrderByDescending(u => u.Id).FirstOrDefault();
return View(news);
}
view
#model Project.Models.News
<a href="#Url.Action("NewsInnerPage", "News")">
<marquee>
<p>
#Html.Raw(Model.Description)
</p>
</marquee>
</a>
sql column
I want to get the value from table last two entries as a single string . can anyone please help me to write the code . how can i append string ???
var news = db.News.OrderByDescending(u => u.Id).Take(2).ToList();
var concatenatedNews = new News {
Description = news[0].Description + news[1].Description
};
or you could do it all in one line
var news = new News { Description = string.Join("", news.OrderByDescending(u => u.Id).Take(2).Select(u => u.Description)) };

MVC 3 WebGrid with a dynamic source

I have a dynamic list of data with a dynamic number of columns being created by a PIVOT function. Everything more or less works, but I wanted to apply some custom formatting to some of the columns. I figured out how to get a list of the columns by just taking the first row and casting it like so:
var columns = Model.Comparisons.Select(x => x).FirstOrDefault() as IDictionary<string, object>;
Next I decided to create my List by looping over the "columns", which works as long as I reference the dynamic fields in the "format:" clause by their dynamic field name directly for example:
foreach (var c in columns)
{
switch (c.Key)
{
case "Cost":
cols.Add(grid.Column(
columnName: c.Key,
header: c.Key,
format: (item) => Convert.ToDecimal(item.Cost).ToString("C")));
break;
default:
cols.Add(grid.Column(columnName: c.Key, header: c.Key, format: item => item[c.Key]));
break;
}
}
The "default" does not dynamically get the value of each record. I believe it has to do with the "item[c.Key]" vs item.Cost. The problem is I don't want to have to write different case for each field, primarily because I don't know them ahead of time as the data can change. There are about 6 fields that will always be present. I do know however the datatype, which is why I wanted to put a custom format on them.
EDIT
I managed to solve this by writing an extension method.
public static class DynamicDataHelper
{
public static WebGridColumn GetColumn(this HtmlHelper helper, string vendor)
{
return new WebGridColumn()
{
ColumnName = vendor,
Header = vendor,
Format = (item) => helper.ActionLink(
(string)Convert.ToDecimal(item[vendor]).ToString("C"),
"VendorSearch",
"Compare",
new { Vendor = vendor, mpn = item.MPN },
new { target = "_blank" })
};
}
}
I edited my post with the Html Helper that I wrote that will in effect build the custom WebGridColumn objects I was having problems with. The "vendor" is passed in from the View and is then resolved at runtime. It works great.

How do I get Choice Values from a Document library's Choice column in code

I am fairly new to SharePoint development and as you may all know that it is very basic for one to know how to access fields in a choice column...
My problem:
I want to access the values of the Check Boxes from a Choice Column.
For Example:
I have a document library called Libe, this document library has a custom column with type Choice and has 4 checkboxes with the values:
Category 1
Category 2
Category 3
Category 4
How do I get the values like literally the text values of what is in the Check Box List: "Category 1", "Category 2" ... "Category 4".
Any ideas?
I can access the column fine and get the selected values, I just do not know how to get the values the user can choose from.
Answer
SPFieldMultiChoice Fld = (SPFieldMultiChoice)list.Fields["Column"];
List<string> fieldList = new List<string>();
foreach (string str in Fld.Choices)
{
fieldList.Add(str);
}
Above is the answer, I can't answer my own question until I have a 100 rep.
using (SPSite site = new SPSite("http://servername/"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["ListName"];
string values = list["yourColumn"] as string;
string[] choices = null;
if (values != null)
{
choices = values.Split(new string[] { ";#" }, StringSplitOptions.RemoveEmptyEntries);
}
}
}
You can try this code for getting choice field value from document library.

Populating Selectlist from multiple fields

My problem is pretty simple. Lets say I have a dropdown with users.
in the database i have 3 fields for my user table:
user_id
user_name
user_firstname
in my MVC app i want to link those users to projects. so thats why i want the dropdown.
now, i want to have a selectlist, with the ID as the value, and the firstname AND lastname to be the 'text'
SelectList sl = new SelectList(users, "user_id", "user_name");
now how do i get the first name also in the text? this should be fairly easy, but seems it isnt...
Use LINQ to transform your list of users into a list of SelectListItem.
var selectOptions =
from u in yourUserQuery
select new SelectListItem {
Value = u.user_id,
Text = u.user_firstname + " " + u. user_name
};
You can then convert that to a SelectList however you see fit. I personally like to define a .ToSelectList() extension method for IEnumerable<SelectListItem>, but to each his own.
You need to build a list from your database in the format you need. Here is what I did after my database read into a DataTable,
IList<UsrInfo> MyResultList = new List<UsrInfo>();
foreach (DataRow mydataRow in myDataTable.Rows)
{
MyResultList.Add(new UsrInfo()
{
Usr_CD = mydataRow["USR_NR"].ToString().Trim(),
Usr_NA = mydataRow["USR_NA_LAST"].ToString().Trim() + " , " +
mydataRow["USR_NA_FIRST"].ToString().Trim()
});
}
return new SelectList(MyResultList, "Usr_CD", "Usr_NA");

ASP.MVC 1.0 Checkbox values with ViewModel and for specific ID

All,
I've read through a lot of posts about Checkboxes and ASP.MVC but I'm not that much wiser.
My scenario:
I have a strongly typed View where I pass a collection of summary objects to the view for rendering in a for-each. This summary object contains label data based on a unique id. I also add a checkbox to the row so do so via:
<td>
<%= Html.CheckBox("markedItem", Model.MarkedItem, new { TrackedItemId = Model.Id })%>
</td>
When I perform a POST to get the submitted results my action method takes the strongly typed ViewModel back but the original summary object that I used to create the list is not populated.
Ok, this is annoying, but I can understand why so I'll live with it.
What I then do is to add a new property to my ViewModel called "MarkedItem" which is a string collection.
On postback this marked item is filled with the before and after states if the checkbox has changed but nothing to tell me which key they were for. Just to clarify, if I send this
TrackedItemId = A, Value = false
TrackedItemId = B, Value = true
TrackedItemId = C, Value = false
and set the page to this:
TrackedItemId = A, Value = true
TrackedItemId = B, Value = true
TrackedItemId = C, Value = false
I will get back this:
MarkedItem[0] = true
MarkedItem[1] = false
MarkedItem[2] = true
MarkedItem[3] = false
in other words [0] is the new value and [1] is the old value, [2] and [3] represent values that haven't changed.
My questions are:
Is this right - that I get before and after in this way? Is there any way to only send the latest values?
How can I get hold of the custom attribute (TrackedItemId) that I've added so that I can add meaning to the string array that is returned?
So far I like MVC but it not handling simple stuff like this is really confusing. I'm also a javascript noob so I really hope that isn't the answer as I'd like to return the data in my custom viewmodel.
Please make any explanations/advice simple :)
<p>
<label>
Select project members:</label>
<ul>
<% foreach (var user in this.Model.Users)
{ %>
<li>
<%= this.Html.CheckBox("Member" + user.UserId, this.Model.Project.IsUserInMembers(user.UserId)) %><label
for="Member<%= user.UserId %>" class="inline"><%= user.Name%></label></li>
<% } %></ul>
and in the controller:
// update project members
foreach (var key in collection.Keys)
{
if (key.ToString().StartsWith("Member"))
{
int userId = int.Parse(key.ToString().Replace("Member", ""));
if (collection[key.ToString()].Contains("true"))
this.ProjectRepository.AddMemberToProject(id, userId);
else
this.ProjectRepository.DeleteMemberFromProject(id, userId);
}
}
With thanks to Pino :)
ok, one hack I've come up with - I really hate that I have to do this but I don't see another way round it and I'm sure it will break at some point.
I've already implemented by own ModelBinder to get round some other issues (classes as properties for example) so have extended it to incorporate this code. We use Guid's for all our keys.
If there are any alternatives to the below then please let me know.
Html
<%= Html.CheckBox("markedItem" + Model.Id, false)%>
C#
(GuidLength is a const int = 36, Left and Right are our own string extensions)
//Correct checkbox values - pull all the values back from the context that might be from a checkbox. If we can parse a Guid then we assume
//its a checkbox value and attempt to match up the model. This assumes the model will be expecting a dictionary to receive the key and
//boolean value and deals with several sets of checkboxes in the same page
//TODO: Model Validation - I don't think validation will be fired by this. Need to reapply model validation after properties have been set?
Dictionary<string, Dictionary<Guid, bool>> checkBoxItems = new Dictionary<string, Dictionary<Guid, bool>>();
foreach (var item in bindingContext.ValueProvider.Where(k => k.Key.Length > GuidLength))
{
Regex guidRegEx = new Regex(#"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
if (guidRegEx.IsMatch(item.Key.Right(GuidLength)))
{
Guid entityKey = new Guid(item.Key.Right(GuidLength));
string modelKey = item.Key.Left(item.Key.Length - GuidLength);
Dictionary<Guid, bool> checkedValues = null;
if (!checkBoxItems.TryGetValue(modelKey, out checkedValues))
{
checkedValues = new Dictionary<Guid, bool>();
checkBoxItems.Add(modelKey, checkedValues);
}
//The assumption is that we will always get 1 or 2 values. 1 means the contents have not changed, 2 means the contents have changed
//and, so far, the first position has always contained the latest value
checkedValues.Add(entityKey, Convert.ToBoolean(((string[])item.Value.RawValue).First()));
}
}
foreach (var item in checkBoxItems)
{
PropertyInfo info = model.GetType().GetProperty(item.Key,
BindingFlags.IgnoreCase |
BindingFlags.Public |
BindingFlags.Instance);
info.SetValue(model, item.Value, null);
}

Resources