using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using WebPart = System.Web.UI.WebControls.WebParts.WebPart;
namespace XlstListModifier.XsltListViewModifier
{
/// <summary>
/// An error exists when an XsltListViewWebPart is retrieving its data from a List on a different SPWeb that that of the
/// SPContext.Current.Web. It occurs when the Microsoft.SharePoint.WebControls.NewMenu is rendered (The NewMenu is rendered
/// due to the default rendering of the embedded ToolbarControl in the XsltListViewWebPart. During the rendering of the NewMenu
/// the list of SPContentType's that are used by the SPList being rendered is retrieved:
///
/// [Microsoft.SharePoint.WebControls.NewMenu.AddMenuItems()]
/// IList<![CDATA[<SPContentType>]]> list;
/// if (base.RenderContext.RootFolderUrl != null)
/// {
/// list = new List<![CDATA[<SPContentType>]]>(this.Web.GetFolder(base.RenderContext.RootFolderUrl).ContentTypeOrder);
/// }
/// else
/// {
/// list = new List<![CDATA[<SPContentType>]]>(this.List.RootFolder.ContentTypeOrder);
/// }
///
/// The base.RenderContext is not null and therefore the first statement gets executed. this.Web is referencing the
/// RenderContext.Web object (which by default uses the SPContext.Current.SPWeb (unless otherwise specified when creating)
/// and therefore fails when the RootFolder object is for an SPList in a different SPWeb.
///
/// The code below sets the RenderContext of the ViewToolBar to the correct SPWeb, as well as setting the SPList and SPView
/// GUID's. The property exposing the toolbar object is unfortunately an internal property though, hence the use of
/// reflection to access the property and set it correctly.
/// </summary>
[ToolboxItemAttribute(false)]
public class XsltListViewModifier : WebPart
{
private bool _bFixed;
private readonly Dictionary<Guid, SPWeb> _oWebs = new Dictionary<Guid, SPWeb>();
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
Title = "XSLT List View Modifier WebPart";
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!_bFixed && Page.IsPostBack)
{
ApplyFix();
_bFixed = true;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
ApplyFix();
_bFixed = true;
}
private void ApplyFix()
{
IEnumerable<XsltListViewWebPart> oParts = GetXsltParts();
foreach (XsltListViewWebPart oPart in oParts.Where(oPart => oPart.WebId != SPContext.Current.Web.ID))
SetToolbarContext(oPart);
}
/// <summary>
/// Get an array of XsltListViewWebPart's that exist on the current WebPartPage
/// </summary>
/// <returns>An array of XstlListViewWebPart's</returns>
private IEnumerable<XsltListViewWebPart> GetXsltParts()
{
return WebPartManager.WebParts.OfType<XsltListViewWebPart>().ToArray();
}
private void SetToolbarContext(XsltListViewWebPart part)
{
try
{
//Get hold of the toolbar that renders the new menu items
ViewToolBar oTbar = GetPrivatePropertyValue<ViewToolBar>(part, "ToolbarControl");
if (oTbar == null) return;
//We keep a collection of SPWeb objects in case there is more than on XsltListViewWebPart on
//the page looking at lists in more than one site (SPWeb). This way we don't instanciate more
//than one SPWeb for each SPWeb needed
SPWeb oWb;
if (_oWebs.ContainsKey(part.WebId))
{
oWb = _oWebs[part.WebId];
}
else
{
oWb = SPContext.Current.Site.OpenWeb(part.WebId);
_oWebs.Add(part.WebId, oWb);
}
//Create the SPContext object that references the correct SPList, SPView and most importantly..SPWeb
SPContext oCtx = SPContext.GetContext(HttpContext.Current, new Guid(part.ViewGuid), part.ListId, oWb);
//Set the RenderContext of the ToolbarControl
oTbar.RenderContext = oCtx;
}
catch (Exception)
{ }
}
public override void Dispose()
{
base.Dispose();
//Ensure that any webs are disposed of incase the GC doesn't pick it up during object disposal
foreach (Guid oId in _oWebs.Keys) _oWebs[oId].Dispose();
}
public static T GetPrivatePropertyValue<T>(object o, string name)
{
if (o == null) throw new ArgumentNullException("o");
PropertyInfo oInfo = o.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (oInfo == null) throw new ArgumentOutOfRangeException("name", string.Format("Property {0} was not found in Type {1}", name, o.GetType().FullName));
return (T)oInfo.GetValue(o, null);
}
}
}
参考地址:SharePoint 2010 cross site XsltListViewWebPart error