web-dev-qa-db-fra.com

Pourquoi ListBoxFor ne sélectionne pas les éléments, mais ListBox l'est?

J'ai le code suivant à mon avis:

<%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

<%= Html.ListBox("MultiSelectList", 
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

La seule différence est que le premier assistant est fortement typé (ListBoxFor), et il ne montre pas les éléments sélectionnés (1,2), même si les éléments apparaissent dans la liste, etc. Le ListBox plus simple fonctionne comme prévu.

Il me manque évidemment quelque chose ici. Je peux utiliser la deuxième approche, mais cela me dérange vraiment et j'aimerais le comprendre.

Pour référence, mon modèle est:

public class ProjectEditModel
{
    public Project Project { get; set; }
    public IEnumerable<Project> Projects { get; set; }
    public IEnumerable<Client> Clients { get; set; }
    public IEnumerable<Category> Categories { get; set; }
    public IEnumerable<Tag> Tags { get; set; }
    public ProjectSlide SelectedSlide { get; set; }
}

Mettre à jour

Je viens de changer le nom de ListBox en Project.Categories (correspondant à mon modèle) et il échoue maintenant de sélectionner l'élément.

<%= Html.ListBox("Project.Categories",
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

Je ne comprends évidemment pas la magie qui se produit ici.

Update 2

Ok, c'est purement nommer, par exemple, cela fonctionne ...

<%= Html.ListBox("Project_Tags",
new MultiSelectList(Model.Tags, "Id", "Name", Model.Project.Tags.Select(t => t.Id)))%>

... car le nom du champ est Project_Tags, pas Project.Tags, en fait, tout autre chose que Tags ou Project.Tags fonctionnera. Je ne comprends pas pourquoi cela provoquerait un problème (à part le fait qu'il correspond au nom de l'entité), et je ne suis pas assez bon pour pouvoir creuser et découvrir.

30
Roger Rogers

Je suis tombé sur ce problème moi-même, j'ai finalement réalisé que le problème était une convention de dénomination.

Vous ne pouvez pas nommer la propriété ViewBag ou ViewData contenant la SelectList ou la MultiSelectList au même nom que votre modèle de propriété contenant les éléments sélectionnés. Du moins pas si vous utilisez l'assistant ListBoxFor ou DropDownListFor.

Voici un exemple:

    public class Person
    {
          public List<int> Cars { get; set; }
    }

    [HttpGet]
    public ActionResult Create()
    {
          //wont work
          ViewBag.Cars = new SelectList(carsList, "CarId", "Name"); 

          //will work due to different name than the property.
          ViewBag.CarsList = new SelectList(carsList, "CarId", "Name"); 

          return View();
    }

    //View
    @Html.ListBoxFor(model => model.Cars, ViewBag.CarsList as SelectList)

Je suis sûr qu'il y a beaucoup d'autres façons de le faire, mais cela a résolu mon problème, j'espère que cela aidera quelqu'un!

25
Olivier

J'ai également été coincé avec ce même problème exact et j'ai rencontré le même problème avec ListBox et ListBoxFor.

Peu importe ce que je fais, je ne peux pas obtenir de sélections sur le ListBoxFor. Si je passe à ListBox et que je lui attribue un nom AUTRE que le nom de propriété des données auxquelles je suis lié, des sélections se produisent.

Mais parce que je n'utilise pas ListBoxFor et que les données se trouvent dans une classe de modèle (Model.Departments) par exemple, je n'obtiens pas de liaison de modèle sur le chemin du retour vers mon contrôleur et donc la propriété est nulle.

[~ # ~] modifier [~ # ~] J'ai trouvé une solution publiée par quelqu'un d'autre ici; Défis avec la sélection des valeurs dans ListBoxFor

3
Joshua Hayes

La bonne réponse est que cela ne fonctionne pas très bien. En tant que tel, j'ai lu le code MVC. Ce que vous devez faire est d'implémenter IConvertible et de créer également un TypeConverter.

Donc, dans mon cas, j'avais une classe Country, de sorte que les gens pouvaient choisir dans une liste de pays. Pas de joie à le sélectionner. J'attendais un objet égal à la comparaison sur le selectedItems contre le listitems mais non, ce n'est pas comme ça que ça fonctionne. Malgré le fait que MultiListItem fonctionne et récupère correctement les éléments sélectionnés, au moment où il est lié à votre modèle, tout est basé sur la vérification que la représentation de la chaîne de votre instance d'objet correspond à la chaîne "value" (ou nom s'il manque) dans la liste des éléments du SelectItemList.

Donc, implémentez IConvertible, renvoyez la valeur de chaîne de ToString qui correspondrait à la valeur dans SelectItemList. par exemple, dans mon cas, CountryCode a été sérialisé dans la propriété SelectItemValue, donc dans ToString IConvertible J'ai renvoyé CountryCode. Maintenant, tout se sélectionne correctement.

Je soulignerai que le TypeConverter est utilisé en chemin. Cette fois, c'est l'inverse. Ce Countrycode revient et "EN" doit être converti en instance de classe Country. C'est là que le TypeConverter est entré en jeu. C'est aussi à peu près au moment où j'ai réalisé à quel point cette approche est difficile à utiliser.

p.s ainsi de votre classe Category dont vous avez besoin pour implémenter IConvertible. Si elle provient du framework d'entité comme l'est ma société, vous devrez utiliser la classe partielle pour implémenter IConvertible et implémenter ToString et la décorer avec un TypeConverter que vous avez également écrit .

3
cineam mispelt

Vous pouvez également essayer d'effacer ModelState pour c.Project.Categories dans le contrôleur:

[HttpPost]
public ActionResult Index(ModelType model)
{
    ModelState.Remove("Project.Categories");
    return View("Index", model);
}

Et utilisez la construction suivante:

<%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name"))%>

Où c.Project.Categories est IEnumerable<int>.

Désolé pour mon anglais. Bonne chance!

2
nika1218

Essaye ça

<%= Html.ListBoxFor(c => c.Project.Categories,
    new MultiSelectList(
        Model.Categories
        ,"Id"
        ,"Name"
        ,Model.Project.Tags.Select(
        x => new SelectListItem()
            {
            Selected = true,
            Text = x.TEXT,
            Value = x.ID.ToString()
            }).ToList())

       )
)%>
1
Alex

Bien que ce ne soit pas une réponse à votre question principale, il convient de noter que lorsque MVC génère des noms, il transforme quelque chose comme Project.Tags en Project_Tags, remplaçant les points par des traits de soulignement.

La raison pour laquelle il le fait est qu'un point dans un ID d'élément ressemblerait à un élément nommé Project avec une classe de balises CSS. Clairement une mauvaise chose, d'où la traduction en soulignements pour garder un comportement prévisible.

Dans votre premier exemple,

<%= Html.ListBoxFor(c => c.Project.Categories,
    new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

la zone de liste tente de se lier à Model.Project.Categories pour votre modèle fortement typé qui a été fourni à la page (en utilisant la notation lambda). Je ne suis pas sûr de ce que fait le deuxième paramètre de ListBoxFor.

Quel est le modèle transmis à la page?

1
Alex White

Html.ListboxFor et Html.Listbox fonctionnent très bien lorsque vous ne liez PAS la zone de liste à sa source de données. Je suppose que l'utilisation prévue est la suivante:

// Model
public class ListBoxEditModel
{
    public IEnumerable<Category> Categories { get; set; }
    public IEnumerable<Category> SelectedCategories { get; set; }
}    

Dans la vue:

@Html.ListBoxFor(m => m.SelectedCategories,
                 new MultiSelectList(Model.Categories, "Id", "Name"))
// or, equivalently
@Html.ListBox("SelectedCategories" ,
                 new MultiSelectList(Model.Categories, "Id", "Name"))

Notez que dans ce cas, vous n'avez pas à dire explicitement quelles valeurs sont sélectionnées dans MultiSelectList - le modèle auquel vous vous liez a la priorité, même si vous le faites!

1
Alex