Html.DropDownList default selection troubleshoot - asp.net

I have a selection of names, drawn from my database, to which I prepend the option "None" in my Controller:
var rtrnStaff = (from st in db.PrmTbl_Staffs
join sal in db.PrmTbl_Salutations on st.SalutationID equals sal.ID
where st.Active == true
select new { st.ID, Name = sal.Desc + ". " + st.Name });
List<SelectListItem> staff = new SelectList(rtrnStaff, "ID", "Name").ToList();
staff.Insert(0, (new SelectListItem { Text = "None", Value = "0" }));
ViewData["Staff"] = staff;
I then present this list as a dropdown multiple times in my View, where each time I feed it a variable containing the ID of the desired default option:
#Html.DropDownList(thisSelectID, (IEnumerable<SelectListItem>)ViewData["Staff"], thisStaffID)
Using breakpoints, I can see that these variables are being filled correctly (eg. "3", "2", etc.), but in every case the option shown is the first one: "None". Where is my error?

When you use DropDownList you don't have any way to set the selected value but in the list itself. None of the overloads allows to do it.
So, if you only have one list, it's unavoidable to have all the controls with the same selected item.
Most times it's much better, and make the code much more clear to use a view model class for your view, and use DropDownListFor.
When you use DropDowListFor you set the default property value in the property of the view model, and you don't have to do anything special to set the selected value.
I.e. create a class like this:
public class MyViewModel
{
public List<SelectListItem> Staff { get; set; }
public int FirstId { get; set; }
public int SecondId { get; set; }
}
Then, in your view,
#model MyViewModel
...
#Html.DropDownListFor(m => m.FirstId, Model.Staff);
#Html.DropDownListFor(m => m.SecondId, Model.Staff);
Create an instance of MyViewModel, intialize the list and the Ids, and pass it to the View
MyViewModel model = new MyViewModel
{
// Initialize here
};
...
return View("ViewName", model);
There are additional advantages: you can use the same view model class as the parameter for the POST action method, and the properties are automatically bound to the corresponding properties, you have Intellisense and a typed view is safer to implement.

Related

Unable to Set DropDownlist with pre-selected value

Unable to populate dropdonwlist with pre-selected value.Populating with pre-selected value works with viewbag method.However when i am trying the same with a Model based method i find no luck.
public ActionResult Edit(int?id)
{
Job_ClientVM Vm = new Job_ClientVM();
Vm.Job_Info = Get_jobdetails(id);
//4 is desired pre-selected ID value
Vm.Recruiters = new SelectList(db.Recruiter_Info.ToList(), "Id",
"Recruiter_Name",4);
}
Here is my View
#Html.DropDownListFor(Model => Model.SelectedRecruiterID, Model.Recruiters, "Assign Recruiter", new { #class = "form-control" })
You need to set the value of SelectedRecruiterID in the GET method before you pass the model to the view.
public ActionResult Edit(int?id)
{
Job_ClientVM Vm = new Job_ClientVM();
Vm.Job_Info = Get_jobdetails(id);
Vm.Recruiters = new SelectList(db.Recruiter_Info.ToList(), "Id", "Recruiter_Name");
// Set the value of SelectedRecruiterID
Vm.SelectedRecruiterID = 4;
// Pass the model to the view
return View(Vm);
}
Note that setting the Selected property (the 4th parameter) in the SelectList constructor is pointless - its ignored when binding to a model property (internally the method builds a new IEnumerable<SelectListItem> and sets the Selected property based on the value of the property.

Dynamic table row display on ASP.NET MVC 3

We have a table with 50 rows and want display initially only 10. Just below the table we'd like to add a "Show all" link (not a button) that allow, dynamically (or after reloading the page), to display of all the 50 rows.
What is the most straightforward way to achieve this?
Thanks.
Consider having a model composed of an int and a List<DataRow>:
public class MyDisplayViewModel
{
public int FullCount { get; set; }
public List<DataRow> Data { get; set; }
}
When populating your Data object (in your controller, for example) you can take the full count of your existing data and pass it in the FullCount property.
public ActionResult GetData(int count)
{
MyDisplayViewModel model = new MyDisplayViewModel();
//pseudocode for getting the total number of records
model.FullCount = yourDataProvider(yourDataType).Count();
//pseudocode for getting the the list of records
model.Data = yourDataProvider(yourDataType, count);
return View(model);
}
You can check then in your view if the data passed to it corresponds to all the "available" data. If not, show the link to get all the data from your database.
#{
foreach(var row in Model.Data)
{
//display your data
}
if (Model.Data.Count != Model.FullCount)
{
#Html.ActionLink("Load all", "GetData", "YourControllerName",
new { count = Model.FullCount }, null)
}
}
Of course, this suggestion is at the conceptual level. the view, for example, can implement an Ajax request, in order to get only the remaining DataRows.

