无法在嵌套网格中找到由“ControlToValidate”引用的控件ID <controlid> </controlid>

时间:2015-04-16 17:13:03

标签: validation gridview

我有一个嵌套网格,可以在FooterControl中添加一行。我希望能够在用户单击“添加”按钮时验证数据。 我在后面的代码中的RowDataBound方法中添加RequiredFieldValidation。 如果此验证不在此方法中,则一切正常。但添加验证会产生错误 - 它无法找到ControlToValidate引用的ControlId。

如果有人能看到这个,看看我哪里出错了,我会很感激... 这是嵌套网格标记:

    <div id="div<%# Eval("GroupID") %>" style="display:none">
<asp:GridView ID="GroupMemberGridView" runat="server" AutoGenerateColumns="false" OnRowDeleting="GroupMemberGridView_RowDeleting" 
     OnRowCommand="GroupMemberGridView_RowCommand" CssClass="grid" ShowFooter="true">
    <Columns>
        <asp:TemplateField HeaderText="MemberID">
            <ItemTemplate>
                <asp:Label ID="mggvLblMemberID" runat="server" Text='<%# Bind("MemberID") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Member Name" ItemStyle-Wrap="false"> 
            <ItemTemplate>
                <asp:Label ID="mggvLblMemberName" runat="server" Text='<%# Bind("MemberName") %>'></asp:Label>
            </ItemTemplate>               
            <FooterTemplate>
                <asp:DropDownList ID="mggvDDLMemberName" runat="server" 
                   class="chosen-single" data-placeholder="Choose member…">
                </asp:DropDownList>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Active Device(s)">                   
            <ItemTemplate>
                <asp:Label ID="mggvLblActiveDevice" runat="server" Text='<%# (Boolean.Parse(Eval("ActiveDevice").ToString())) ? "Yes" : "No" %>'></asp:Label>
            </ItemTemplate>                   
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Action" ItemStyle-Wrap="false" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate>
                <asp:Button ID="mggvDeleteButton" runat="server" CausesValidation="False" CommandName="Delete" 
                    Text="Delete" CssClass="gridActionbutton"  OnClientClick="return confirm('Are you sure you want to delete this Group Member?')" >
                </asp:Button>
            </ItemTemplate>                                     
            <FooterTemplate>
                <asp:Button ID="mggvAddButton" runat="server" CommandName="Add" Text="Add Member" Width="90%" ClientIDMode="Static"
                    CssClass="gridActionbutton" CausesValidation="true" OnClientClick="return Validate(this);" >
                </asp:Button>
            </FooterTemplate>
        </asp:TemplateField>
    </Columns>
    </asp:GridView>
</div>

这是创建验证

的RowDataBound事件
if (e.Row.RowType == DataControlRowType.DataRow)
{
    int tiGroupID = Convert.ToInt32(UserGroupGridView.DataKeys[e.Row.RowIndex].Value.ToString());                       
    GridView tgvGroupMember = (GridView)e.Row.FindControl("GroupMemberGridView");
    populateGroupMemberGrid(tgvGroupMember, tiGroupID);

    //Get the Footer controls that have the new entry data
    Control tFooterControls = CommonMethods.getFooterControls(tgvGroupMember);                
    DropDownList tddlRecipientNames = tFooterControls.FindControl("mggvDDLMemberName") as DropDownList;
    m_strXmlTableData = m_pagingClient.GetAllPossibleGroupMembers(m_strUserID, tiGroupID);
    DataTable tdtAllGroupMembers = CommonMethods.ParseXML(m_strXmlTableData);
    tddlRecipientNames.DataSource = tdtAllGroupMembers;
    tddlRecipientNames.DataTextField = tdtAllGroupMembers.Columns["MemberName"].ToString();
    tddlRecipientNames.DataValueField = tdtAllGroupMembers.Columns["MemberID"].ToString();
    tddlRecipientNames.DataBind();
    tddlRecipientNames.Items.Insert(0, new ListItem("", "0"));

    RequiredFieldValidator validator = new RequiredFieldValidator();
    validator.ID = "ReqValueDDLMember_" + tiGroupID.ToString();
    string id = ((DropDownList)tFooterControls.FindControl("mggvDDLMemberName")).UniqueID;
    validator.ControlToValidate = ((DropDownList) tFooterControls.FindControl("mggvDDLMemberName")).UniqueID;
    validator.EnableClientScript = true;
    validator.ErrorMessage = "Selection required.";
    validator.CssClass = "message-error-dropdown";
    validator.ValidationGroup = "DDLVal_" + tiGroupID.ToString();
    tgvGroupMember.Controls.Add(validator);

    Button tbtnAdd = tFooterControls.FindControl("mggvAddButton") as Button;
    tbtnAdd.ValidationGroup = "DDLVal_" + tiGroupID.ToString();
}

感谢。

