可以在另一个 Web 部件中加载 Web 部件吗?

12

我们想要的是:拥有一个通用的Web Part,其周围有自定义的框架,并在其中动态加载其他无框架的Web Part。你觉得这是否可能?有点像Jan Tielens SmartPart,但不是针对ASP.Net用户控件,而是针对其他Web Part... ;)

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

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

就是这么简单...我们还使用反射将Web部件存储为Xml等,但那不是重点。


这对于简单的 Web 部件可能管用,但对于利用消费者/生产者等 Web 部件特定功能的部件是否也适用呢? - Steven Robbins
你在谈论连接,对吗?我想是的,这只是设置正确的属性值的问题。但我还没有尝试过这个... - noocyte
你有没有尝试过以这种方式加载内容编辑器 Web 部件?当我实例化一个部件时,即使将存储键 Internal 设置为 Web 部件,它似乎也无法使用... - Henry C
不好意思,我从未尝试过那个。可能有一些特殊的要求。我建议您使用Reflector来查看在SharePoint实例化期间是否发生了任何特殊情况。 - noocyte
5个回答

4

我不这么认为。我之前试过,它抱怨只能在页面初始化时添加WebPartZone项。我认为当它开始初始化你的“容器”WebPart时,添加更多区域已经太晚了,因为包含页面已经被初始化。


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

4
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"类,它只是一个包含WebPart配置的容器,只是一堆属性。有两个问题我想更详细地讨论:
  1. parentWebPartGuid - 这是托管WebPart的Guid(唯一标识符?)。由于某种原因,我们必须使用反射将"StorageKeyInternal"设置为此值(它是一个私有属性)。你可能可以不设置它,但至少对于大多数WebPart,我们必须设置它。

  2. config.Properties - 这是配置值(我们在自定义的.xml文件中设置它们,但可以从任何地方获取)。它可能看起来像this..

在我们的框架中,我们还支持动态属性值等等,但那是另一天的事情...希望这一切都讲得通,并能帮助到某些人。

感谢noocyte提供的出色解决方案。我面临的问题是,WP似乎无法编辑。当我打开工具窗格以设置Feed URL时,我看到这个错误:“您尝试更改的Web Part无效或已被另一个用户删除。刷新页面。”你有什么想法为什么会发生这种情况吗? - Gonzalo
不好意思,我不知道。从我的回答可以看出,我写这个已经将近6年了,所以这种方法很可能已经不再适用。而且自那时以来,我已经换了工作(两次!),所以我无法访问我们当时编写的代码...抱歉我不能提供更多帮助。 - noocyte

2
你们没有获取WebPartFactory类源代码的机会吗?或者能否提供更多关于它的信息?有伪代码吗?如果自定义Web部件在库中,则可以通过与RSSViewer相同的方式引用它,对吗?我不是很确定如何完成你在这里所做的操作,我非常想更好地了解如何做到这一点。
谢谢!

我们通过反射来实现这个。因此,您需要类型和程序集,然后应该能够很好地加载它。但是有一些注意事项...我会尽快更新我的答案并提供一些代码。 - noocyte

2

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

[注意]我的答案是通用的(即在Web设计方面),我不知道它在您的技术上下文中如何,所以也许我应该删除这个答案...


0

当我想在另一个自定义 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"  %>

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

当我想要动态实例化它时,实际上是在 .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);

这段代码对我来说总是有效的,我喜欢这种方式,因为实例化WebPart的唯一要求是正确的命名空间,在某些情况下,我将其存储在SharePoint列表中,它变得真正动态。 - Ewerton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接