我正在使用asp.net 4.5使用模型绑定改造旧的Web表单站点。
在DetailsView
中,我有一个下拉列表,允许选择特定的“客户”,另一个允许选择属于该客户的特定“项目”。因此,项目的下拉列表必须在客户端编号上进行过滤,如果用户更改了客户端选择,我想按客户编号过滤项目列表。
我无法弄清楚如何在客户端ddl上获取SelectedIndexChanged
方法来触发Select
的{{1}}方法,所以我总结出唯一的方法是在所有情况下按客户编号过滤项目ddl。我在选择客户端时收到错误消息:
NullReferenceException未被用户代码
处理
在我的aspx中直接指向Project
的ddl。
这是详细信息视图的缩写版本,您可以看到客户端ddl和项目ddl(我在编辑模式下运行):
Projects
有两个<asp:DetailsView ID="AdministratorDetailsView" runat="server" AutoGenerateRows="False"
DataKeyNames="AdministratorNumber" ItemType="BusinessLogic.Administrator"
Width="99%"
SelectMethod="AdministratorDetailsView_GetItem"
UpdateMethod="AdministratorDetailsView_UpdateItem"
DeleteMethod="AdministratorDetailsView_DeleteItem"
FieldHeaderStyle-Width="30%" EditRowStyle-Width="99%"
InsertRowStyle-Width="70%" RowStyle-Width="99%" CssClass="admin"
AutoGenerateDeleteButton="true" AutoGenerateEditButton="true" >
<Fields>
<asp:TemplateField HeaderText="AdministratorCode" SortExpression="AdministratorCode">
<EditItemTemplate>
<asp:Label ID="AdministratorCode" Text="<%# Item.AdministratorCode%>" runat="server" />
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="AdministratorCode" Text="<%# Item.AdministratorCode%>" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="ClientNumber" SortExpression="ClientNumber">
<EditItemTemplate>
<asp:DropDownList ID="ddClients" runat="server"
AutoPostBack="true"
DataTextField="ClientName" DataValueField="ClientNumber"
ItemType="BusinessLogic.Client"
SelectMethod="ddClients_GetList"
SelectedValue="<%# Item.ClientNumber%>"
OnSelectedIndexChanged="AdministratorDetailsView_ddlClients_SelectedIndexChanged"/>
</EditItemTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddClients" runat="server" Enabled="false"
DataTextField="ClientName" DataValueField="ClientNumber"
ItemType="BusinessLogic.Client"
SelectMethod="ddClients_GetList"
SelectedValue="<%# Item.ClientNumber%>"/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Projects" >
<EditItemTemplate>
<asp:DropDownList ID="ddProjects" runat="server" AutoPostBack="true"
DataTextField="ProjectName" DataValueField="ProjectNumber"
ItemType="BusinessLogic.Project"
SelectMethod="ddProjects_GetList"
SelectedValue="<%# Item.ProjectNumber%>" />
</EditItemTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddProjects" runat="server" Enabled="false"
DataTextField="ProjectName" DataValueField="ProjectNumber"
ItemType="BusinessLogic.Project"
SelectMethod="ddProjects_GetList"
SelectedValue="<%# Item.ProjectNumber%>"/>
</ItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
,每个下拉列表一个,以及客户端的SelectMethods
方法:
selectedindexchaneged
一切顺利,直到用户选择触发SelectIndexChanged事件的不同客户端,然后我们到达DataBind(),在那里我们得到null异常(找到ddlProj)。
需要一些关于如何根据新客户端选择刷新项目列表的想法。
如何强制Public Function ddClients_GetList() As List(Of BusinessLogic.Client)
Dim special As New List(Of Client)
special = CurrentClient.ClientList() 'add whole list
Dim NullClient As New Client()
NullClient.Load(0)
NullClient.ClientName = "<-Not Selected-->"
special.Add(NullClient) 'Had to have a client with 0 in the list since most admins don't have anything but 0 inthis field
Return special
End Function
Public Function ddProjects_GetList(<Control("ddClients")> ByVal ClientNumber As Integer) As List(Of BusinessLogic.Project)
Dim special As New List(Of Project)
If ClientNumber = 0 Then
special = CurrentProject.ProjectList() 'add whole list, refine it if Clients Drop Down selected
Else
special = CurrentProject.ProjectList(ClientNumber) 'add whole list, refine it if Clients Drop Down selected
End If
Dim NullProject As New Project()
NullProject.Load(0)
NullProject.ProjectName = "<-Not Selected-->"
special.Add(NullProject) 'Had to have a Project with 0 in the list since most admins don't have anything but 0 inthis field
Return special
End Function
Protected Sub AdministratorDetailsView_ddlClients_SelectedIndexChanged(sender As Object, e As EventArgs)
Dim ddlProj As DropDownList
ddlProj = AdministratorDetailsView.FindControl("ddProjects")
ddlProj.ItemType = "BusinessLogic.Project"
ddlProj.DataBind()
End Sub
再次运行ddProjects
,以避免空引用并重新加载控件?
答案 0 :(得分:1)
原来这个问题不是ModelBinding独有的,而是一般的WebForms问题。 除非添加一些代码,否则您不能在另一个数据控件中具有级联下拉列表,它们彼此依赖。 在我的例子中,我试图将两个下拉列表绑定到底层的详细信息视图,同时设置所选的值。因时间不起作用。在您尝试更改父下拉列表中的选择之前似乎是正确的,然后在第二个上获得null异常,因为它尚未加载。这都是时机。如果您尝试使用保存的值解决它,它会变得太乱。 我的解决方案是,将另一个项目绑定到基础详细信息视图(如果您愿意,可以将其隐藏)。然后添加一些代码以使其与实际值保持同步。不要在二级下拉列表上绑定SelectedValue,这是导致问题的原因。使用添加的字段在代码中管理选定的值。
<asp:DetailsView ID="AdministratorDetailsView" runat="server" AutoGenerateRows="False"
DataKeyNames="AdministratorNumber" ItemType="BusinessLogic.Administrator"
Width="99%" SelectMethod="AdministratorDetailsView_GetItem" UpdateMethod="AdministratorDetailsView_UpdateItem" DeleteMethod="AdministratorDetailsView_DeleteItem"
FieldHeaderStyle-Width="30%" EditRowStyle-Width="99%" InsertRowStyle-Width="70%" RowStyle-Width="99%" CssClass="admin" AutoGenerateDeleteButton="true" AutoGenerateEditButton="true"
>
<Fields>
<asp:TemplateField HeaderText="Client Name" SortExpression="ClientName">
<EditItemTemplate><asp:DropDownList ID="ddClients" runat="server" AutoPostBack="true" DataTextField="ClientName" DataValueField="ClientNumber" ItemType="BusinessLogic.Client" SelectMethod="ddClients_GetList" SelectedValue="<%# Item.ClientNumber%>" OnDataBound="ddClients_DataBound" OnSelectedIndexChanged="AdministratorDetailsView_ddlClients_SelectedIndexChanged" /> </EditItemTemplate>
<ItemTemplate><asp:DropDownList ID="ddClients" runat="server" Enabled="false" DataTextField="ClientName" DataValueField="ClientNumber" ItemType="BusinessLogic.Client" SelectMethod="ddClients_GetList" SelectedValue="<%# Item.ClientNumber%>"/></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="Project Number" >
<EditItemTemplate><asp:Label ID="ProjectNumberLabel" runat="server" Text="<%# BindItem.ProjectNumber%>"></asp:Label></EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Project Name" SortExpression="ProjectNumber">
<EditItemTemplate><asp:DropDownList ID="ddProjects" runat="server" AutoPostBack="true" DataTextField="ProjectName" DataValueField="ProjectNumber" ItemType="BusinessLogic.Project" SelectMethod="ddProjects_GetList" OnDataBound="ddProjects_DataBound" OnSelectedIndexChanged="ddProjects_SelectedIndexChanged" />
</EditItemTemplate>
<ItemTemplate><asp:DropDownList ID="ddProjects" runat="server" Enabled="false" DataTextField="ProjectName" DataValueField="ProjectNumber" ItemType="BusinessLogic.Project" SelectMethod="ddProjects_GetList" SelectedValue="<%# Item.ProjectNumber%>" />
</ItemTemplate>
</asp:TemplateField>
</asp:DetailsView>
在后面的代码中,不是我们如何设置和获取绑定项目编号的值: 受保护的子AdministratorDetailsView_ddlClients_SelectedIndexChanged(sender as Object,e As EventArgs) ''如果这个选择改变了,我们需要改变一些标志,也许是项目ddl ''这会导致Project ddl出现null异常,无法弄清楚如何重新加载它。尝试使用DataBind(),与在Select
的参数中使用ValueProvider相同 Dim ProjectNumberLabel As Label = AdministratorDetailsView.FindControl("ProjectNumberLabel")
ProjectNumberLabel.Text = "0" 'Synchronize to no selection
Dim ddProjects As New DropDownList
ddProjects = AdministratorDetailsView.FindControl("ddProjects") 'Have to use Find Control because ddl is buried in DetailsView
ddProjects.SelectedValue = 0 'Cause it to read Select an Item
ddProjects.DataBind() 'Rebinding it causes the SelectMethod to run.
End Sub
Protected Sub ddProjects_DataBound(sender As Object, e As EventArgs)
'Because of the problem with cascading references, we had to remove the SelectValue declaration from ddProjects.
'So we must make sure that once theddProjects is databound, it points to the selected value from the data
Dim ddlProjects As DropDownList
ddlProjects = AdministratorDetailsView.FindControl("ddProjects")
Dim ProjectNumberLabel As Label = AdministratorDetailsView.FindControl("ProjectNumberLabel")
ddlProjects.SelectedValue = CInt(ProjectNumberLabel.Text)
End Sub
希望其他人认为这很有用。我无法在任何地方找到它。
答案 1 :(得分:0)
很棒的帖子,因为我遇到了类似的难题而且让我思考。我之前已经在模型绑定方法之外做了这个,并且解决方案基本上是相同的,如上所述。我使用了ListView,并添加了方法:
OnItemDataBound = “lvWhatever_ItemDataBound”
触发一个事件,从列表视图数据项中的另一个绑定控件获取相关ID:
if (e.Item.ItemType == ListViewItemType.DataItem)
{
// get the ID from from another control in this item record: lblItemID
ListViewDataItem lvdi = (ListViewDataItem)e.Item;
if (lvdi != null)
{
Label iID = (Label)lvdi.FindControl("lblItemID");
if (iID != null)
其中lblItemID.Text包含相关ID。然后我可以查询获取相关列表,并手动将其绑定到DDL。非常好 - 非常感谢。