如何使用多选下拉列表绑定多个值

How to Bind Multiple Values with Multi-Select Dropdown List

提问人:MelB 提问时间:2/6/2023 最后编辑:RenaMelB 更新时间:2/7/2023 访问量:1536

问:

我有一个多选下拉列表,我需要从中获取值并放置在另一个数据表中 - 事件表。

我正在从这个模型中提取下拉列表的值:

using System.ComponentModel.DataAnnotations;

namespace DWITracker.Model
{
    public class Charge
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [Display(Name = "Charge")]
        public string ChargeCode { get; set; }
        [Required]
        [Display(Name = "Charge Description")]
        public string ChargeDesc { get; set; }
    }
}

这是我的 Create.cs:

using DWITracker.Data;
using DWITracker.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;

namespace DWITracker.Pages.Incidents;

[BindProperties]

public class CreateModel : PageModel
{
    private readonly ApplicationDbContext _db;
    public Incident Incident { get; set; }
    public CreateModel(ApplicationDbContext db)
    {
        _db = db;
    }

    public IEnumerable<City> DisplayPIAddressCityData { get; set; }
    public IEnumerable<County> DisplayPIAddressCountyData { get; set; }
    public IEnumerable<Ethnicity> DisplayPIEthnicityData { get; set; }
    public IEnumerable<int> Charge { get; set; }
    public IEnumerable<SelectListItem> ChargeList { get; set; }

    public async Task<PageResult> OnGet()
    {
        await _db.City.Select(a => a.CityName).ToListAsync();
        DisplayPIAddressCityData = await _db.City.ToListAsync();
        await _db.County.Select(a => a.CountyName).ToListAsync();
        DisplayPIAddressCountyData = await _db.County.ToListAsync();
        await _db.Ethnicity.Select(a => a.EthnicityName).ToListAsync();
        DisplayPIEthnicityData = await _db.Ethnicity.ToListAsync();
        var charges = from c in _db.Charge
                         select c;
        ChargeList = charges.Select(c => new SelectListItem { Value = c.Id.ToString(), Text = c.ChargeCode });
        return Page();
    }
    
    public async Task<IActionResult> OnPost()
    {
        await _db.Incident.AddAsync(Incident);
        await _db.SaveChangesAsync();
        TempData["success"] = "Incident Information added successfully.";
        return RedirectToPage("Index");
    }

}

以下是我观点的相关部分:

                    <td style="width: 40%">
                    <div class="mb-3">
                        <label asp-for="Incident.ArrestCharges" class="control-label"></label>
                        <select asp-for="Incident.ArrestCharges" class="form-select" multiple="multiple" asp-items="Model.ChargeList">
                            <option value="">Select Applicable Charge(s)</option>
                        </select>
                    </div>
                </td>

现在,如果我从多选下拉列表中选择多个项目,则只会将第一个选择的 Id 值放入 Incident.ArrestCharges 列中。

我希望它放置一个逗号分隔的多个费用代码(不是 ID)列表,如下所示:VTL 1192-1、VTL 1192-2、VTL 1192-2

希望能得到一些关于我需要删除、添加或更改的内容的指导。这是我第一次创建多选下拉列表并绑定它。谢谢!

asp.net asp.net-core 语法 razor-pages 多选

评论


答:

0赞 Albert D. Kallal 2/7/2023 #1

好的,那么如果用户选择 2 个、1 个或 5 个选项?

然后,您需要在此处添加 2 个、1 个或 5 个 recrods。

你不能这样做:

例如:tblPeople

FirstName: John
LastName: Smith
HotelsBooked: 123, 832, 992

那么,通过存储 3 个值(我预订的酒店),那么现在怎么办?

您将如何报告,或使用sql来查询我预订了哪些酒店?

答:你不能!!

因此,不要突然开始将多个 ID 推入一列,因为从那时起,您就破坏了使用 SQL 查询该数据的所有可能性。

换句话说,你要打破你的关系模型,你这样做了,那么你的整个系统就无法报告我预订了哪些酒店(在上面的例子中)。

