Создать страницу Aspx экземпляра элемента управления Ascx во внутреннем классе без загрузки FilePath - PullRequest
0 голосов
/ 25 августа 2009

Вопрос: Возможно ли во внутреннем коде (не в коде позади, а в фактическом внутреннем классе) загрузить и отобразить страницу или элемент управления, определенный в .aspx или .ascx, без необходимости использовать Load (путь) и вместо этого просто создать экземпляр класса page / control?

Я хочу быть в состоянии сделать это (из внутреннего класса, а не кода позади):

MyControl myCtl = new MyApp.Views.Shared.MyControl();
String html = Util.ControlToString(myCtl); //I get an empty string & hidden errors

вместо этого

public static string ControlToString(string path)
{
    Page pageHolder = new Page();
    MyControl myCtl = (MyControl)pageHolder.LoadControl(path);
    pageHolder.Controls.Add(myCtl);
    StringWriter output = new StringWriter();
    HttpContext.Current.Server.Execute(pageHolder, output, false);
    return output.ToString();
}

подробности: В веб-приложении Asp.net мне иногда нужно отобразить пользовательский элемент управления (.ascx) или страницу (.aspx) в виде строки HTML. Когда страница или элемент управления наследуются от кода, его класс отображается в intellisense в моем внутреннем коде, и я могу создать экземпляр и установить свойства без получения ошибок времени компиляции или выполнения. Однако, когда я пытаюсь отобразить страницу или элемент управления, я всегда получаю пустую строку, и при проверке страница или элемент управления показывают скрытые внутренние ошибки визуализации, если я не загружаю страницу или элемент управления, используя физический путь к файлу.

Я думаю, что ключевая проблема связана с тем, когда и как файлы .aspx / .ascx компилируются во время выполнения. Я не хочу создавать предварительно скомпилированную библиотеку классов пользовательских элементов управления, потому что это сделает процесс проектирования неудобным, и мне очень нравятся дизайнерские функции, предлагаемые страницами .aspx / .ascx, и поэтому я хотел бы найти способ Сделайте так, чтобы страницы компилировались в решении так, чтобы они были пригодны для использования как любой другой внутренний класс, но все еще могли быть созданы с помощью дизайнера. Я хочу, чтобы лучшее из обоих миров (1) могло редактировать страницы и элементы управления в конструкторе и (2) создавать экземпляры и устанавливать их свойства с помощью внутренних классов.

Ответы [ 4 ]

1 голос
/ 09 марта 2010

Вот подход, который может помочь в подобных ситуациях.

Код "back-end" может не знать, где находится пользовательский элемент управления, но пользовательский элемент управления знает, где он находится.

Итак, в пользовательском элементе управления добавьте статический метод, подобный этому:

public partial class MyControl : UserControl
{
  ...
  public static MyControl LoadControl(CustomDto initialData)
  {
    var myControl = 
        (MyControl) 
        ((Page) HttpContext.Current.Handler)
        .LoadControl("~\\UserControlsSecretPath\\MyControl.ascx");
    myControl._initialData = initialData;
    return myControl;
  }
  ...
  private CustomDto _initialData;
}

(CustomDto включен, чтобы проиллюстрировать, как исходные данные могут быть переданы в пользовательский контроль. Если вам не нужно это делать, извлеките их!)

При этом код, который загружает пользовательский элемент управления, не должен знать путь к физическому расположению пользовательского элемента управления. Если это местоположение когда-либо изменится, обновите это местоположение. Весь другой код, который использует этот UserControl, остается неизменным.

В своем внутреннем коде или где-либо еще вы можете сделать что-то такое:

var control = MyControl.LoadControl(customDto);
PlaceHolder1.Controls.Add(control);
0 голосов
/ 01 сентября 2009

Я разработал решение, которое решает мою проблему в VS 2008:

  1. Создание решения для основного сайта: Создание решения для сайта MVC 1 в VS 2008
  2. Создание библиотеки классов модели: Добавление библиотеки классов для кода модели
  3. Создать код представления: Добавить «Пустой веб-сайт» для хранения страниц .ascx и добавить ссылку на библиотеку моделей
  4. Создать сайт развертывания: Добавить проект развертывания, который компилирует «Пустой сайт», перейдите на «страницу свойств» и Проверить : «Объединить все выходные данные в одну сборку» и « Считать компонентом библиотеки "и обязательно UnCheck :" Разрешить обновление этого предварительно скомпилированного сайта "
  5. Ссылка на вывод развертывания: В основной проект добавьте ссылку на вывод сайта развертывания.
  6. ASP. - Скомпилированные элементы управления: Элементы управления отображаются под ASP. пространство имен и названы двумя способами О. Если страница .ascx / aspx не объявляет «ClassName», то они именуются с использованием своей папки и имени файла с подчеркиванием ex. <% @ Control Language = "C #" ClassName = "Admin_Index"%> B. если они объявили имя класса, то это его имя

  7. Элемент списка

Использование: Пример кода ниже

Вот пример использования

