web-dev-qa-db-ja.com

ASP.NETで動的に作成されたコントロールとポストバック

私はこの質問が何千回も尋ねられたことを知っており、以前にそれと苦労しましたが、何らかの理由で、達成したいことを達成できません...クリックすると動的に追加されるLinkBut​​tonが動的に追加されます同じパネルへのコントロール(この例では、テキストボックス)。その目的は、LinkBut​​tonがクリックされた回数と同じ数のコントロールを継続的に追加することです(つまり、1回クリックすると、1つのボックスになり、次に別のクリックで2つのボックスが表示され、別のクリックで3番目に追加されます)。次のコードでは、シリアル化された現在の日付と時刻を使用して、各テキストボックスコントロールに一意のIDを作成しています。

コードを実行するときに[フィルターの追加]をクリックすると新しいテキストボックスが生成されますが、もう一度クリックすると新しいテキストボックスが作成され、その前のテキストボックスが破棄されます。代わりに、以前のテキストボックスとその中に送信されたデータを永続化したいと思います。

よろしくお願いします。

Aspxで:

<asp:Panel ID="pnlFilter" runat="server">

</asp:Panel>

Aspx.cs:

protected void Page_Init(object sender, EventArgs e)
{
        LinkButton lb = new LinkButton();
        lb.ID = "lbAddFilter";
        pnlFilter.Controls.Add(lb);
        lb.Text = "Add Filter";
        lb.Click += new EventHandler(lbAddFilter_Click);
}


void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    pnlFilter.Controls.Add(tb);
}
15
Honus Wagner

このようなことを行おうとしている人のために:しないでください。代わりに、情報の流れを考え、それを行うためのより良い方法があることを理解してください。入力コントロールを動的に作成する必要はありません。それらは静的である可能性があり、1つに記入して送信すると、その情報はどこかに移動する必要があります(データベース、キャッシュ、セッションなど)。そこに到着したら、ポストバックで、選択したストレージ内のすべてのアイテムをループし、それらのディスプレイを作成します。

それは私がやったことであり、それは人生をずっと簡単にしました。それが誰かを助けることを願っています。

16
Honus Wagner
void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    pnlFilter.Controls.Add(tb);
}

それはうまくいきません-あなたがあなたの質問で推論したように、それはライフサイクルの問題です。これを使用する方法はたくさんありますが、Honus Wagnerの提案が最も柔軟です。

ここで起こっていることは、イベントハンドラーでControls.Addを呼び出しており、コントロールがコントロールリストに追加され、次のリクエスト(部分的なポストバック、完全なポストバック、または新しいページヒット)で、そのコントロールあなたはどこにもそれを永続化していないのでなくなっています。

現在、ページイベントのライフサイクルで何が起こっているかを示します。

  1. ページはOnInitを呼び出し、LinkBut​​tonを追加します
  2. ユーザーがLinkBut​​tonをクリックする
  3. ページはOnInitを呼び出してポストバックリクエストを開始します。 LinkBut​​tonが再作成されます
  4. イベントハンドラーが呼び出され、TextBoxが動的に作成され、コントロールコレクションに追加されます。
  5. ユーザーがもう一度LinkBut​​tonをクリックする
  6. ページはOnInitを呼び出して、LinkBut​​tonを再度追加します。 pnlFilter.Controlsコレクションは、マークアップで静的に宣言した要素を除いて、この時点では空です。
  7. イベントハンドラーが呼び出され、新しいTextBoxがコントロールリストに追加されます。

これがベストプラクティスであるかどうかはわかりませんが、これに似たモデルで成功しました(このコードはテストしていないため、調整が必要になる場合があります)。

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    LinkButton lb = new LinkButton();
    lb.ID = "lbAddFilter";
    pnlFilter.Controls.Add(lb);
    lb.Text = "Add Filter";
    lb.Click += new EventHandler(lbAddFilter_Click);

    // regenerate dynamically created controls
    foreach ( var control in PersistedControls )
    {
        pnlFilter.Controls.Add(GenerateControl(control));
    }
}

private IList<Control> _persistedControls;
private const string PersistedControlsSessionKey = "thispage_persistedcontrols";
private IList<Control> PersistedControls
{
    if (_persistedControls == null)
    {
        if (Session[PersistedControlsSessionKey] == null)
            Session[PersistedControlsSessionKey] = new List<Control>();
        _persistedControls = Session[PersistedControlsSessionKey] as IList<Control>;
    }
    return _persistedControls;
}    

void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    PersistedControls.Add(tb);
    pnlFilter.Controls.Add(tb);
}

実際のコントロールオブジェクトをセッションストアに追加するかどうかはわかりません。コントロールIDに問題があり、ポストバックでエラーが発生すると思われます(コントロールがシリアル化できない可能性がありますか?)。私が開発したソリューションでは、GenerateControlメソッドに相当するもので新しいTextBox/Label/ComboBox/etcを生成し、生成する必要のあるUIコントロールのさまざまなプロパティを含むPersistedControlsコレクションにカスタムコンテナークラスを格納しました各ページでそれを初期化します。

11
Stefan Mohr

すべてのポストバックで各コントロールを再作成する必要があると思います。

Repeaterコントロールには、データバインド時に子を再作成するためにその子に関する十分な情報が格納されていることを知っています...少しの作業を節約するためにそれを使用できる場合があります。

1
AaronSieb

追加してみてください

if(!Page.IsPostback)
  --- your code that adds different controls.
0
Rahul Soni