Control isn't getting the Selected value from DropDownListFor

I got the following Model:
public class ViewBloqueioNotaFiscal
{
public ViewComboStatus ComboStatus = new ViewComboStatus();
public class ViewComboStatus
{
public int? IdStatusSelecionado { get; set; }
public IEnumerable<SelectListItem> ComboStatus { get; set; }
}
}
The following controller method:
public ViewBloqueioNotaFiscal.ViewComboStatus geraComboStatus(int? statusSelecionado)
{
ViewBloqueioNotaFiscal.ViewComboStatus combo = new ViewBloqueioNotaFiscal.ViewComboStatus
{
IdStatusSelecionado = statusSelecionado,
ComboStatus = new[]{
new SelectListItem { Value = 1, Text = "Op1"},
new SelectListItem { Value = 2, Text = "Op2"}
}
};
return combo;
}
And my aspx is like:
<%: Html.DropDownListFor(x => x.ComboStatus.IdStatusSelecionado, Model.ComboStatus.ComboStatus) %>
Its getting perfectly displayed for selection but when I submit my form, my post method from controller gets the model perfectly with the values except for this combo that Im recieving null value into the model. As its the first one that I try, I think that something is wrong.
Could you guys check that for me? If you have any better solution for this I d like to know too.
thanks for the help !
You are not binding to the correct property of your view model. You are binding to some complex object (ComboStatus) which doesn't make sense.
You should bind the drop down list to the IdStatusSelecionado property:
<%: Html.DropDownListFor(
x => x.ComboStatus.IdStatusSelecionado,
Model.ComboStatus.ComboStatus
) %>
A strongly typed DropDownListFor helper requires at least 2 things on your view model:
A scalar property (int, decimal, string, ...) which will be used to bind to
A collection of value/text pairs.
If the collection of value/text pairs contains an item whose value is equal to the scalar property you used as first argument, this item will be preselected. For example if you wanted to preselect the second item in your example you would set IdStatusSelecionado=2 on your view model.
Side note: Model.ComboStatus.ComboStatus looks terrible. Please rename.

HTML.DropDownList values from multiple sources?