更新 这是一个使用Master.Pages的ASP.NET Web应用程序。最初,我使用.ID并得到了同样的错误。我认为这是因为ID不是唯一的,因为有几个嵌套网格。但是,使用UniqueId标识父网格中的特定嵌套网格。但我得到同样的错误。

如果我在标记中创建验证器,则验证仅适用于第一个嵌套网格。

为什么不能找到控件?

似乎我已尝试将ControlToValidate设置为特定于DropDown控件的唯一ID。

我认为我的问题是控件ID必须是唯一的。这是一个带有Master.Pages的嵌套网格标记,在嵌套网格中为FooterControl指定ID,如'mggvDDLMember',生成ID为'ct100$MainContent$UserGroupGridView$GroupMemberGridView$.....$mggvDDLMemberName'.

在代码中设置ID,如下所示:

ControlToValidate = 'mggvDDLMemberName' 

抛出一个无法找到控件的错误。

使用Control的uniqueID,mggvDDLMemberName是'ct100 $ MainContent $ UserGroupGridView $ GroupMemberGridView $ .. $ mggvDDLMemberName这会产生ControlToValidate在表单标记之外的错误,因为MainContent是PlaceContentHolder:

ControlToValidate = ((DropDownList)tFooterControls.FindControl("mggvDDLMemberName")).UniqueID;

尝试消除'ct100',使控件位于占位符内。我试试这个:

ControlToValidate = ((Dropdown) this.Form.FindControl("MainContent").FindControl("UserGroupGridView").FindControl("ct102").FindControl("GroupMemberGridView").FindControl("ctl13").FindControl("mggvDDLMemberName")).ID;

