8

ListBox を拡張するカスタム コントロールを開発しました。コントロールは、たとえば AJAX 要求の結果として、クライアント側で発生した要素への変更を「記憶」するという考え方です。

コントロールは非表示の入力もレンダリングし、AJAX 要求の結果は非表示の入力に格納されます。これはポストバックされ、コントロールの LoadPostData() メソッドは非表示の入力を探し、非表示の入力にデータがある場合は、そこから ListItem コレクションを作成します。

これは、ユーザーがリスト ボックスから選択を行っている限り、完全に機能します。そうでない場合、LoadPostData() メソッドは呼び出されないため、新しい ListItem コレクションは作成されません。(デバッガーを使用してこれを確立しました。)

LoadPostData メソッドは、POST データ コレクションにコントロールの UniqueID (つまり、HTML の「name」属性) に対応するデータが含まれている場合にのみ呼び出されると想定しています。ユーザーがリスト ボックスから選択を行っていない場合、リスト ボックスの UniqueID の投稿データには何も含まれず、LoadPostData() は呼び出されません。あれは正しいですか?

ユーザーが選択したかどうかに関係なく、カスタム ListBox の LoadPostData() メソッドがポストバックごとに呼び出されるようにする方法を誰か提案できますか?

前もって感謝します-私は本当にこれにこだわっています。

デビッド

4

3 に答える 3

8

これに飛び込むのが少し遅れましたが、将来の参考のために、私が同様のことをどのように達成したかを以下に示します…</p>

私のコントロールは、ノードのテンプレートを使用するツリーです。これに対処していた問題は、ノードの展開/折りたたみ状態に対するクライアント側の変更をキャプチャする方法でした。最終的に機能したのは次のとおりです。

CreateChildControls で、隠しフィールドをルート コントロールのコントロール コレクションに追加します。

protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
    ...
    _cdExpanded = new HiddenField();
    _cdExpanded.ID = "cdExpanded";
    this.Controls.Add(_cdExpanded);
    ...
}

OnInit 呼び出しで

protected override void OnInit(EventArgs e)
{
    ...
    Page.RegisterRequiresPostBack(this);
    ...
}

LoadPostData で、非表示フィールドの UniqueID (ClientID ではない) と一致する投稿コレクションの値を探します。

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
    ...
    string cdExpanded = postCollection[_cdExpanded.UniqueID];
    ...
}

個々のノードのクラス内には、トグル ボタンの onclick イベントに、ベース コントロールの ID と個々のノードを引数として受け取る JavaScript 関数の呼び出しを設定するコードがあります。

    string ToggleScript
    {
        get
        {
            return "ToggleNode('" + this.ClientID + "', '" + _TreeRoot.ClientID + "');";
        }
    }
    protected override void Render(HtmlTextWriter writer)
    {
        ...
        if (this.HasChildren)
        {
            writer.AddAttribute("onclick", ToggleScript);
        }
        ...
    }

これにより、getElementById を使用して隠しフィールドを簡単に見つけることができます。

function ToggleNode(nodeID, treeID) {
var cdExpanded = document.getElementById(treeID + "_cdExpanded");
...
}

次に、発生したイベントの必要に応じて、JavaScript が非表示フィールドの値を変更します。サーバーに戻ると、このフィールドの内容を解析して、再度レンダリングする前に必要に応じてコントロールの状態を変更できます。(注: 実際には、さまざまなイベントを追跡するために 3 つの隠しフィールドを使用していますが、概念は同じです)

これが将来他の人に役立つことを願っています…</p>

于 2011-02-25T13:44:32.553 に答える
3

アイテムが選択されている場合にのみ機能するのは本当に奇妙に聞こえます。LoadPostData が呼び出されているかどうかを確認する簡単な方法は、トレースを有効にして、IPostBackDataHandler.LoadPostData(...) に次を配置することです。

Page.Trace.Write("My control", "LoadPostData");

その場合は、次のものが揃っていることを確認する必要があります。

Page.RegisterRequiresPostBack(this) in OnInit

これが完全なサンプル コントロールです。

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel.Design;
using System.ComponentModel;
using System.Web.UI.Design;

namespace Controls
{
    public sealed class ExtendedListBoxDesigner : ControlDesigner
    {

        public override string GetDesignTimeHtml()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<div>My designer</div>");
            return sb.ToString();
        }

    }

    [DesignerAttribute(typeof(ExtendedListBoxDesigner), typeof(IDesigner))]
    public class ExtendedListBox : ListBox, INamingContainer, IPostBackDataHandler 
    {
        bool IPostBackDataHandler.LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
        {
            Page.Trace.Write("ExtendedListBox", "LoadPostData");
            return true;
        }


        protected override void OnInit(EventArgs e)
        {
            Page.RegisterRequiresPostBack(this);
            base.OnInit(e);
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            base.RenderContents(writer);
            writer.Write(string.Format("<input type=\"hidden\" name=\"{0}_dummy\" value=\"alwaysPostBack\">", this.ID));
        }

    }
}

ページは次のようになります。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ControlSamlpe._Default" Trace="true" %>
<%@ Register Assembly="Controls" Namespace="Controls" TagPrefix="sample" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <sample:ExtendedListBox runat="server" ID="extListBox"></sample:ExtendedListBox>
    <asp:Button runat="server" ID="go" />
    </div>
    </form>
</body>
</html>

[go] をクリックすると、トレースに「LoadPostData」が表示されます。

于 2010-07-09T09:45:16.333 に答える
1

投稿データにコントロールの UniqueID と同じ名前のアイテムが含まれていない限り、LoadPostData() メソッドは呼び出されないことを確認しました。[編集: Init() 中に Page.RegisterRequiresPostback を呼び出すと、これを克服できます。] 理由はわかりますが、かなり制限されています。

LoadPostData() メソッド中にまったく処理しないことで、問題を解決しました。代わりに、代わりに OnLoad() で呼び出すメソッドで処理しました。

このアプローチを使用する場合は、次の 2 つの点に注意する必要があります。

1) LoadPostData() メソッドに引数として渡される postCollection NameValueCollection オブジェクトにアクセスできなくなりました。これは、Request.Form コレクションから投稿データを抽出する必要があることを意味しますが、これは少し難しい作業です。2) OnLoad() は ViewState 処理コードの後に​​発生するため、ListItems の作成後に SelectedValue を手動で設定する必要があります。そうしないと、リストボックスが AJAX を介して入力され、ユーザーが選択を行うと、選択が失われます。

これが将来誰かに役立つことを願っています。

于 2010-07-09T09:55:28.903 に答える