Show selected items in a multi-select list box - c#

I have tried various ways to get this to work. I think maybe I have something missing in my model or controller, so I'm posting all three parts.
I have data in a database that shows some advising topics chosen by an advisor in a previous appointment. When the advisor calls up that appointment, the app should display a list of all possible topics with the ones previously chosen highlighted. Everything works except that last bit.
I know I'm retrieving the right information because I can display the selected items separately. I just can't get them to show up selected. Here's the code. I'm cutting out irrelevant parts.
public class AppointmentModel
{ ...
public string AdvisingTopicId { get; set; }
public List<SelectListItem> AdvisingIdList { get; set; }
public SelectList AdvisingTopicNames { get; set; }
}
public class HomeController : AdvisorBaseController
{ ...
var topicCodes = appointment.advising_topic.ToList();
var advisingTopics = new SelectList((from t in topicCodes
select t.name).ToList(), "name");
var topicsList = (from t in db.advising_topic
select new SelectListItem
{
Selected = false,
Text = t.name,
Value = SqlFunctions.StringConvert((double)t.advising_topic_id).Trim()
}).ToList();
foreach (var topicCode in topicCodes)
{
var selTopic = topicsList.Find(x => x.Value == topicCode.advising_topic_id.ToString());
if (selTopic != null)
{
selTopic.Selected = true;
}
} ...
var appointmentModel = new AppointmentModel
{ ...
AdvisingTopicNames = advisingTopics,
AdvisingIdList = topicsList,
};
and then the view
#model AcademicAdvising.Models.AppointmentModel
<h3>Advising Topics</h3>
<ul>
#foreach (var item in Model.AdvisingTopicNames)
{
<li>#Html.DisplayFor(x => item)</li>
}
</ul>
#Html.ListBoxFor(m=>m.AdvisingIdList, new SelectList(Model.AdvisingTopicNames, "Value", "Text", Model.AdvisingTopicNames.SelectedValue))
Note that the foreach correctly displays the selected items. That's just for testing and will be pulled out. the ListBoxFor is where I'm struggling. What I have here doesn't work (shows the full list with nothing highlighted). And that's the bit where I have tried various approaches, with all fails.

It looks like you may have accidentely went a level too deep. You already have a select list which is all the listboxfor function wants.
#Html.ListBoxFor(m=>m.AdvisingIdList, Model.AdvisingTopicNames)
But honestly looking at how you are defining your lists I think what you really want might be
#Html.ListBoxFor(m=>m.AdvisingIdList, Model.AdvisingIdList)

Related

Can't get DropDownList working in .NET (C#)

I'm still pretty new to .NET, but I think I've read everything there is to read on this subject (including similar questions on SO, which is where I got some of the things I've tried). I feel like I've tried everything possible and I still can't get it to work.
I have a Note class and a Category class. Pretty straightforward, each note has a Category property, so I want to have a dropdown list in my Create view that displays categories. I can get a list to display the category names correctly, but that's it. It keeps telling me there's no IEnumerable in my ViewData called "Categories" when there definitely, 1000% for sure is...
The Create action in my NoteController looks like this:
// GET: Create
public ActionResult Create()
{
SelectList items = (new CategoryService()).GetCategories().Select(c => new SelectListItem
{
Value = c.CategoryId.ToString(),
Text = c.Name
}) as SelectList;
ViewData["Categories"] = items;
return View();
}
And I've tried a few variations in the view:
#Html.DropDownListFor(e=>e.CategoryId , (IEnumerable<SelectListItem>) ViewData["Categories"])
#Html.DropDownList("Categories", "Select a Category")
My Create view uses a NoteCreate model, which has this:
public class NoteCreate {
...
[Display(Name = "Category")]
[Required]
public string CategoryId { get; set; }
And my NoteService has a CreateNote method like so:
public bool CreateNote(NoteCreate model)
{
using (var ctx = new ApplicationDbContext())
{
bool isValid = int.TryParse(model.CategoryId, out int id);
if (!isValid)
{
id = 0;
}
var entity =
new Note()
{
OwnerId = _userId,
Title = model.Title,
Content = model.Content,
CreatedUtc = DateTimeOffset.Now,
Status = model.Status,
CategoryId = id
};
ctx.Notes.Add(entity);
return ctx.SaveChanges() == 1;
}
}
I figured I have to turn the ID into a string for the sake of the dropdown list (because SelectListItem's Value and Text are strings), which is why I parse it back into an int here
I tried attaching the list to the ViewBag instead, and I've tried variations of both DropDownListFor and DropDownList
One of those combinations resulted in a dropdown list actually showing, and I don't remember what it was, but selecting an item resulted in a null being passed to the NoteCreate method (model.CategoryId)
Can anyone help me, and potentially many others who will struggle with this in the future because the documentation is so terrible?
UPDATE:
My controller has been refactored to:
// GET: Create
public ActionResult Create()
{
List<SelectListItem> li = new List<SelectListItem>();
List<Category> Categories = (new CategoryService()).GetCategories().ToList();
var query = from c in Categories
select new SelectListItem()
{
Value = c.CategoryId.ToString(),
Text = c.Name
};
li = query.ToList();
ViewBag.Categories = li;
return View();
}
and my view has been refactored to:
#Html.DropDownList("Categories", ViewBag.Categories as SelectList, new { #class = "form-control" })
This is closer, as I can now load the view and see the Category names in the dropdown. However, when I save, model.CategoryId in my CreateNote method is null, so the CategoryId value isn't actually being passed from the dropdown into the model.
If ViewModel is used in the view then its better to paa the data through model properties to the view. No need to put the collection for Dropdownlist in ViewData or ViewBag.
For the detail way of using Dropdownlist through SelectList and pass to the view through, I would refer an answer I had posted:
MVC C# Dropdown list Showing System.Web.SelectListItem on the model and can not blind to controller
The model passed to your view needs a property for CategoryId.
Your Html Helper is looking for CategoryId here:
#Html.DropDownListFor(e=>e.CategoryId
Ok... I figured it out.
It's so stupid.
The key you use to store the SelectList in your ViewData HAS to be the same as the name of the property on the model, even though you can explicitly tell it to use the list using a different key....
So even if you wanted to use the same SelectList for a few different properties (but process them differently in your service, say), you'd have to pass it to the ViewData redundantly for each property
So instead of passing my SelectList through as ViewBag.Categories, I passed it in as ViewBag.CategoryId, and that worked.
I'm going to go drink a lot of alcohol now.
In Controller
List<SelectListItem> li = new List<SelectListItem>();
var query = from of in your_context.Categories
select new SelectListItem()
{
Value = of.CategoryId.ToString(),
Text = of.Name
};
li = query.ToList();
ViewBag.Category_ = li;
View
<div class="col-md-10">
#Html.DropDownList("Categories", ViewBag.Category_ as List<SelectListItem>, new { #class = "form-control" })
</div>

ASP.net MVC/EF/C# add none related table records to query in controller

I am trying to add records from table position for positionName(s) to let user select a position for employee when editing.My last attempts is to add a navigation property like field in company model
public virtual ICollection<Position> Mpositions { get; set; }
But all I got so far is null ref exception or no element in viewModel with property "PositionName" ass per viewbag didn't bother using everybody keeps recommending to avoid it so not going to do so either.
public ActionResult Edit([Bind(Include = "CompanyID,CompanyName,EntityForm,Address,Dissolute,CreationDate,FiscaleYear,Description")] Company company)
{
var GlbUpdate = db.Companies.Include(c => c.Members).Include(p => p.Mpositions);
List<Company> mdlCompanies = new List<Company>();
foreach (var item in GlbUpdate)
{
if ((item.Mpositions==null) || (item.Mpositions.Count() == 0))
{
item.Mpositions = (ICollection<Position>)new SelectList(db.Positions.Except((IQueryable<Position>)db.Positions.Select(xk => xk.Members)), "PositionID", "PositionName");
}
mdlCompanies.Add(item);
//I tried first to edit the Mpositions property directly in gblUpdate
//item.Mpositions = (IEnumerable<Position>)db.Positions.Select(p => new SelectListItem { Value = p.PositionID.ToString(), Text = p.PositionName}) ;
//(ICollection<Position>)db.Positions.ToListAsync();
}
In the view I have this
List<SelectListItem> mPositionNames = new List<SelectListItem>();
#*#this yields no results if I try gettign it from the compani record itself it gives a logic error where all id match all positionNames impossible to select an item and only positions already registered are available on the dropDownlist*#
#{foreach (var item in Model.Mpositions)
{
mPositionNames.Add(new SelectListItem() { Text = item.PositionName, Value = item.PositionID.ToString(), Selected = (false) ? true : false });
#*#selected attribute set to false not an issue, no data to select from :p so far*#
}
}
#*#null exception(if i try to midify Mpositions directly in controler) here or empty list if modify it then put it with original query in a new list*#
<div class="SectionContainer R-sectionContainerData" id="MwrapperDataRight">
#Html.DropDownListFor(mpos => item.PositionID, (SelectList)Model.Mpositions)
</div>
All I want to do is pull the positions table to create a drop downList so users can change the position of an employee but since position has a 1>many relation with employee not companies it is not bound automatically by EF nor I seem to be able to use Include() to add it.
Your query for editing positions are complex. This query must edit person's info only. Using Edit action for formalizing position's edit are not correct.It's againts to Single Responsibility Principle. Use ViewComponents for this situation. Load positions separately from person info.
I found a suitable solution using a model that encapsulate the other entities then using Partialviews/RenderAction so each part handles one entity/operation.

Dynamic list of dropdownlists

I'm working on my first MVC project and I wanted to see if there's a better way to do this. I'm pretty sure there is.
I'm building an application for audits. Each audit has a list of questions, and each question has a list of options to select from. The questions and options can change. So, I have to dynamically build the table of questions and the dropdowns with the question's options.
Here's how I'm creating the SelectLists in the controller
foreach (var r in employeeAuditData.Audit.AuditResults)
{
ViewData["Result" + r.AuditQuestionID] =
new SelectList(r.AuditQuestion.QuestionOptions, "QuestionOptionID", "OptionText", r.QuestionOption);
}
And here's the code for my view
foreach (var r in Model.Audit.AuditResults.OrderBy(r => r.AuditQuestion.Ordinal).ToList())
{
<tr>
<td>#r.AuditQuestion.QuestionText</td>
<td>#Html.DropDownList("Result" + r.AuditQuestionID, "(Select)")</td>
</tr>
}
This works but using concatenation for the ViewData key seems like a funky way to do this. Is there a better way?
How about making a ViewModel?
class AuditResultSelectList
{
int QuestionId { get; set;}
SelectList QuestionOptions { get; set;}
}
Then in your controller:
List<AuditResultSelectList> list = new List<AuditResultSelectList>();
foreach (var r in employeeAuditData.Audit.AuditResults)
{
AuditResultSelectList a = new AuditResultSelectList()
{
QuestionId = r.AuditQuestionID,
QuestionOptions = new SelectList(r.AuditQuestion.QuestionOptions, "QuestionOptionID", "OptionText", r.QuestionOption)
}
list.Add(a);
}
ViewData["QuestionSelectLists"] = list;
Then your view can pull the list out using:
var x = (List<AuditResultSelectList>) ViewData["QuestionSelectLists"];

C# MVC DropDownListFor List of Strings

I am working with a list of string items in mvc that needs to be selected from a drop down list. The drop down list is binding fine, and value's are setting fine, but even though the current item being iterated matches an item in the drop down list it isn't being pre-selected as it's value, can anyone point me in the right direction?
#for (var i = 0; i < Model.StringList.Count; i++)
{
if (BL.Helpers.StringHelpers.Validate(Model.DisplayStringSegments[i]))
{
<div id="editor-field">
#Html.DropDownListFor(m => m.StringList[i], Model.PosterOptions, String.Empty, new { })
</div>
}
else
{
<div id="editor-label">#Model.StringList[i]</div>
#Html.HiddenFor(model => model.StringList[i])
}
}
So for this case, the Options is a list of strings holding only one value, "Test" -> set both as Text and Value;
PosterOptions.Add(new SelectListItem() { Text = "Test", Value = "Test" });
Can anyone tell me why the current StringList[i] isn't being pre selected, even though it has the value of "Test" ?
For anyone that comes across this;
I had to "Hack" a solution, I did this by:
Changing my ViewModel's (Model.Options)
List<SelectListItem> to a List<string>
Changing my drop down list selection to the following, forcing the selected value;
<div id="editor-field">
#{
string currentString = Model.StringList.ElementAt(i).ToString();
}
#Html.DropDownListFor(m => m.StringList[i], new SelectList(Model.Options, currentString), String.Empty, new {})
</div>
Perhaps there is a better way, but this works!
Another way could be setting the current selected item during the list creation, like this:
PosterOptions.Add(new SelectListItem() { Text = "Test", Value = "Test", Selected = true });
I had the same issue and your response helped me. I don't think that's a "hack" though. Because in your question you were using the same SelectList for all the dropdownlists so even though you mention you didn't want to create multiple lists for the drop downs I can't see another way when you have multiple drop downs as you need to specify different selected values.
As a small refactoring you can get rid of the temp variable and access the selected value directly like this:
#Html.DropDownListFor(m => m.StringList[i], new SelectList(Model.Options, Model.StringList[i]), String.Empty, new {})
In your example you don't need to distinguish between text and value but in my case it was required. When that's necessary it can be accomplished by providing the value and text field names for the SelectList. For example, say you need multiple dropdowns with Country values such as:
Country class:
public class Country
{
public string Code { get; set; }
public string Name { get; set; }
}
Model:
public List<string> CustomerCountryList { get; set; }
public IEnumerable<Country> CountryList { get; set; }
and the View:
#Html.DropDownListFor(m => m.CustomerCountryList[i], new SelectList(Model.CountryList, "Code", "Name", Model.CustomerCountryList[i]))

How do I link two collections from a model to a view?

I have a model defined:
public class ExhibitionItemModel
{
public IEnumerable<tblItem> Items { get; set; }
public tblExhibition Exhibition { get; set; }
public IEnumerable<tblExhibitionItem> ExhibitionItems { get; set; }
}
tblItem contains information about a specific item, e.g., description, code, etc.
tblExhibitionItem contains an id from tblItem, an exhibition id tells the system for which exhibitionm a specific item belongs, and a price for that item/exhibition combination.
In my Controller, I am populating ExhibitionItemModel:
ExhibitionItemModel exhibitionItemModel = new ExhibitionItemModel();
exhibitionItemModel.Exhibition = db.tblExhibitions.Find(id);
exhibitionItemModel.ExhibitionItems = (from objExhibitionItems in db.tblExhibitionItems
where objExhibitionItems.ExhibitionID == id
select objExhibitionItems).AsEnumerable<tblExhibitionItem>();
exhibitionItemModel.Items = (from objItem in db.tblItems
select objItem).OrderBy(item => item.Code).AsEnumerable<tblItem>();
return View(exhibitionItemModel);
Im my view, I list out all the items and I want to have a checkbox which is selected according to whether the item is in tblExhibitionItem. Then a textbox to enter the price for the item.
So far, I have:
#foreach (var item in Model.Items)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Code)</td>
<td>#Html.DisplayFor(modelItem => item.Description)</td>
<td>#Html.CheckBoxFor(modelItem => item.AdditionalItem.Value, new { #disabled = "disabled" })</td>
<td>#Html.CheckBox("chkIsSelected")</td>
<td>#Html.Editor("txtPrice")</td>
</tr>
}
As I am a newbie to MVC, I am stuck as to how I establish the link between the checkbox and the Price textbox for an ExhibitionItem to that of the Item. In other words, when I commit the changes to a database, I am populating the ExhibitionItems with the ID of the Item and the Price, depending on whether the Checkbox ("chkIsSelected") is selected.
Additionally, what do I need to change to get the checkbox and Price populated if there are already exhibitionitems in the database?
You can achieve this using ajax.
You need to figure out what checkbox is for what value. So the best way to link it is by using the ID.
So your checkbox code may look like this:
<td>#Html.CheckBox("chkIsSelected", new { #value = item.ID })</td>
Now you'd use jquery to get an array of selected checkbox ids, and pass that to your controller.
$('#submitButton').click(function(e) {
e.preventDefault();
var selectedItems = new Array(); // Create Array
// Add to array
$('#chkIsSelected:checked').each(function() { selectedItems.push($(this).val()); });
$.ajax({
url: '/update-exhibition-item',
data: { items: selectedItems },
traditional: true,
success:function (data) {
// do something.
});
});
});
Now you have to read this into your controller. This could be a partialview, or a proper view with results. It's up to you. Regardless, you'd cycle through the array and update each value that exists in the array. e.g.
[ActionName("update-exhibition-item")]
public ActionResult UpdateExhibition(int[] items)
{
var list = from a in tableA
where items.Contains(a.id)
select a;
// etc.
}

Categories