Создайте экземпляр Aspx-страницы элемента управления Ascx во внутреннем классе без загрузки пути к файлу

StackOverflow https://stackoverflow.com/questions/1325101

  •  19-09-2019
  •  | 
  •  

Вопрос

Вопрос: Возможно ли это во внутреннем коде (не в коде, а в реальном серверном классе) загружать и отображать страницу или элемент управления, определенный в .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) создавать экземпляры и задавать их свойства с помощью внутренних классов.

Это было полезно?

Решение

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

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

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

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 включено для иллюстрации того, как исходные данные могут быть переданы пользовательскому элементу управления.Если вам не нужно этого делать, достаньте его!)

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

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

var control = MyControl.LoadControl(customDto);
PlaceHolder1.Controls.Add(control);

Другие советы

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

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

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

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

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

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

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

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

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

  1. Создание Основного решения для сайта: Создайте веб-решение на MVC 1 в версии VS 2008
  2. Создать Библиотеку классов модели: Добавьте библиотеку классов для кода модели
  3. Создать Код просмотра: Добавьте "Пустой веб-сайт" для хранения страниц .ascx и добавьте ссылку на библиотеку моделей
  4. Создать сайт развертывания: Добавьте проект развертывания, который компилирует "Пустой веб-сайт" на "страницу свойств" и Проверить:"Объединить все выходные данные в единую сборку" и "Рассматривать как библиотечный компонент" и обязательно Снимите флажок:"Разрешить возможность обновления этого предварительно скомпилированного сайта"
  5. Выходные данные эталонного развертывания: В основном проекте добавьте ссылку на выходные данные сайта развертывания.
  6. АСПИД.- Скомпилированные элементы управления: Элементы управления отображаются под ASP.пространство имен и именуются двумя способами A.если на странице .ascx / aspx не указано "имя_класса", то они именуются с использованием имени их папки и файла с подчеркиванием, например.<%@ Язык управления="C#" имя_класса="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);

        }
    }

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

//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();

        }
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top