FilePath をロードせずにバックエンド クラスで Ascx コントロールのインスタンス Aspx ページを作成する

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

  •  19-09-2019
  •  | 
  •  

質問

質問: バックエンドコードで可能ですか (コードビハインドではなく、実際のバックエンドクラス内) Load(path) を使用せずに、.aspx または .ascx で定義されたページまたはコントロールを読み込んでレンダリングし、代わりにページ/コントロール クラスのインスタンスを作成するだけです。

これを(コードビハインドではなくバックエンドクラスから)できるようにしたいです。

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 WebApp では、ユーザー コントロール (.ascx) またはページ (.aspx) を HTML 文字列としてレンダリングする必要がある場合があります。ページまたはコントロールがコード ビハインドから継承すると、そのクラスがバックエンド コードのインテリセンスに表示され、コンパイル時エラーや実行時エラーが発生することなくインスタンスを作成してプロパティを設定できます。ただし、ページまたはコントロールをレンダリングしようとすると、常に空の文字列が表示され、物理ファイル パスを使用してページまたはコントロールを読み込まない限り、検査するとページまたはコントロールに抑制された内部レンダリング エラーが表示されます。

重要な問題は、.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 初期データをユーザー コントロールに渡す方法を示すために含まれています。必要ないなら抜いてください!)

これにより、ユーザー コントロールを読み込むコードは次のようになります。 ない ユーザー コントロールが物理的に配置されている場所へのパスを知る必要があります。その場所が変更された場合は、この 1 つの場所を更新します。この UserControl を使用する他のコードはすべて変更されません。

バックエンド コードまたはその他の場所で、次のことを行うことができます。

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

他のヒント

一般的に言えば:いいえ。

私の知る限り、ASP.NET は次のものを継承しています。 あなたのクラスから .aspx/.ascx テンプレートをコードと結合します。これが、コントロールが空で表示される理由です。テンプレートとコードを組み合わせるコードがありません。これは通常、ページまたはユーザー コントロールに初めてアクセスするときに ASP.NET によって行われます (まさにこれが、最初のヒットが少し遅い理由です。実際にはフックアップ コードを生成してコンパイルします)。

プリコンパイルされた Web サイトの場合、ASP.NET はプリコンパイルされた Web サイトの .dll の一部としてこのコードを事前に生成するため、そのようなサイトの読み込みが速くなります。ただし、IIRC では引き続きインスタンスを作成する必要があります。 生成された 元のクラスではなくクラス。

これは非常に一般的な要求ですが、これまでのところ MS はこれを行うためのツールを提供していません。

編集: コントロールをメモリ内の文字列にレンダリングする理由はわかりませんが、ビルドの問題に対する解決策はあるかもしれません。

コンパイルされていない .ascx ファイル (Web アプリケーション モデルではなく Web サイト モデルを使用) にこだわる場合は、それらのファイルをメイン プロジェクトのサブフォルダーに物理的に配置することで実際に個別に開発し、コンテンツ ファイルとしてのみ扱うことができます。次に、このサブフォルダーをルート フォルダーとして別のプロジェクトを作成できます。このサブフォルダー内のファイルを Web サイト ファイルとして扱う必要があるだけで、メイン プロジェクトは引き続き Web アプリケーションにすることができます。(メイン プロジェクトに .csproj ファイルを含めたくないため、実際にはこれをお勧めします。)

ただし、共有コード (つまり、コントロール プロジェクトとメイン プロジェクト間で共有されるコード) は、相互依存関係なしに各プロジェクトを個別にコンパイルできるように、別のライブラリ プロジェクトに配置する必要があります。

使用する LoadControl メイン プロジェクト内でそれらをオンザフライでコンパイルします (コードビハインドが可能です)。ただし、プロパティを設定する必要がある場合は、共有プロジェクトでインターフェイスを定義し、適切なユーザー コントロールに実装して、作成したコントロールをキャストする必要があります。 LoadControl 適切なインターフェイスに接続します。

私は VS 2008 の問題を解決するソリューションを開発しました。

  1. メイン サイト ソリューションを作成します。 2008年VSでMVC 1 Webサイトソリューションを作成します
  2. モデル クラス ライブラリを作成します。 モデルコードのクラスライブラリを追加する
  3. ビューコードを作成します。 .ascx ページを保持する「空の Web サイト」を追加し、モデル ライブラリへの参照を追加します。
  4. 導入サイトを作成します。 「空の Web サイト」をコンパイルする展開プロジェクトを追加し、「プロパティ ページ」に移動し、 チェック:「すべての出力を 1 つのアセンブリにマージ」および「ライブラリ コンポーネントとして扱う」を必ず実行してください。 チェックを外す:「このプリコンパイル済みサイトの更新を許可します」
  5. 参照デプロイメント出力: メイン プロジェクトに、展開サイトの出力への参照を追加します。
  6. ASP。- コンパイルされたコントロール: コントロールは ASP の下に表示されます。名前空間と2つの方法で名前が付けられています。.ascx / aspx ページで「ClassName」が宣言されていない場合は、フォルダー名とファイル名にアンダースコアを付けて名前が付けられます。<%@コントロール言語= "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);

        }
    }

Ruben のアドバイスに沿って、より簡単な解決策を思いつきました。約1か月間問題なく動作しています。

//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