SharePoint:CompositeField在预渲染事件期间抛出NullReferenceException

时间:2013-08-05 21:38:47

标签: c# asp.net sharepoint-2010

我在自定义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文档和我在网上冲浪时找到的任何文档相当稀疏...

我应该如何使用此控件?或者我应该使用基本的asp.net控件分解并手动处理每个字段?有更好的选择吗?在几十个字段中,有一些需要自定义工作 - 如果没有,可以使用SharePoint的默认列表项表单处理其余字段。

在我的* .aspx页面的PlaceHolderMain内容元素下,我正在使用这样的控件:

<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;
    }
}

2 个答案:

答案 0 :(得分:0)

最初,我放弃了使用SharePoint:CompositeField和类似控件的整个想法。我回去使用基本的ASP.Net控件。除了处理附件外,这个工作正常。但我无法弄清楚如何处理附件,但在我对解决方案的研究中,我注意到我可以恢复使用默认的SharePoint列表表单。这就是我尝试过的,但后来我遇到了查找字段的问题。我无法让他们使用声明性XML,必须在功能激活期间创建字段。这又导致了场序列的问题。查找字段全部出现在表单的末尾,而不是它们在其余字段中的适当位置。我尝试使用FieldLinksCollection.Reorder(string[])方法解决此问题,但无法使用新字段序列更新列表。

最后,我使用声明性XML的组合来定义我的站点列,内容类型,列表定义和列表实例。然后我使用默认列表表单。虽然查找字段存在于XML中,但它们没有正确绑定,我发现一些代码提示了一种以编程方式修复损坏的绑定的方法:https://stackoverflow.com/a/18192756/1075980。这种行动组合解决了我的问题。

答案 1 :(得分:0)

我不确定这是否适用于您的情况,但它可能有助于其他googlers排查此错误。我得到了同样的错误,发现原因是我不小心将<SharePoint:CompositeField runat="server" />偷偷带进我的ListFieldIterator下面的表单模板中。我相信复合字段属于字段控件,或者必须定义一个属性来告诉它要渲染哪个字段。