.NET 2.0/3.0より前は、FindControl
を以前に使用しました。なんらかの理由で、私のコントロールのIDに割り当てられたファンキーな名前が付けられたようです。たとえば、ID「cbSelect」をチェックボックスに割り当てましたが、FindControlはそれを見つけません。 HTMLを表示すると、割り当てられたctl00_bodyPlaceHolder_ctl02_cbSelect
。
それについて言及しているFindControlの例は1つも見つかりませんでした。実際、誰もが通常のように検索コントロールを使用しているようです。
それで、私は何か間違ったことをしていますか? .Netは変更されましたか?誰かが私にこれに光を当てることができますか?それは本当にイライラします!
おそらくMasterPageまたはユーザーコントロール(ascx)を使用しているため、クライアントIDが変更されるのはこのためです。マスターページに、ページ内のコントロールと同じIDを持つコントロールがあるとします。これにより、衝突が発生します。 IDの変更により、すべてのClientIDプロパティがページ上で一意になるようになります。
FindControlは、MasterPagesを操作するときに特別な注意が必要です。 ASP.NET 2.0 MasterPagesおよびFindControl() をご覧ください。 FindControlは 名前付けコンテナー 内で機能します。 MastePageとページは異なる名前付けコンテナーです。
エクステンダーを記述して、再帰を使用してページ上の任意のコントロールを見つけることができます。これは、Util/Helperクラスにある可能性があります。
public static Control FindAnyControl(this Page page, string controlId)
{
return FindControlRecursive(controlId, page.Form);
}
public static Control FindAnyControl(this UserControl control, string controlId)
{
return FindControlRecursive(controlId, control);
}
public static Control FindControlRecursive(string controlId, Control parent)
{
foreach (Control control in parent.Controls)
{
Control result = FindControlRecursive(controlId, control);
if (result != null)
{
return result;
}
}
return parent.FindControl(controlId);
}
私は単純な拡張メソッドで「ほとんどの」ケースでこの問題を回避するためにかなりの幸運を持っています
コントロール階層全体をスキャンする場合は、ページ自体を含め、考えられる上位レベルのコンテナーコントロールで呼び出すことができます。
private static Control FindControlIterative(this Control control, string id)
{
Control ctl = control;
LinkedList<Control> controls = new LinkedList<Control>();
while(ctl != null)
{
if(ctl.ID == id)
{
return ctl;
}
foreach(Control child in ctl.Controls)
{
if(child.ID == id)
{
return child;
}
if(child.HasControls())
{
controls.AddLast(child);
}
}
ctl = controls.First.Value;
controls.Remove(ctl);
}
return null;
}
コントロールコレクションでコントロールを検索するときは、ソースポストレンダリングに表示されるIDではなく、常にコントロールに割り当てたIDを使用してください。 FindControl()が存在することがわかっているコントロールを見つけられない場合は、コントロール階層の右側のブランチを検索していない可能性があります。再帰関数は私にとって成功しています。
VB.NET 3.5で使用する例を次に示します。
Function FindControlRecursive(ByVal ctrl As Control, ByVal id As String) As Control
Dim c As Control = Nothing
If ctrl.ID = id Then
c = ctrl
Else
For Each childCtrl In ctrl.Controls
Dim resCtrl As Control = FindControlRecursive(childCtrl, id)
If resCtrl IsNot Nothing Then c = resCtrl
Next
End If
Return c
End Function
これは、ベースページクラスでこの関数を局所的に実装する方法の例です。
Dim form HtmlForm = CType(FindControlRecursive(Me, "Form"), HtmlForm)
これは私のために働いたVB.NETコードです:
<Extension()> _
Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control
If controlToStartWith Is Nothing Then Return Nothing
If controlToStartWith.ID = controlIdToFind Then Return controlToStartWith
For Each childControl As Control In controlToStartWith.Controls
Dim resCtrl As Control = FindChildControlById(childControl, controlIdToFind)
If resCtrl IsNot Nothing Then Return resCtrl
Next childControl
Return Nothing
End Function ' Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control
クレジットはジョージの最初のVB.NETコードに使用されます。私はそれをほんの少し変更しましたが、2つの機能的な変更があります。入力コントロールとしてnull/Nothingが渡された場合、またはエラーが発生した場合、エラーは発生せず、拡張機能として実装されています。私の他の3つのマイナーな変更は機能に影響しませんが、私にとっては、コードの簡素化でした。しかし、私はそれが非常に主観的であることを知っています。
したがって、このメソッドは以下で使用できます。
Dim c1 As Control = Page.FindChildControlById("aspControlID")
そして、次のように、それをコントロールの特定の子クラスに変換したい場合:
Dim c1 As Control = Page.FindChildControlById("aspControlID")
Dim c As HyperLink = TryCast(c1, HyperLink)
更新:関数の名前は「FindChildControlById」になりました(以前は「FindMiControl」でした)。 SpeedNetの提案の方が気に入りました。
Htmlをレンダリングするとき、ASP.NETはすべてのコントロールIDの前に、ドキュメントルートまでさかのぼる階層内の名前付けコンテナー(ユーザーコントロールなど)のIDを付けます。これにより、すべてのIDがポストバックなどで一意になります。
これは、元のマークアップでIDを使用する必要があるFindControlの使用には影響しません。
ここに、Webフォームコントロールの命名方法に関するリファレンスがあります...