既然列数据类型是“数字”和“id”?那么,当然,你不能只是塞进多个 ID 值。正如我上面的例子所示,如果为该列使用字符串,您可以推入多个值,但如前所述,您这样做的 INSTANT 是什么?就在这时,你打破了关系模型,但这并不是真正的关系“巨无霸”,而是现在你没有实际的方法来使用SQL来查询这些数据。(甚至将“ID”转换回正确的值,以便在报表甚至网页中显示。

因此,在上面,如果我想允许该人预订多个酒店,我不能突然加入多个值,而是需要创建一个新的子表。在我看来,这将是

tblPeople - 我上面关于这个人的信息。

tblPeopleBookedInhotels - 这是新的子表。

因此,代替上面的(我试图将多个 id 推入该列,我现在将得到这个:

所以,老:

enter image description here

新增功能:

enter image description here

因此,您可以看到我们希望预订多家酒店的“即时”,然后我们就不能使用用于保存一个值和一个 ID 的一列 (hotel_id)。

问题更严重,因为你的列数据类型无疑是数字类型,因此你甚至不能塞进一串“id's”。

更糟糕的是,你不想这样做,因为从那时起,任何查询该数据的能力也都消失了。

因此,不要将多个值推入该列(不允许多个值)。

您必须获取用户选择,然后遍历这些选择,向这个新表添加一个全新的行,该表现在可以包含许多酒店预订的“列表”。

我的意思是,您可以尝试将其 kluge 化,并将一个列类型从“数字”更改为字符串,然后尝试将多个值作为字符串插入,但随后您破坏了所有假设并期望该列中只有一个“id”值的现有软件。

编辑:写出多行的示例

好吧,假设我们有“人”,然后是他们想去的酒店列表框或其他什么。

所以,我们有这个:

所以,我们将有人物,一张酒店表,当然还有我们的表,允许每个人进行多个酒店预订。

所以,tblHotels预订了。

我们实际上有这个:

enter image description here

因此,假设我们有一些标记来显示 Person。

没什么特别的,只是一些标记,还有那个酒店列表框。

所以,我们有这个标记(不是那么重要)。

<div id="EditRecord" runat="server" style="float:left;display: normal;border:solid 2px;padding:15px;border-radius:12px">

    <h3>Edit Bookings</h3>

    <div style="float:left" class="iForm">
            <label>First Name</label>
            <asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140" /> <br />
            <label>Last Name</label>
            <asp:TextBox ID="tLN" runat="server" f="LastName" Width="140" /> <br />
            <label>City</label>
            <asp:TextBox ID="tCity" runat="server" f="City" Width="140" /><br />
            <label>Prov</label>
            <asp:TextBox ID="tProvince" runat="server" f="Province" Width="75" ></asp:TextBox>
    </div>
    <div style="float:left;margin-left:20px;margin-top:-30px" class="iForm">
        <label>Notes</label> <br />
        <asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine" 
            Height="150px" f="Notes" ></asp:TextBox> <br />
    </div>
    <div style="float:left;margin-left:20px;margin-top:-30px" class="iForm">
        <label>Select Hotels</label> <br />
        <asp:ListBox ID="lstHotels" runat="server"
            DataValueField="id"
            DataTextField="HotelName"
            SelectionMode="Multiple" Height="180px" Width="183px">
        </asp:ListBox>
    </div>
    <div style="clear:both;height:20px"></div>

    <button id="cmdSave" runat="server" class="btn myshadow" onserverclick="cmdSave_ServerClick" >
        <span aria-hidden="true" class="glyphicon glyphicon-floppy-saved"> Save</span> 
    </button>

    <button id="cmdCancel" runat="server" class="btn myshadow" style="margin-left:15px" >
        <span aria-hidden="true" class="glyphicon glyphicon-arrow-left"> Back/Cancel</span>
    </button>

    <button id="cmdDelete" runat="server" class="btn myshadow" style="margin-left:15px">
        <span aria-hidden="true" class="glyphicon glyphicon-trash"> Delete</span>
    </button>

</div>

好的,所以我们的代码必须加载一个人,并加载我们想要选择的酒店的列表框。

因此,要加载的代码是这样的:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            ViewState["PKID"] = 16;  // assume passed value from preivous page
            LoadData();
        }
    }

    void LoadData()
    {
        string strSQL =
            @"SELECT id, HotelName FROM tblHotelsA
             ORDER BY HotelName";
        lstHotels.DataSource = General.MyRst(strSQL); // load list box
        lstHotels.DataBind();

        strSQL = $@"SELECT * from People WHERE ID = {ViewState["PKID"]}";
        DataTable dtPerson = General.MyRst(strSQL);
        General.FLoader(EditRecord, dtPerson.Rows[0]);
    }

现在我们看到/有了这个:

enter image description here

因此,保存代码必须:

将任何编辑保存为一个人,然后为列表框中选择的每家酒店写出一个新行。

所以,保存代码是这样的:

    protected void cmdSave_ServerClick(object sender, EventArgs e)
    {
        int PKID = (int)ViewState["PKID"];
        General.FWriter(EditRecord, PKID, "People"); // send form to database

        // now save/process list box of multiple selected hotels
        // create one new whole row for each hotel selected.
        string strSQL = "SELECT * FROM tblHotelsBooked WHERE ID = 0";
        using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
        {
            using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
            {
                conn.Open();
                DataTable dtBookedHotels = new DataTable();
                dtBookedHotels.Load(cmdSQL.ExecuteReader());
                foreach (ListItem OneHotel in lstHotels.Items)
                {
                    if (OneHotel.Selected) 
                    {
                        DataRow MyNewBooking = dtBookedHotels.NewRow();
                        MyNewBooking["People_ID"] = PKID;
                        MyNewBooking["Hotel_id"] = OneHotel.Value;
                        dtBookedHotels.Rows.Add(MyNewBooking);
                    }  
                }
                SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
                SqlCommandBuilder daU = new SqlCommandBuilder(da);
                // now save/send all rows in data table back to database
                da.Update(dtBookedHotels);
            }
        }
    }