public ActionResult Index()
{
    var ctl = new ASP.Directory_FirmProfile();  //create an instance
    ctl.Setup(new MyDataModel);                 //assign data

    //string test = CompiledControl.Render(ctl); //output to string
    return new HtmlCtl.StrongView(ctl);         //output to response
}    



   public class CompiledControl
    {
        public static string Render(Control c)
        {
            Page pageHolder = new Page();
            pageHolder.Controls.Add(c);
            StringWriter output = new StringWriter();
            HttpContext.Current.Server.Execute(pageHolder, output, false);
            return output.ToString();
        }

        public static void Render(Control c, StringWriter output)
        {
            Page pageHolder = new Page();
            pageHolder.Controls.Add(c);
            HttpContext.Current.Server.Execute(pageHolder, output, false);
        }

        public static void Render(Control c, HttpResponseBase r)
        {
            Page pageHolder = new Page();
            pageHolder.Controls.Add(c);
            HttpContext.Current.Server.Execute(pageHolder, r.Output, false);
        }


    }


    public class StrongView : ActionResult
    {
        private Control ctl;
        public StrongView(Control ctl)
        {
            this.ctl = ctl;
        }

        public string VirtualPath{get;set;}


        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            HtmlCtl.CompiledControl.Render(ctl, context.HttpContext.Response);

        }
    }
0 голосов
/ 05 января 2010

Я придумал более простое решение в духе советов Рубена. Работает без проблем около месяца:

//Example usage

//reference the control
var emailCTL = new HtmlCtl.ControlOnDisk<MyControlType>(@"~\Views\EmailTemplates\MyControlType.ascx");

//if you have a code behind you will get intellisense allowing you to set these properties
// and re-factoring support works most places except the template file. 
emailCTL.c.title = "Hello World "; //title is a property in the code behind
emailCTL.c.data = data; //data is a property in the code behind

string emailBody = emailCTL.RenderStateless(); 



//Helper Class
    public class ControlOnDisk<ControlType> where ControlType : UserControl
    {
        public ControlType c;
        Page pageHolder = new Page();
        public ControlOnDisk(string path)
        {
            var o = pageHolder.LoadControl(path);
            c = (ControlType)o;
            pageHolder.Controls.Add(c);
        }

        public string RenderStateless()
        {

            StringWriter output = new StringWriter();

            // set up dumby context for use in rendering to email stream
            StringBuilder emailMessage = new StringBuilder();
            TextWriter tw = new StringWriter(emailMessage);
            HttpResponse dumbyResponse = new HttpResponse(tw);
            HttpRequest dumbyRequest = new HttpRequest("", "http://InsertURL.com/", ""); //dummy url requierd for context but not used
            HttpContext dumbyContext = new HttpContext(dumbyRequest, dumbyResponse);
            //HttpContextBase dumbyContextBase = new HttpContextWrapper2(dumbyContext);

            dumbyContext.Server.Execute(pageHolder, output, false);
            return output.ToString();

        }
    }
0 голосов
/ 25 августа 2009

Вообще говоря: нет.

Насколько я знаю, ASP.NET наследует от ваших классов , чтобы объединить шаблон .aspx / .ascx с вашим кодом. Вот почему ваши элементы управления отображаются пустыми: код для объединения шаблона с вашим кодом отсутствует. Обычно это делается в ASP.NET при первом обращении к странице или пользовательскому элементу управления (именно поэтому первое попадание немного медленное: оно фактически генерирует и компилирует код подключения).

Для предварительно скомпилированных веб-сайтов ASP.NET заранее создает этот код как часть .dll предварительно скомпилированного веб-сайта, поэтому такие сайты загружаются быстрее. Тем не менее, IIRC вам все равно нужно будет создавать сгенерированные классы, а не исходные классы.

Это довольно распространенный запрос, но до сих пор MS не предоставила инструменты для этого.

Редактировать: Хотя я не понимаю, почему вы хотите отобразить элемент управления в строку в памяти, у меня может быть решение проблем сборки.

Если вы придерживаетесь не скомпилированных файлов .ascx (используя модель веб-сайта, а не модель веб-приложения), вы на самом деле можете разрабатывать их отдельно, помещая их физически в подпапку основного проекта и рассматривая их как файлы содержимого. только. Затем вы можете создать отдельный проект с этой подпапкой в ​​качестве корневой. Вам нужно только обрабатывать файлы в этой подпапке как файлы веб-сайта, основным проектом по-прежнему может быть веб-приложение. (На самом деле рекомендуется, потому что вы не хотите, чтобы файлы .csproj были включены в основной проект.)

Однако общий код (то есть общий для проекта элементов управления и основного проекта) следует поместить в отдельный библиотечный проект, чтобы можно было скомпилировать каждый проект отдельно без взаимозависимостей.

Использование LoadControl в основном проекте скомпилирует их на лету (возможен код сзади); если вам нужно установить свойства, вы должны определить интерфейсы в общем проекте, внедрить их в соответствующие пользовательские элементы управления и привести элемент управления, созданный с помощью LoadControl, к соответствующему интерфейсу.

...