它会抛出无法找到控件的错误。 我真的不知道如何做到这一点或是否可以做到...... :( 我试过客户端解决方案无济于事。我可能需要再次调查一下。

更新 我回到了验证器的原始设计,而不是动态添加它们。这是带有验证器的FooterTemplate标记:

<FooterTemplate>
    <asp:DropDownList ID="mggvDDLMemberName" runat="server" class="chosen-single" data-placeholder="Choose member…">
    </asp:DropDownList>
    <asp:RequiredFieldValidator ID="ReqValueDDLMemberInsert" runat="server" InitialValue="0" ControlToValidate="mggvDDLMemberName" ValidationGroup="InsertGroupMemberValidation" ErrorMessage="Selection required." CssClass="message-error-dropdown">
   </asp:RequiredFieldValidator>
</FooterTemplate>

这是开始验证的添加按钮:

<FooterTemplate>
   <asp:Button ID="mggvAddButton" runat="server" CommandName="Add" Text="Add Member" Width="90%" CssClass="gridActionbutton" CausesValidation="true" ValidationGroup="InsertGroupMemberValidation" >
   </asp:Button>
</FooterTemplate>

3 个答案:

答案 0 :(得分:2)

我正在添加一个新答案,以澄清一次&amp;对于所有案例中需要设置BaseValidator.ControlToValidate的所有内容,包括后代/嵌套控件,因为有很多问题。我希望将来可能会对其他人以及当前的提问者使用。

我在上面找到的引用不正确/不足,其他地方的帖子也说明应该设置为Control.IDControl.UniqueID。在所有案例中工作的代码使用.NET 4中添加的方法:Control.GetUniqueIDRelativeTo(Control control)。我无法在网络上的任何地方找到任何使用示例(包括此处的stackoverflow)。给定BaseValidator validatorControl control一旦将它们添加到页面的控件层次结构中,就需要以下内容:

validator.ControlToValidate = control.GetUniqueIDRelativeTo(validator);

GetUniqueIDRelativeTo的文档不正确/误导。它声明它返回&#34;指定控件的UniqueID属性的前缀部分&#34 ;;实际上它返回&#34;后缀&#34;部分,即前缀 right 的部分,这是方法名称所暗示的。实际上,它会在从左侧删除control.UniqueID后返回剩余validator.NamingContainer.UniqueID的右侧部分。这会正确处理后代/嵌套控件。

BaseValidator.ControlToValidate设置为Control.IDControl.UniqueID只是上述情况的退化情况,这些情况适用于特殊情况(并且GetUniqueIDRelativeTo()无论如何都会返回相同的结果)。如果(且仅当)验证器和控件位于相同 NamingContainer 中,则Control.ID有效;如果(且仅当)验证器的 NamingContainer 页面,那么Control.UniqueID有效。它们都不适用于其他嵌套/后代情况。

值得注意的是,如果控件的 NamingContainer 与验证者 NamingContainer 相同或后代,则control.GetUniqueIDRelativeTo(validator)throw new InvalidOperationException("This control is not a descendent of the NamingContainer of '<validator>'")。当ASP.NET生成有关无法找到要进行验证的控件的错误时,这就是您最终导致错误的原因。在这种情况下,您需要在页面工作之前对其进行排序,方法是将验证器移动到控件所在位置或最高位置。

对于原始提问者:IDUniqueID对于她的原始代码都不正确,如上所述。我认为她最后通过将验证器从一个单独的位置/ NamingContainer移动到与DropDownList控件相同的位置/ NamingContainer来解决它,因此现在能够摆脱ID退化的情况。

答案 1 :(得分:1)

这对我有用... 不需要唯一的ID,而是ValidationGroup名称。 所以,我在FooterTemplate的标记中创建了所有控件。但我没有命名ValidationGroup属性。我在代码隐藏中做到了这一点。 这是下拉列表,验证器和按钮的标记:

<FooterTemplate>
    <asp:DropDownList runat="server" 
        ID="mggvDDLMemberName" 
        class="chosen-single" 
        data-placeholder="Choose member…">
    </asp:DropDownList>
    <asp:RequiredFieldValidator runat="server" 
        ID="ReqValueDDLMemberInsert" 
        InitialValue="0"                                             
        ControlToValidate="mggvDDLMemberName" 
        ErrorMessage="Selection required." 
        CssClass="message-error-dropdown">
   </asp:RequiredFieldValidator>
</FooterTemplate>


<FooterTemplate>
    <asp:Button runat="server" 
        ID="mggvAddButton" 
        CommandName="Add" 
        Text="Add Member" 
        Width="90%" 
        CssClass="gridActionbutton"/> 
</FooterTemplate>

在RowDataBound事件中,当我将数据绑定到下拉列表时,我为每个控件命名ValidationGroup。

protected void UserGroupGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    try
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            int tiGroupID = Convert.ToInt32(UserGroupGridView.DataKeys[e.Row.RowIndex].Value.ToString());
            GridView tgvGroupMember = (GridView)e.Row.FindControl("GroupMemberGridView");
            populateGroupMemberGrid(tgvGroupMember, tiGroupID);
            //Get the Footer controls 
            Control tFooterControls = CommonMethods.getFooterControls(tgvGroupMember);                
            DropDownList tddlRecipientNames = tFooterControls.FindControl("mggvDDLMemberName") as DropDownList;
            m_strXmlTableData = m_pagingClient.GetAllPossibleGroupMembers(m_strUserID, tiGroupID);
            DataTable tdtAllGroupMembers = CommonMethods.ParseXML(m_strXmlTableData);
            tddlRecipientNames.DataSource = tdtAllGroupMembers;
            tddlRecipientNames.DataTextField = tdtAllGroupMembers.Columns["MemberName"].ToString();
            tddlRecipientNames.DataValueField = tdtAllGroupMembers.Columns["MemberID"].ToString();
            tddlRecipientNames.ValidationGroup = "InsertMember" + tiGroupID.ToString();
            tddlRecipientNames.DataBind();
            tddlRecipientNames.Items.Insert(0, new ListItem("", "0"));//This is needed for the jquery-chosen dropdown to add data-holder text

            RequiredFieldValidator validator = (RequiredFieldValidator)tFooterControls.FindControl("ReqValueDDLMemberInsert");
            validator.ControlToValidate = ((DropDownList)tFooterControls.FindControl("mggvDDLMemberName")).GetUniqueIDRelativeTo(validator);            
            validator.ErrorMessage = "Selection required.";
            validator.CssClass = "message-error-dropdown";
            validator.ValidationGroup = "InsertMember" + tiGroupID.ToString();

            Button tbtnAdd = tFooterControls.FindControl("mggvAddButton") as Button;
            tbtnAdd.ValidationGroup = "InsertMember" + tiGroupID.ToString();
            tbtnAdd.CausesValidation = true;
        }
    }
    catch (Exception ex)
    {
        logger.ErrorException(ex.Message, ex);
        Response.Redirect("~/Error.aspx?use=" + m_strUserType);
    }
}

我希望这可以帮助别人并节省一点时间。

答案 2 :(得分:0)

我自己正在做这样的事情。根据我的理解(例如http://forums.asp.net/t/952691.aspx?Problem+in+Custom+Control+Unable+to+find+Control+To+Validate+Property+),验证者的ControlToValidate必须 设置为控件的ID 提供它被放入同样的 NamingContainer 作为控件可以设置为控件的UniqueID 提供验证器放在实际中页即可。
[编辑此段和链接的说明不充分/不完整;请参阅下面的新单独帖子,以获得正确的讨论和解决方案。]

我认为网格/转发器中的每一行都是它自己的NamingContainer。无论如何,如果您尝试(坚持UniqueID)将验证器添加到页面级别(请注意,validator.ID必须本身是唯一的,或者不设置它? ),现在这会使你的情况有效吗?我有点想知道......

P.S。 或者,要对ID使用控件的ControlToValidate,您必须在控件旁边添加验证器,即将其添加到DropDownList的的控件集合中,而不是在您所在的位置把它?