因此,现在我们为每个选定的酒店写出一行数据。

但是,在这个例子中?

我们现在的问题是,当我们返回页面再次编辑时。

现在,我们不仅要加载一个要编辑的 reocrd,还要加载列表框,其中包含可能的选择,并选择/突出显示每个现有酒店,以便用户可以“查看”和“知道”选择了哪些选项。

如果用户取消选择酒店,则必须从 tblHotelsBooked 中删除/删除该子行。

现在,如果这只是一个单一的值(如favorote food),或者类似的东西,那么这个UI可能会相当不错。但是,就我而言,鉴于我们正在选择(并添加)酒店给用户?

然后我们真的需要采用一个用户界面,用户可以在其中看到哪些酒店,添加更多酒店,并删除酒店。

所以,实际上,列表框应该变成一个类似表格的视图(比如网格视图)。

因此,当我们返回该 reocrd 时,我们可以更清楚地查看、选择、添加、删除、编辑、更改该信息。

所以,实际上,我们应该转储列表框,并使用某种类型的网格。

那么结果将是这样的:

enter image description here

现在,在上面的例子中,我们有“更多”而不是选定的列表。

那么,如果我们只有一个包含选定酒店的列表框?当然,但在这个例子中,我们需要“更多”,因为我想添加更多酒店,或者删除它们。

那么,如果我们“坚持”列表框呢?

然后,我们需要添加代码来选择-突出显示现有选择。

然后,当我们取消选择时,我们需要添加代码来“删除”子行。

评论

0赞 MelB 2/7/2023
我明白你在说什么,并准备收到像你这样的回应。在这种情况下,唯一要完成的查询可能是查看包含多个值的列并搜索字符串“containing”。
0赞 Albert D. Kallal 2/7/2023
那么,您必须冒着修改表格结构的风险,并将该列从数字类型更改为文本列类型。这很有可能破坏您的数据库模型,也很有可能破坏任何假定该数字类型列的代码,并且系统中的任何现有查询都可能并且可能会中断。这也意味着您不能再对该列使用 SQL 联接。
0赞 MelB 2/7/2023
你有一个非常简单的工作演示吗?我完全愿意尝试它,只是没有没有样品的技能来执行它。
0赞 Albert D. Kallal 2/7/2023
当然,请看我的编辑 - 我展示了我们如何为列表框中的每个选定项目“写出”一行。
0赞 Rena 2/7/2023 #2

我希望它放置一个逗号分隔的多个收费代码(不是 ID)列表

这是因为您的下拉值已使用 Id 设置,请将该值更改为如下所示:ChargeCode

ChargeList = charges.Select(c => new SelectListItem { Value = c.ChargeCode, Text = c.ChargeCode });

并且多个下拉列表应与要求中的 type 属性匹配,因此您需要创建一个单独的属性来接收多个选定值,然后将此列表转换为带有逗号的字符串。最后,您可以将此字符串值设置为 。List<string>List<string>Incident.ArrestCharges

这是您可以遵循的完整工作演示:

@page
@model CreateModel 
<form method="post">
    <div class="mb-3">
        <label asp-for="Incident.ArrestCharges" class="control-label"></label>
           //change the tag helper value here...........
         <select asp-for="MultiCharge" class="form-select" multiple="multiple" asp-items="Model.ChargeList">
            <option value="">Select Applicable Charge(s)</option>
        </select>
    </div>
    <input type="submit" value="Post" />
</form>

PageModel(页面模型)

[BindProperties]
public class CreateModel : PageModel
{
    //more properties......
    public Incident Incident { get; set; }
    public IEnumerable<SelectListItem> ChargeList { get; set; }
    public List<string> MultiCharge { get; set; }  //receive the multiple selected values
    public async Task<PageResult> OnGet()
    {   
        //other not important code......
        //var charges = from c in _db.Charge
        //             select c;
        //hard-coding here is just for easy testing
        var charges = new List<Charge>()
        {
            new Charge(){ChargeCode="aa",Id=1},
            new Charge(){ChargeCode="bb",Id=2},
            new Charge(){ChargeCode="xx",Id=3}
        };
        // change the Value here....
        ChargeList = charges.Select(c => new SelectListItem { Value = c.ChargeCode, Text = c.ChargeCode });
        return Page();
    }
    public async Task<IActionResult> OnPost()
    {
        //convert list into string with comma.....
        Incident.ArrestCharges = String.Join(",", MultiCharge); 
        //..... 
        return RedirectToPage("Index");
    }

}

评论

0赞 MelB 2/8/2023
也感谢您的回复。我认为两者都是我正在做的事情的可行解决方案。