カスタム SharePoint リスト フォームで次のエラーが表示されます: 例外の詳細: System.NullReferenceException: オブジェクト参照がオブジェクトのインスタンスに設定されていません。
Stack Trace:
[NullReferenceException: Object reference not set to an instance of an object.]
Microsoft.SharePoint.WebControls.CompositeField.get_Visible() +41
System.Web.UI.Control.PreRenderRecursiveInternal() +22
System.Web.UI.Control.PreRenderRecursiveInternal() +223
System.Web.UI.Control.PreRenderRecursiveInternal() +223
System.Web.UI.Control.PreRenderRecursiveInternal() +223
System.Web.UI.Control.PreRenderRecursiveInternal() +223
System.Web.UI.Control.PreRenderRecursiveInternal() +223
System.Web.UI.Control.PreRenderRecursiveInternal() +223
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3393
<SharePoint:CompositeField>
フォームで使用を開始した後、エラーが発生し始めました。私は間違っているかもしれませんが、このコントロールを使用しようとしているのは、さまざまなフィールドのさまざまなフィールド タイプに自動的に適応し、ページ モード (新規、編集、または表示) に適応すると考えたからです。私はそれを間違って使用していると思われますが、MSDN ドキュメントと、Web サーフィンから見つけたドキュメントはかなりまばらです...
このコントロールはどのように使用すればよいですか? それとも、基本的なasp.netコントロールを使用して、個々のフィールドを分解して手動で処理する必要がありますか? より良いオプションはありますか?数十個のフィールドのうち、カスタム作業が必要なものはいくつかあります。カスタム作業が必要ない場合でも、残りのフィールドは SharePoint の既定のリスト アイテム フォームで適切に処理されます。
*.aspx ページのPlaceHolderMain
content 要素の下で、次のようなコントロールを使用しています。
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<!-- more content -->
<div id="main-form">
<!-- more content -->
<div>
<asp:Label runat="server" ID="LTSAttachmentsLabel" AssociatedControlID="LTSAttachmentsCompositeField" Text="Attach File" CssClass="label"></asp:Label>
<SharePoint:CompositeField runat="server" ID="LTSAttachmentsCompositeField" FieldName="LTS Attach File" />
</div>
<!--
about two dozen <div> tags; much of it similar to the above
with Label and CompositeField controls
-->
</div>
<!-- more content -->
</asp:Content>
私は厳密に宣言的な使用から始めましたが、一連のエラーとそれらを修正しようとした後、ページの PreInit および Load イベントで次のことを実行しています。
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
_currentWeb = SPContext.Current.Web; // page-scoped property
string listGuid = Request.QueryString["List"];
_formList = _currentWeb.Lists[new Guid(listGuid)]; // page-scoped property
string itemGuid = Request.QueryString["Item"];
if (!itemGuid.IsNullOrEmptyTrimmed())
{
_itemID = itemGuid.ToIntegerNullSafe(); // page-scoped property
_item = _formList.GetItemById(_itemID.Value); // page-scoped property
}
_pageMode = (SPControlMode)Enum.Parse(typeof(SPControlMode), Request.QueryString["ControlMode"]); // page-scoped property
if (SPContext.Current.FormContext.FormMode == SPControlMode.Invalid && _pageMode != SPControlMode.Invalid)
{
SPContext.Current.FormContext.FormMode = _pageMode;
}
if (Request.QueryString["IsDlg"] != null)
{
_formIsDialog = Request.QueryString["IsDlg"] == "1"; // page-scoped property
}
if (Request.QueryString["ID"] != null)
{
_itemID = int.Parse(Request.QueryString["ID"]); // page-scoped property, unnecessary redundancy?
}
}
protected void Page_Load(object sender, EventArgs e)
{
// unrelated code
var spControls = from c in this.GetChildControlsRecursive()
where c is CompositeField
select c;
foreach (CompositeField cf in spControls)
{
cf.ListId = _formList.ID;
cf.ItemId = _itemID ?? -1;
}
// unrelated code
}
興味深いことに、GetChildControlsRecursive
すべての子コントロールを階層コレクションではなくフラットな列挙可能なコレクションとして返します。
// extension class in separate file
public static class ControlExtensions
{
public static IEnumerable<Control> GetChildControlsRecursive(this Control parentControl)
{
Stack<Control> todo = new Stack<Control>();
HashSet<Control> results = new HashSet<Control>();
todo.Push(parentControl);
results.Add(parentControl);
while (todo.Count > 0)
{
Control parent = todo.Pop();
foreach (Control child in parent.Controls)
if (results.Add(child))
todo.Push(child);
}
return results;
}
}