skip to Main Content

I have a common file in which I place the most commonly used functions and methods which then get called by other pages, using its name and dot operator. Among these functions, I have implemented a function that uses FindControls. Instead of returning a control, it’s returning null and I am getting the following error:

Object reference is not set to an object and null

mycontrol = (global::System.Web.UI.WebControls.Repeater)Page.FindControl("Repeater1");
mycontrol.DataSource = message; // getting that error at this place

I am expecting the FindControls method to return the corresponding control from the page. What am I doing wrong?

2

Answers


  1. There is nothing stopping you from say creating a general code module for a hodge-podge of general code routines that you want to share among all webforms you have.

    To make this work, you need:

    The general code routine(s) should be static class. And the only real rule you have to follow is that class cannot use class scoped variables, since they are shared among all users. This is a minor issue, and simple adopting a design pattern that you never try to persist class scoped values in that shared code, and you’ll be just fine.

    Next up:

    Some out of the blue code module that is NOT part of a webforms code behind thus has no clue which form/web page it is to operate on, does it?

    I mean, if I have 5 browser tabs open, and I call some general code routine, which web page is that code going to operate on? (Answer: it has no clue).

    So, the simple solution is to always pass the current page, or the current div or whatever you want these general helper routines to operate on.

    So, find control can be used in such routines, but you still have to pass the current page to such routines so find control or whatever still can be used.

    Let’s make up a routine to say fill out a GridView.

    So, we could use this format on the page

            string strSQL =
                @"SELECT * FROM tblHotelsA
                 ORDER BY HotelName";
    
            General.LoadGridData(GridView1, strSQL);
    

    And our static class (our shared code library) is thus this:

    public static class General
    {
    
        public static void LoadGridData(GridView GV, string strSQL)
        {
            GV.DataSource = MyRst(strSQL);
            GV.DataBind();
    
        }
    
    
        public static DataTable MyRst(string strSQL, string sConn = "")
        {
            DataTable rstData = new DataTable();
    
            if (sConn == "")
                sConn = Properties.Settings.Default.TEST4;
    
            using (SqlConnection conn = new SqlConnection(sConn))
            {
                using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
                {
                    cmdSQL.Connection.Open();
                    rstData.Load(cmdSQL.ExecuteReader());
                }
            }
            return rstData;
        }
    

    So, we really did not require find control here, did we?

    However, let’s say for some strange reason, we did want to use find control in our code.

    So, we could say have this in general:

        public static void LoadGridDataS(Page MyPage, string sGV, string strSQL)
        {
            GridView GV = (GridView)MyPage.FindControl(sGV);
            GV.DataSource = MyRst(strSQL);
            GV.DataBind();
    
        }
    

    And thus we would then use from the web page code this:

        protected void cmdLoadGrid_Click(object sender, EventArgs e)
        {
            string strSQL =
                @"SELECT * FROM tblHotelsA
                 ORDER BY HotelName";
    
    
            General.LoadGridDataS(Page,"GridView1", strSQL);
    
        }
    

    So, as above shows, we CAN use find control, but really, in most cases you don’t need to use find control, but just pass the control type in the first place!

    And I think after the first day (or so) I became tired of filling out controls on a page. That same code was to fill out some form data on a page.

    So, I built a routine in General in which I send it the div on the page, and a data row. (So now code can automatically fill out controls for me).

    So, say I have this markup:

    (I going to post this markup – really not important, and you can skip most of this markup).

    However, what I did was add a made-up attribute for each control called "f" which means the data row column name.

    So, now with this markup:

     <div id="EditRecord" runat="server" style="float: left; display: none; padding: 15px">
        <div style="float: left" class="iForm">
            <label>HotelName</label>
            <asp:TextBox ID="txtHotel" runat="server" Width="280" f="HotelName" /><br />
            <label>First Name</label>
            <asp:TextBox ID="tFN" runat="server" Width="140"  f="FirstName"/><br />
            <label>Last Name</label>
            <asp:TextBox ID="tLN" runat="server" Width="140" f="LastName" /><br />
            <label>City</label>
            <asp:TextBox ID="tCity" runat="server" Width="140" f="City" ClientIDMode="Static" /><br />
            <label>Province</label>
            <asp:TextBox ID="tProvince" runat="server" Width="75" f="Province" /><br />
        </div>
        <div style="float: left; margin-left: 20px" class="iForm">
            <label>Description</label>
            <br />
            <asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine"
                Height="150px" f="Description"></asp:TextBox><br />
            <asp:CheckBox ID="chkActive" Text=" Active" runat="server" 
                TextAlign="Right" f="Active" />
            <asp:CheckBox ID="chkBalcony" Text=" Has Balcony" runat="server" 
                TextAlign="Right" f="Balcony"/>
        </div>
        <div style="clear: both"></div>
        <button id="cmdSave" runat="server" class="btn myshadow" type="button"
            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"
            type="button"
            onclick="$('#EditRecord').dialog('close');return false;" >
            <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"
            type="button"
            onserverclick="cmdDelete_ServerClick"
            onclick="if (!confirm('Delete Record?')) {return false};">
            <span aria-hidden="true" class="glyphicon glyphicon-trash">Delete</span>
        </button>
    
        </div>
    

    I can fill out above with this:

        protected void cmdEdit_Click(object sender, EventArgs e)
        {
            Button cmdEdit = (Button)sender;
            GridViewRow gRow = (GridViewRow)cmdEdit.NamingContainer;
    
            string PKID = GridView1.DataKeys[gRow.RowIndex]["ID"].ToString();
            ViewState["PKID"] = PKID;
    
            string strSQL
                = $"SELECT * FROM tblHotelsA WHERE ID = {PKID}";
            DataTable rstData = General.MyRst(strSQL);
            General.FLoader(EditRecord, rstData.Rows[0]);  // send table row to div
    
            // now call the jQuery.ui pop dialog routine.
            string MyJava = $"pophotel('{cmdEdit.ClientID}')";
            Debug.Print(MyJava);
    
            ClientScript.RegisterStartupScript(Page.GetType(), "mypop", MyJava, true);
    
        }
    

    So, in above, we use several general routines, including the floader. (You pass it a data row).

    So, all of the above markup gets filled.

    Note how in this case I passed a div. I used a div since I might want several sections on the page with different data source.

    The code for this floader thus has to loop all of the controls (in that div, but I could pass the whole page).

    the code looks like this:

        public static void FLoader(HtmlGenericControl F, DataRow rst)
        {
            foreach (System.Web.UI.Control c in F.Controls)
            {
                if (c.GetType() == typeof(TextBox))
                {
                    TextBox ctlC = c as TextBox;
                    if (ctlC.Attributes["f"] != null)
                        if (rst.Table.Columns.Contains(ctlC.Attributes["f"]))
                            ctlC.Text = (DBNull.Value.Equals(rst[ctlC.Attributes["f"]]) ? "" : rst[ctlC.Attributes["f"]].ToString());
                }
                else if (c.GetType() == typeof(Label))
                {
                    Label ctlC = c as Label;
                    if (ctlC.Attributes["f"] != null)
                        if (rst.Table.Columns.Contains(ctlC.Attributes["f"]))
                            ctlC.Text = (DBNull.Value.Equals(rst[ctlC.Attributes["f"]]) ? "" : rst[ctlC.Attributes["f"]].ToString());
    
                }
                else if (c.GetType() == typeof(DropDownList))
    
           .etc for more control types.
    

    So, the above loops the controls, and checks for a "f" attribute, and fills out the form, (and then I pop it using jQuery.ui).

    The result looks like this:

    enter image description here

    So, I have many routines in my General class, and you are rather free to call/use such code from a web form, and that includes using find control, passing controls, or often passing the WHOLE page as per my example.

    So, in above, when user clicks on a grid view row, then I use that floader routine to fill out the "hotel controls" in that div, and then I pop that div using jquery.ui. But, as you can see, as you build up the routines in General, then without question, you write less and less code with a nice "grab bag" of utility routines to do basic things like filling out controls in a div without having to hand code what amounts to the same code over and over.

    Login or Signup to reply.
  2. I think that the answer posted by @Albert. D. Kallal is a much more complete and an in depth explanation of how to go about it.

    But since you asked me, I’ll try to give my view here (don’t treat this as an answer – Its just better to post it as an answer than lots of blocks of comments)

    I tried to copy and paste your code from the 3 comments into one block of code, and ended up with the below.

    public static bool DisplayValidationMessages(Control Page, String string) 
    { 
        if (SessionState.Instance.ValidationStatusMessage != null) 
        { 
            Control control = Page; 
            global::System.Web.UI.WebControls.Repeater Repeater1 = null; 
            if (SessionState.Instance.ValidationStatusMessage.MessageList.Count != 0) 
            {
                List<MessageListContract> listMessages = new 
                List<MessageListContract>(SessionState.Instance.ValidationStatusMessage.MessageList); 
                List<MessageListContract> error = new List<MessageListContract>(); 
                ///// Facing error at this line since the FindControl returns null instead of returning a control from another page 
                myrepeater = (global::System.Web.UI.WebControls.Repeater)control.FindControl("Repeater1"); 
                foreach (var msg in listMessages) 
                { 
                    Service.Exception TypeOfMessage = msg.MessageType; 
                    switch (TypeOfMessage)
                    { 
                        case Service.Exception.error: 
                            error.Add(msg); 
                    } 
                }
                ////Facing error at this line since the list error is not assigned to a null valued object 
                myrepeater.DataSource = error; 
                myrepeater.DataBind(); 
                if (Count >= 1) 
                { 
                    errorRepeater = (global::System.Web.UI.HtmlControls.HtmlGenericControl)control.FindControl("errorRepeater"); 
                    errorRepeater.Visible = true; 
                } 
                // "}" I cannot match this curly bracket to another so have remarked/remmed it out?
            } 
            return false; 
        } else { 
            return true; 
        } 
    } 
    

    I’m sorry my C# is not so good, so I ended up with an extra closing curly bracket? AnywaynI remmed out the one which I thought was incorrect.

    And often when we have issues the best way to isolate them is to break them down into their individual parts, what I mean by this is that you are havig problems with the FindControl method/function, so it would be better if you isloated the code for the FindControl method into a seperate function first and tested it with a few controls on different web pages.

    And coming fro a VB background, first I just put a function together to search in a PAGE object for a child control. It only searches for the control and nothing else, no error messages anything else, though I have commented it to show where/what the errors were.

    Then I converted it to C# using Telerik free online code converter.

    So first here is the VB code
    Note also that it ONLY searches the top level of the page, it does recurse through eeach of the child objects/controls, which may themselves contain other objects/controls.

    You may need to create a separate function or modify your findcontrol function to account for child objects (especially if your repeater is not on the directly page but is nested inside another object/control.

    '--------------------------------------------------------------------
    'FindControl Method
    '--------------------------------------------------------------------
    Public Shared Function FindThisControl(ByVal PageObj As Page, FindCtrlName As String) As System.Web.UI.Control
       '
       'find control in page and return found control
       'if no match is found then return nothing/null
    
       Dim FoundCtrl As System.Web.UI.Control = Nothing
       FindThisControl = Nothing
       If IsNothing(PageObj) Then
          'Error - Page object was nothing/null
       ElseIf isnothing(FindCtrlName) Then
          'Error - FindCtrlName was nothing/null
       ElseIf Trim(FindCtrlName) = "" Then
          'Error - FindCtrlName was empty string
       Else
          FoundCtrl = PageObj.FindControl(FindCtrlName)
          If IsNothing(FoundCtrl) Then
             'Error - Control FindCtrlName was not found in PageObj page 
          Else
             FindThisControl = FoundCtrl
          End If
       End If
       '
    End Function
    

    And then converted to C# code

    // --------------------------------------------------------------------
    // FindControl Method
    // --------------------------------------------------------------------
    public static System.Web.UI.Control FindThisControl(Page PageObj, string FindCtrlName)
    {
       // 
       // find control in page and return found control
       // if no match is found then return nothing/null
       //
    
       System.Web.UI.Control FoundCtrl = null;
       FindThisControl = null;
       if (PageObj == null)
       {
          // Error - PageObj page was nothing/null
       }
       else if (FindCtrlName == null))
       {
          // Error - FindCtrlName the name of the control to find was nothing/null
       }
       else if (Strings.Trim(FindCtrlName) == "")
       {
          // Error - FindCtrlName the name of the control to find was an empty string
       }
       else
       {
          //Try to find the control
          FoundCtrl = PageObj.FindControl(FindCtrlName);
          if (FoundCtrl == null)
          {
             // Error - No matching named control was found
          }
          else
             FindThisControl = FoundCtrl;
       }
    }
    

    Niether the VB or C# code will find your Repeater if it is nested inside another control, you MUST loop through each of the child cotrols, check if it has any children, and loop through the, so it can be quite recursive, but good planning and sticking to the basics will help. And that is why I would go with the Albert D. Kallal method, and try to step through it step by step until you understand whats going on.

    I simply posted this here since you asked me specifically.

    Some further reading:
    Microsoft documentation
    https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.page.findcontrol?view=netframework-4.8.1#system-web-ui-page-findcontrol(system-string)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search