问题 可以在另一个内部加载Web部件吗?


所以,这就是我们想要做的事情:我们想要一个带有自定义框架的通用Web部件,然后在其中动态加载其他Web部件(无框架)。你觉得这有可能吗?有一点像 Jan Tielens SmartPart,仅适用于ASP.Net用户控件,但适用于其他Web部件...;)

编辑:我们现在已经能够做到这一点。解决方案实际上非常简单。看看代码:

public class WebPartWrapper : System.Web.UI.WebControls.WebParts.WebPart {
    protected override void CreateChildControls() {    
        Panel pnl = new Panel();
        this.Controls.Add(pnl);
        WebPart dynamicPart = WebPartFactory.CreateWebPart("RSSViewer");
        pnl.Controls.Add(dynamicPart);
    }
}

这样简单...我们也使用反射将webparts存储为Xml等,但这不是重点。


6703
2018-01-07 08:52


起源

这适用于简单的webparts,但它是否适用于利用webpart特定事物(如消费者/生产者等)的东西? - Steven Robbins
你在谈论连接,对吗?我想是的,这只是设置正确的属性值的问题。但我没试过这个...... - noocyte
您是否尝试过以这种方式加载内容编辑器Web部件?当我实例化一个,即使在为Web部件设置存储密钥内部之后,它似乎也不可用...... - Henry C
不,抱歉从未尝试过。它可能有一些特殊要求。我建议您使用Reflector来查看在SharePoint实例化期间是否发生了特殊情况。 - noocyte


答案:


我不这么认为。我曾经尝试了一段时间,它抱怨只能在Page Init中添加WebPartZone项目。我认为当初始化你的“容器”WebPart时,由于保留页面已经初始化,所以添加更多区域为时已晚。


4
2018-01-07 09:02



我现在已经发现了如何做到这一点,并将更新我的帖子来展示这一点。 :) - noocyte


public class WebPartWrapper : System.Web.UI.WebControls.WebParts.WebPart {
    protected override void CreateChildControls() {    
        Panel pnl = new Panel();
        this.Controls.Add(pnl);
        var factory = new WebPartFactory()
        WebPart dynamicPart = factory.CreateWebPart("RSSViewer", this.Guid);
        pnl.Controls.Add(dynamicPart);
    }
}

public class WebPartFactory {
    public WebPart CreateWebpart(string webpartName, Guid parentWebPartGuid)
    {
        var config = ConfigurationFactory.LoadConfiguration(webpartName);

        Assembly webPartAssembly = Assembly.Load(config.Assembly);
        Type webPartType = webPartAssembly.GetType(config.Class);
        object actualWebPart = Activator.CreateInstance(webPartType);

        foreach (var item in config.Properties)
        {
            PropertyInfo webPartProperty = webPartType.GetProperty(item.Name);
            object webPartPropertyValue = Convert.ChangeType(itemValue, Type.GetType(item.Type));
            if (!String.IsNullOrEmpty(item.Value))
                webPartProperty.SetValue(actualWebPart, webPartPropertyValue, null);
        }

        RunMethod("set_StorageKeyInternal", actualWebPart, new object[] { parentWebPartGuid });
        return actualWebPart as WebPart;
    }

    private void RunMethod(string methodName, object objectInstance, object[] methodParameters)
    {
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Public |
            BindingFlags.NonPublic;

        Type t = objectInstance.GetType();
        MethodInfo m = GetMethod(t, methodName, flags);
        if (m != null)
        {
            m.Invoke(objectInstance, methodParameters);
        }
    }

    private MethodInfo GetMethod(Type instanceType, string methodName, BindingFlags flags)
    {
        MethodInfo m = instanceType.GetMethod(methodName, flags);
        if (m != null)
        {
            return m;
        }

        if (instanceType.GetType() == typeof(object) || instanceType.BaseType == null)
        {
            return null;
        }

        return GetMethod(instanceType.BaseType, methodName, flags);
    } 
}

