Ray Fischer Ответов: 2

Невозможно использовать findcontrol для извлечения текстового поля в gridview - двойные источники данных


I am trying to assign a value to a read-only TextBox which is lodged inside a DataGrid in a webpage. The TextBox is being reconstructed with each Postbox using automation called Add_TextBoxes. The TextBoxes inside the DataGrid can either be modified themselves by making an entry within the box (calls a method, TextBox_Changed) which is assigned idndividually, dynamically to each TextBox when they are created, or else via the automation which is causing me the problem. The automated call to update the textboxes within the grid is anchored inside longer textboxes, which are parsed based on a certain logic to distribute pieces of the Text (Substrings) to each of the textboxes in the grid. The problem I am having is that the textboxes inside the Grid are not being found. I am getting a Null Error exception. Here are some snippets of Code -

Внутри моей Page_Load (вложенный, если вызвать добавление текстовых полей в сетку после обратной передачи)

   else if (Session["SrcCtrl"].ToString() == "Noms")
    {
        this.Add_TxtBoxes();
        this.parse_Nomkey();
    }
}


Вот метод Add_TxtBoxes:

protected void Add_TxtBoxes()

        {

            List<sessVar> sessVars = ret_sv_data();
            int rw = 0;
            int cl = 0;

            // Cursor through all 31 positions

            for (int i = 0; i <= 30; i++)
            {

                //  Check to see if a new row for the data table / data grid is needed, - skipped on first iteration (row = 0)

                if (i > 0)
                {
                    if (newRow(sessVars[i].Nompos) == true)
                    {
                        rw = rw + 1;
                    }
                }

                //  Check to see if a column change is needed (col + 2), as we move through each component of the nomenclature

                if (i >= 1)
                {
                    if (sessVars[i - 1].Keyvis == false)
                    {
                        rw = 0;
                        cl = cl + 2;
                    }
                }

                //  If session variable is visible in the grid, add its text box and description one column over

                if (sessVars[i].Keyvis == true)
                {
                    Control thsctrl = this.FindControl("txtBoxNomPt" + rw + cl);

                    TextBox txtBxNm = new TextBox();
                    txtBxNm.Width = 48;
                    txtBxNm.ID = "txtBoxNomPt" + rw + cl;
                    txtBxNm.AutoPostBack = true;
                    txtBxNm.Attributes.Add("runat", "server");
                    txtBxNm.TextChanged += new EventHandler(TextBox_TextChanged);
                    this.NomFlds.Rows[rw].Cells[cl].Controls.Add(txtBxNm);
                    {
                        if (!string.IsNullOrEmpty(sessVars[i].Value as string) || sessVars[i].Svexists == true)
                        {
                            txtBxNm.Text = sessVars[i].Value.ToString();
                            if (sessVars[i].Value.Substring(0, 1).ToString() == "*")
                            {
                                this.NomFlds.Rows[rw].Cells[cl + 1].Text = sessVars[i].PosDesc.ToString();
                            }
                            else
                            {
                                TextBox_TextChanged(txtBxNm, EventArgs.Empty);
                                txtBxNm.Text = Session[sessVars[i].Sessnm].ToString();
                            }
                        }
                    }
                }
            }
        }



Вот метод присвоения текстовому полю в сетке значения после того, как пользователь обновил одно из более длинных строковых текстовых полей "макрос" и выделил табуляцию, чтобы начать последовательность синтаксического анализа -

public void parse_str_arr(int[] pos, string[] cts, string inp_str)
  {
      int startpt = 0;
      String thsval = "";
      for (int i = 0; i < pos.Length; i++)
      {
          if (pos[i] != -1)
          {
              string prow = cts[i].Substring(0, 1).ToString();
              int ctrw = Int32.Parse(prow);
              GridViewRow thsrow = this.NomFlds.Rows[ctrw];
              String ctrlnm = "txtBoxNomPt" + cts[i].ToString();
              Control thsfld = this.FindControl(ctrlnm);
             // Control [] thsfldarr = Retr_Set(ctrlnm, ctrlnm.Length);
             // Control thsfld = thsfldarr[1];
              TextBox thsfldtxt = thsfld as TextBox;
              if (inp_str.Length >= (startpt + PoslengthA[i]))
              {
                  thsval = inp_str.Substring(startpt, PoslengthA[i]).ToString();
                  thsfldtxt.Text = thsval;
                  TextBox_TextChanged(thsfld, EventArgs.Empty);
                  Session[SessnameA[pos[i]]] = thsval;
              }
              else
              {
                  thsval = ValueA[pos[i]];
                  thsfldtxt.Text = thsval;
                  TextBox_TextChanged(thsfld, EventArgs.Empty);
                  Session[SessnameA[pos[i]]] = thsval;
              }
              startpt = startpt + PoslengthA[pos[i]];

          }
      }
  }



