Можно загрузить веб-часть внутри другой?

Итак, это то, что мы хотим сделать: мы хотим иметь общую веб-часть с настраиваемой рамкой вокруг нее, а затем динамически загружать в нее другие веб-части (без рамки). Неужели это возможно, вы думаете? Немного как Jan Tielens SmartPart, только не для пользовательских элементов управления ASP.Net, но для других веб-частей...;)

Изменить: мы смогли сделать это сейчас. Решение было довольно простым. Проверьте код:

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

Просто, как это... Мы также используем рефлексию для хранения веб-страниц как Xml и т.д., но это не так.

Ответ 1

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", это просто контейнер для конфигурации веб-страниц, всего лишь набор свойств. Есть два вопроса, которые я хотел бы обсудить более подробно:

  • parentWebPartGuid - это Guid (UniqueId?) веб-сайта хостинга. По какой-то причине мы должны установить значение "StorageKeyInternal" для этого значения, используя отражение (это частная собственность). Вы можете уйти, не устанавливая его, но, по крайней мере, для большинства веб-сайтов мы должны были установить его.

  • config.Properties - это значения конфигурации (мы установили их в пользовательский .xml файл, но не стесняйтесь получать это из любого места). Это может выглядеть немного как this.

В наших рамках мы также поддерживаем такие вещи, как динамические значения свойств и т.д., но это на другой день... Надеюсь, это все имеет смысл и может помочь кому-то.

Ответ 2

Я так не думаю. Я попробовал это некоторое время назад, и он жаловался только на возможность добавлять элементы WebPartZone в Page Init. Я думаю, что к тому моменту, когда вы получите инициализацию своего "контейнера" WebPart, слишком поздно добавить дополнительные зоны, поскольку страница холдинга уже была инициализирована.

Ответ 3

Есть (по крайней мере) два способа сделать это: использовать iframe HTML-элемент или просто div, содержимое которого изменяется JavaScript (возможно, с Ajax).

[ПРИМЕЧАНИЕ] Мой ответ является общим (то есть на стороне веб-дизайна), я понятия не имею, как это происходит в вашем техническом контексте, поэтому, возможно, я должен удалить этот ответ...

Ответ 4

Нет шансов получить источник для класса WebPartFactory? Или, может быть, немного больше информации об этом? Возможно, псевдокод? Если пользовательская веб-часть находится в галерее, на нее можно ссылаться так же, как и RSSViewer. Я просто не уверен, как делать то, что вы здесь сделали, и мне очень хотелось бы лучше понять, как это сделать.

Спасибо!

Ответ 5

Когда вы хотите создать экземпляр пользовательской веб-части внутри другой пользовательской веб-части, я использую следующий код в .ascx

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

Значение пространства имен и значение сборки можно скопировать из строки SafeControls из webconfig или из файла пакета (на вкладке manifest):)

Когда я хочу создать экземпляр, dinamteryy (фактически) использует следующий код в .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);