这段代码需要一些解释...请原谅我如果不编译,我不得不删除一些原始代码,这是非常具体的实现。我也没有显示“config”类,它只是一个用于配置webparts的容器,只是一堆属性。有两个问题我想更详细地讨论:

  1. parentWebPartGuid - 这是托管webpart的Guid(UniqueId?)。出于某种原因,我们必须使用反射(它是私有属性)将“StorageKeyInternal”设置为此值。你可能没有设置它,但至少对于我们必须设置它的大多数webparts。

  2. config.Properties - 这是配置值(我们在自定义的.xml文件中设置它们,但随时随地都可以获得)。它看起来有点像 这个..

在我们的框架中,我们也支持诸如动态属性值之类的东西,但那是另一天...希望这一切都有意义并且可以帮助某人。


4
2018-02-12 14:14



谢谢你这个伟大的解决方案noocyte。我面临的问题是WP似乎不可编辑。当我打开工具窗格以设置订阅源URL时,我看到此错误:“您尝试更改的Web部件无效或已被其他用户删除。刷新页面。”有关为什么会发生这种情况的任何想法? - Gonzalo
不,对不起,我不知道。你可以从我的回答中看出,我写这篇文章差不多6年了,所以这种方法很可能不再适用。我也换过工作(两次!),因此我无法访问我们当时写的代码...抱歉,我无法提供更多帮助。 - noocyte


有(至少)两种方法可以做到这一点:使用iframe HTML元素,或者只是一个内容由JavaScript更改的div(可能使用Ajax)。

[注意]我的答案是通用的(即在网页设计方面),我不知道你的技术背景如何,所以也许我应该删除这个答案......


2
2018-01-07 09:02





没有机会获得WebPartFactory类的源代码吗?或者可能有更多关于它的信息?可能是伪代码?如果自定义Web部件位于库中,则可以使用与RSSViewer正确相同的方式引用它?我只是不确定如何去做你在这里所做的事情,我非常想更好地理解如何做到这一点。

谢谢!


2
2017-11-24 19:39



我们通过反思来做到这一点所以你需要Type和Assembly,那么你应该能够正确加载它。但是有一些警告......我会用一些代码尽快更新我的答案。 - noocyte
非常感激! - Tyler


当想要在另一个自定义webpart中实例化自定义webpart时,我在.ascx中使用以下代码

<%@ Register tagPrefix="uc1" Namespace="Megawork.Votorantim.Intranet.Webparts_Intranet.LikeButton" Assembly="Megawork.Votorantim.Intranet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=769156d154035602"  %>

可以从webconfig或包文件(在清单选项卡中)从SafeControls行复制Namespace值和Assembly值:)

当我想实例化它dinammicaly(事实上)是在.cs中使用以下代码

//This is the namespace of the control that will be instantiated dinamically    
string type = "My.Custom.Namespace.WebpartToBeAdded.WebpartToBeAdded";

// Instantiate the control dinamically based on his type
System.Web.UI.WebControls.WebParts.WebPart genericWP = (System.Web.UI.WebControls.WebParts.WebPart)Activator.CreateInstance(Type.GetType(type));

// sets the page to the genericWP (i dont know if this is required)
genericWP.Page = this.Page;

// Note: if you want to call custom methods of the dinamically instantiated controls (like a custom load method) you will need to create an interface and make your dinamically instantiated webpart implement it. You will need to do it in that file that have the following code: private const string _ascxPath @"~/_CONTROLTEMPLATES/...". Then you can do the following
//IMyInterface ig = (IMyInterface)genericWP;
//ig.MyCustomLoadMethod(someParam);

// Adds the controls to a container, an asp panel by example.
panelDinamicControls.Controls.Add(genericWP);

0
2018-04-15 12:56



这段代码总是对我有用,我喜欢这种方式,因为实例化webpart的唯一要求是正确的命名空间,在某些情况下我将它存储在sharepoint列表中,它变得真正的恐怖。 - Ewerton