Вот где происходит моя ошибка. Элемент управления thsfldtxt не найден. При рендеринге я вижу текстовые поля и знаю, что они там есть. Но я их не нахожу. Либо есть скрытые постбэки, которые сдувают имена, либо у меня где-то есть проблема с синтаксисом. Я написал функцию для переназначения имен, но не знаю, где ее встроить. Это действительно заставило меня бежать!

Я использую большое количество массивов для управления этим приложением, потому что это много манипуляций со строками с использованием фиксированных правил. Поэтому, если есть вопросы о массивах и их функциях, не стесняйтесь задавать их.

Что я уже пробовал:

Попытался использовать классный инструмент, который я нашел в сети для получения элементов управления внутри DataGrids под названием FlattenHierarchy, который должен был устранить проблему контейнера данных для поиска вложенного элемента управления, но это тоже не сработало.

2 Ответов

Рейтинг:
18

St0rmi

Пожалуйста, прочтите о жизненных циклах, круговых поездках и состояниях просмотра. Проблема, с которой вы сталкиваетесь, заключается в том, что вы предполагаете, что элемент управления будет постоянным после обратной передачи. Поэтому, когда вы смотрите в свой html-код, вы видите текстовое поле, но после того, как вы сделали некоторую обратную передачу, оно больше не существует, только его состояние просмотра. Таким образом, чтобы получить значение, к которому вы хотите получить доступ, вы должны создать элемент управления с одним и тем же идентификатором для каждой обратной передачи, и .NET назначит ему viewstate.

Рабочий пример

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using System.Data;

partial class _Default : System.Web.UI.Page
{
    private DataSet ds;
    protected void Button1_Click(object sender, System.EventArgs e)
    {
        Label lbl;
        Label lbltext;
        TextBox txt;
        foreach (GridViewRow r in gvIndex.Rows)
        {
            lbl = r.FindControl("lblID");
            lbltext = r.FindControl("lblText");
            txt = r.FindControl(string.Concat("txt", lbl.Text));
            lbltext.Text = txt.Text;
        }
    }

    protected void Page_Load(object sender, System.EventArgs e)
    {
        if (!this.IsPostBack)
        {
            DataTable dt = new DataTable();
            ds = new DataSet();
            dt.Columns.Add("ID", typeof(string));
            dt.Rows.Add("1");
            dt.Rows.Add("2");
            dt.Rows.Add("3");
            dt.Rows.Add("4");
            dt.AcceptChanges();
            ds.Tables.Add(dt);

            this.gvIndex.DataSource = ds.Tables[0];
            this.gvIndex.DataBind();
        }
        CreateTextBoxes();
    }


    public void CreateTextBoxes()
    {
        Label lbl;
        TextBox txt;
        foreach (GridViewRow r in gvIndex.Rows)
        {
            lbl = r.FindControl("lblID");
            txt = new TextBox();
            txt.ID = string.Concat("txt", lbl.Text);
            r.Cells(0).Controls.Add(txt);
        }
    }
}


и органы управления:

<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="gvIndex" runat="server" AutoGenerateColumns="False">
            <Columns>
                <asp:TemplateField>
                    <ItemTemplate>
                        <asp:Label ID="lblID" runat="server" Text='<%# Eval("ID") %>'></asp:Label>
                        <asp:Label ID="lblText" runat="server"></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
        <asp:Button ID="Button1" runat="server" Text="Create" />
    </div>
    </form>
</body>


Элементы управления добавляются динамически на каждой странице page_load, существует несколько способов передачи информации идентификаторов, например, в Viewstate или Session object. Поэтому в вашем случае после того, как вы знаете идентификаторы добавленных элементов управления, сохраните их в объекте viewstate или session (или базе данных), воссоздайте его при следующей обратной передаче, и вы найдете их.


Ray Fischer

Okay, I found the source of the error and it was definitely going in the direction of ST0rmi's post (with a little help from Rick). I have a bunch of session variables which are constantly being regenned based on each of the individual (smaller) textboxes which in turn feed the larger fields to be parsed later on in the code. They were overwriting the field's contents within the statebag multiple times(!). Thus when I went to parse my string, it had reverted itself back to its old (prechanged) form. In terms of the TextBox not being found, this appeared to be a container problem. I was in fact recreating them with each postback and giving them the correct Name. But I was searching at page level and I needed to search at DataRow Level. There was also this very strange phenomenon of them not having the string I was looking for as an ID but rather as a UniqueID. I found some stuff online about this but can't really understand the difference.

St0rmi

The thing is, every DOM element needs a unique ID. So if you are using a datagrid which repeats data, this means if you have a textbox1 in a datarow, this will be repeated for every row. So in order to make it work, there is a container "datarow1" for the 1st row which has a container "datagridxy" and the UniqueID of the TextBox changes to "xxx_datagrid1_datagridxy_textbox1_1" (or similar). But if you are in codebehind, you are able to access that TextBox as "Textbox1" if you already have the container of it (datarow1). ASP.NET is not able to know which textbox1 you want if there are 10 on the page and you invoke Page.Findcontrol. So finding a control with its ID is always on container level.