In ASP.NET MVC, is it possible to fill the list of values of a Html.DropDownList from multiple data sources along with multiple manually entered values?
Basically, I envision it being formated like the below using something along the lines of OPTGROUP:
**Group 1**
Manual Item 1
Manual Item 2
**Group 2**
DS1 Item 1
DS1 Item 2
**Group 3**
DS2 Item 1
DS2 Item 2
I've thought about using a view on the DB and getting the data from that, however, I've not really the faintest how to lay it out like above using helpers and to pass the data to it from multiple sources.
Thanks for any help in advance.
As always start with a model (actually start with a unit test but no time for this here):
public class MyModel
{
public string SelectedItem { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var items1 = new[]
{
new { Value = "1", Text = "Manual Item 1" },
new { Value = "2", Text = "Manual Item 2" },
};
// TODO: Go fetch those from your repo1
var items2 = new[]
{
new { Value = "3", Text = "DS1 Item 1" },
new { Value = "4", Text = "DS1 Item 2" },
};
// TODO: Go fetch those from your repo2
var items3 = new[]
{
new { Value = "5", Text = "DS2 Item 1" },
new { Value = "6", Text = "DS2 Item 2" },
};
var items = items1.Concat(items2).Concat(items3);
var model = new MyModel
{
Items = new SelectList(items, "Value", "Text")
};
return View(model);
}
}
And finally a strongly typed view to the model:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyApp.Models.MyModel>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%= Html.DropDownListFor(x => x.SelectedItem, Model.Items) %>
</asp:Content>
You will probably define an intermediary type to avoid the anonymous types that I've used for brevity.
Remark: If your original question was about using an OPTGROUP then ignore my answer and make your intention clear so that you can get a more adapted answer.
It seems it would be easier for you to write your own helper. The basic syntax to do that is this:
// The class can be named anything, but must be static and accessible
public static class HtmlHelperExtensions
{
// The method name is what you want to call on Html,
// in this case Html.CoolSelectList(arguments...)
//
// The method has to be static, and the first argument should be of the type
// you're extending (in this case HtmlHelper, which is the type of the
// Html property on your view). The first argument must be prefixed with the
// "this" keyword, to indicate it's an extension method.
//
// All the following arguments will be arguments that you supply when calling
public static string CoolSelectList(this HtmlHelper helper,
IEnumerable<IEnumerable<CoolThingThatWeMakeAListOf>> groups)
{
// I chose an argument of type IEnumerable<IEnumerable<T>>, since that
// allows you to create each group of item on its own (i.e. get them from
// various data sources) and then add all of them to a list of groups
// that you supply as argument. It is then easy to keep track of which
// items belong to which groups, etc.
// Returned from the extension method is a string you have built, that
// constitutes the html you want to output on your view. I usually use
// the TagBuilder class to build the html.
return "this is what will be on the page";
}
}
Many solutions exist for you problem. One would be the one that Tomas described, another is a Controller Action that returns PartialView, which contains the code to render the input and option tags, another solution would be to have the Controller Action populate the ViewData with a SelectList or have the SelectList as a strong type for your View/ViewUserControl (Partial).

Asp.Net Gridview - One Column is List<string> - Want to Show Only The Last Item

I have an Asp.Net GridView. One of the Columns is a List, but I only want to show the Last Item in the list. How can I do this?
List<string> Column1
I am binding the Gridview to a business object:
public Class GridObject
{
List<string> Column1 { get; set; }
}
EDIT
This worked, but is it the best solution:
<%# ((List<string>)Eval("Column1"))[((List<string>)Eval("Column1")).Count - 1] %>
I would add a property to the object you are binding to, and use that property instead of the list property in your binding.
public Class GridObject
{
List<string> Column1 { get; set; }
public string Column1LastValue
{
get
{ // return Column1.Last(); if linq is available
return Column1[Column1.Count-1];
}
}
}
Edit: Adding a presentation wrapper allows you to unit test what will be displayed. You are doing a translation in the view, which is OK, but since you technically have some logic happening to translate your business object to something proper for display, you would likely want to unit test that translation. Then, any formatting you want to apply to any of your business object fields is wrapped in a testable class, rather than hidden on the untestable view. Here is a sample of how this could be done:
public class GridObjectView
{
private GridObject _gridObject;
public GridObjectView(GridObject gridObject)
{
_gridObject = gridObject;
}
public string Column1
{
get
{
return _gridObject.Column1.Last();
}
}
}
Then to do the databinding, you could do this:
List<GridObject> data = GetGridData();
grid.DataSource = data.Select(g => new GridObjectView(g));
grid.DataBind();
Your best bet is to create a template column and use an inline script to retrieve the value from the list:
<%= ((List<string>)DataBinder.Eval("Column1"))[((List<string>)DataBinder.Eval("Column1")).Count] %>
Or you could store the result in the text of a label or a literal.
Hope that helps

Resources