トップ回答者
ASP.NET Webアプリケーションにおけるカスタムコントロールの作成方法について

質問
-
VisualStudio2015にて、ASP.NET Webアプリケーションを作成しています。
Webフォームのカスタムコントロールについて、お知恵をお貸しください。******環境******
OS:Windows10 Pro
VS:VisualStudio Professional 2015 Version 14.0.25431.01 Update 3
Framework:4.7.2
(言語:VisualBasic)******やりたいこと(最終目標)******
サーバーコントロールの「TextBox」を継承して作成した継承コントロールと、
サーバーコントロールの「DropDownList」を継承して作成した継承コントロールの2つを使用した
カスタムコントロールを作成し、カスタムコントロールを貼り付けたWeb画面にて使用する。
(カスタムコントロールのデザインとしては、
テキストボックスの隣にドロップダウンリストがあるコントロールを作成したいと思っています)
******教えていただきたいこと******
・継承コントロールを使用してのカスタムコントロールの作成の仕方
→既存のサーバーコントロールを使用したカスタムコントロールならば、
WebControlクラスのRenderContentsメソッドをオーバーライドし、HTMLを直書きして作成できましたが、
継承コントロールを使用すると、Web画面のデザイナーにカスタムコントロールが表示されませんでした。
※そもそも、上記の作り方が正しいのかも、わかりません。・カスタムコントロールにて、使用している継承コントロールの機能を使用する方法
→上記の質問の一部にもなりますが、RenderContentsメソッドにて、
テキストボックスとドロップダウンリストを作るHTMLを直書きで記述して作成してしまうと、、
この方法では、カスタムコントロールにどんなコントロールが貼られているか、
システムが認識できないため、テキストボックスとドロップダウンリストのそれぞれの機能が
使えないものだと思います。
どうすれば、カスタムコントロールの部品には「テキストボックス」と「ドロップダウンリスト」があり、
システム側に、この2つを認識させることができるでしょうか?
******(参考として)このようなカスタムコントロールを作成したい理由/背景******
もともとは、「Windowsフォームアプリケーション」にて作成したアプリを
Webアプリケーション化しようとしたのが発端です。完成済みのWindowsフォームアプリケーションでは、上記の「やりたいこと」に記載した内容のように、
「Windows標準コントロールの「TextBox」を継承して作成した継承コントロールと、
Windows標準コントロールの「ComboBox」を継承して作成した継承コントロールの2つを使用した
ユーザーコントロール」を作成しています。Webアプリケーション側でも、同じようにユーザーコントロールを作成しようと思いましたが、
Webのユーザーコントロールは、再利用ができないことを知り、
カスタムコントロールを作成することにしました。
(完成済みのWindowsフォームアプリケーションには、複数のプロジェクトがあり、
Web側でも同じ構成にしたいと考えています。ですが、ユーザーコントロールは再利用できないとなると、
各プロジェクトにユーザーコントロールをそれぞれ配置することになり、
メンテナンス性に欠けるため、できれば避けたいと思っています)
ちなみに、
https://codezine.jp/article/detail/162
上記のサイトに、カスタムコントロールの作成の仕方が載っていましたが、
自分が勉強不足というのもあり、正直、よくわかりませんでした。
Windowsフォームアプリケーションでできることが、
Webアプリケーションでもできるのかすら、わかりませんが、
どうぞ、ご教授の程、よろしくお願いいたします。- 編集済み ささごん 2020年5月7日 8:04
回答
-
前に書きました、
コントロールのインスタンスがユーザーの PC に存在する Windows Forms アプリと、コントロールのインスタンスはサーバーにしかなく(それもユーザーがブラウザを見ているときは消去されてます)ユーザーの PC にあるのはサーバーが送信した html, css, javascript だけという Web Forms アプリの違いを認識してますか?
・・・ということはしっかり認識できましたでしょうか?
ASP.NET Web Forms アプリ開発では、サーバー側とクライアント側で起こっていることは違うということを強く意識してください。その意識を持って開発しないと、今後何度も同じ問題にはまって悩むことになります。
で、本題ですが、質問者さんのコード、> Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
> writer.Write("<asp:TextBox ID = ""TextBox1"" runat=""server"" ...以下省略...が目的に合ってません。
その writer.Write が何をしているのかというと、ブラウザに送る html コードをそのまま書き込んでいるのです。
なので、ブラウザは、
<asp:TextBox ID = "TextBox1" runat="server" ...
というコードを受け取ることになります。それはブラウザにとって有効な html コードではなく、ブラウザとしては何ともできないのは分かりますか?
RenderContents メソッドの writer.Write の引数に設定する文字列はブラウザが受け取って解釈できる有効な html コードでないとダメです。
具体例は以下の記事を見てください。記事のタイトルは置いといて、基本的な書き方、特に RenderContents の writer.Write の引数が html ソースの文字列であることに注目してください。TagKey メソッドも必要ですので注意してください。
多言語対応カスタムコンロトール
http://surferonwww.info/BlogEngine/post/2010/12/06/Multi-Languages-Web-Custom-Control.aspxしかし、RenderContents メソッドでは、今回の質問者さんの課題であるサーバーコントロールをカスタムコントロールに含めることができません。
サーバーコントロールをカスタムコントロールに含めるには CreateChildControls メソッドを使ってください。具体例は以下の記事を見てください。
リソース埋め込みカスタムコントロール
http://surferonwww.info/BlogEngine/post/2012/05/19/Web-custom-control-with-embedded-resources.aspx- 回答としてマーク ささごん 2020年5月10日 22:54
すべての返信
-
Hoshinaです
こんにちは
質問に対して、質問をすることを最初にお断りしておきます。
質問1.
ASP.NET で作成するとのことですが、
以下のどの方法を想定していますか?・Web Form
参考としているページは、こちらのようです
・Web MVC
質問2.
ご希望の画面は、HTML だけを組み合わせて実現できますか?
一般論として、Windows フォームでできることがなんでも WEB アプリでできるとは限りません。
私流の回答は、次になります。
Web MVC で作成するとして、複数のコントロールから構成された部品画面を作り、
その部品画面を複数の画面から共通部品として使用するようにします。
例えば、複数の画面共通のヘッダーやフッターやメニューなどを一か所に定義して、使いまわすといった場合が例として分かりよいと思います。
その部品画面の先頭で、@model を定義して、部品画面を描写するのに必要なモデルを定義します。Web MVC の場合、部品画面はアンダースコア「_」で始まる名前を付けるという慣習があり、
それを利用する側では、@Html.Partial("_部品画面名") のように記述します。
これが、希望する回答になっていればよいのですが。
それでは -
Hoshina さん>
> ASP.NET で作成するとのことですが、
> 以下のどの方法を想定していますか?
>
> ・Web Form
> 参考としているページは、こちらのようです
> ・Web MVCASP.NET Web Forms アプリの話に間違いないと思います。
質問の最初の方に「Webフォームのカスタムコントロールについて」と書いてありますし、質問者さんが参考にされているサイト、
https://codezine.jp/article/detail/162
も ASP.NET Web Forms アプリの記事です。
-
質問者さん>
質問者さんがやりたいことを実現するために考えた必要最小限のサンプルコードを書いてアップしてください。(あくまで必要最小限でお願いします。長いコードをベタっと張り付けるのは NG です)
そして、そのコードの実行結果がどうなるかできれば画像をアップして説明し、それが質問者さんの期待することとどう違うのか書いてください。
> Windowsフォームアプリケーションでできることが、
> Webアプリケーションでもできるのかすら、わかりませんが、コントロールのインスタンスがユーザーの PC に存在する Windows Forms アプリと、コントロールのインスタンスはサーバーにしかなく(それもユーザーがブラウザを見ているときは消去されてます)ユーザーの PC にあるのはサーバーが送信した html, css, javascript だけという Web Forms アプリの違いを認識してますか?
それを認識できれば、
> 、「Windowsフォームアプリケーション」にて作成したアプリをWebアプリケーション化
ということは考えもしないと思うのですが・・・
-
Hoshinaです
質問者の方不在で、やり取りをすることについて、
このやり取りが質問者さんや読者さんに何か役に立てば無駄ではなかろうと考えています。
SuferOnWww さん、ご指摘ありがとうございます。
実は、Web Forms であろうなとは思っていましたが、初めの内容の投稿をしました。
その意図は、できることなら Web MVC に誘導できたら、そうした方が良いかなと思ったからです。
(この誘導は、余計がことかもしれませんけど)
その理由は、Windows Form を Web アプリに作り替えるとした場合、
・かなり柔軟性の高い操作方法を提供することが必須になるだろう
・そうした場合、Web Forms のポストバックによる仕組みは、ブラウザ上での操作に制限がでやすくなるだろう
・一方 Web MVC の方がブラウザ上での柔軟な操作を提供しやすいだろう
こうしたことが理由で、私がやるとしたらの仮定の下 MVC を使用する場合を例に挙げてみました。それでは
-
Hoshinaさま
返信ありがとうございます。
最初にいただいた投稿内容についてご返答いたしますと、
質問1に対する返答
→Web Formを考えております。
お恥ずかしい話ですが、WebMVCというのは初めて知りました。
質問2に対する返答
→できるものだと考えています。
Windowsフォームアプリケーションにて作成している画面も、
基本はTextBoxやComboBoxぐらいしか使っておらず、
Webでも同じようにTextBoxやDropDownListを使って、画面を組み立てる予定です。
もちろん、Webフォームでは、Windowsフォームアプリケーションのように
コントロールの座標を指定したりすることができないなど、
デザインの仕方が全く異なることは理解しています。
WindowsフォームをWeb化することを考慮し、
WebMVCという新しい選択肢をご紹介してくださり、ありがとうございます。
ですが、大変申し訳ありませんが、WebMVCでの知識が全くないため、
現状では、Webフォームでの開発を前提にご助言をいただけると幸いです。
(ただ、Webフォームにこだわる理由もないので、
WebMVCに方針転換することもあるかもしれません) -
SurferOnWwwさま
返信ありがとうございます。
まず最初に、
> コントロールのインスタンスがユーザーの PC に存在する Windows Forms アプリと、コントロールのインスタンスはサーバーにしかなく(それもユーザーがブラウザを見ているときは消去されてます)ユーザーの PC にあるのはサーバーが送信した html, css, javascript だけという Web Forms アプリの違いを認識してますか?
と質問されれば、正直、まだしっかりとは理解できていません。
実のところ、Webフォームアプリケーションの開発は初めてで、
基礎的なところも、まだまだ足りていないものと思っています。
故に、
> > 、「Windowsフォームアプリケーション」にて作成したアプリをWebアプリケーション化
> ということは考えもしないと思うのですが・・・
との考えにも行きつきませんでした。
それはつまり、Windowsフォームでは、すべてクライアントで処理するに対して、
Webフォームはサーバーで処理するため、通信やメモリなどの関係上、
WindowsフォームアプリケーションをWeb化することは適切ではないということでしょうか?
WindowsフォームをWeb化するという話は別として、
新たにWebフォームアプリケーションをつくるという話として進めさせてください。
サンプルコードを投稿してくださいとのことでしたので、
以下、作成したコードの抜粋部分を投稿させていただきます。
○○○ファイル構成○○○
ソリューション
┣CommonUILibプロジェクト(継承コントロールやカスタムコントロールを定義する)
┃┣BaseControlsフォルダ
┃┃┣AppBaseComboBox.vb(DropDownListを継承し作ったコントロール)
┃┃┗AppBaseInpAll.vb(TextBoxを継承し作ったコントロール)
┃┣BaseControlsフォルダ
┃┃┣WebCustomControl2.vb(TextBoxとDropDownListを含むカスタムコントロール)
┃┃┗WebCustomControl3.vb(AppBaseInpAllとAppBaseComboBoxを含むカスタムコントロール)
┗UserControls調査プロジェクト(スタートアッププロジェクト)
┗WebForm1.aspx(カスタムコントロールを表示したい画面
○○○ソースコード○○○
AppBaseComboBox.vbファイル-----------------------
Public Class AppBaseComboBox
Inherits DropDownList
Public Sub New()
MyBase.New()
End Sub
※その他、DropDownListへデータバインドするメソッドなど
End Class
--------------------------------------------------
AppBaseInpAll.vbファイル-----------------------
Public Class AppBaseInpAll
Inherits TextBoxPublic Sub New()
MyBase.New()
End Sub
※その他、書式設定のプロパティなど
End Class
--------------------------------------------------
WebCustomControl2.vbファイル-----------------------
Public Class WebCustomControl2
Inherits WebControl<Bindable(True), Category("Appearance"), DefaultValue(""), Localizable(True)>
Property Text() As String
Get
Dim s As String = CStr(ViewState("Text"))
If s Is Nothing Then
Return String.Empty
Else
Return s
End If
End GetSet(ByVal Value As String)
ViewState("Text") = Value
End Set
End PropertyProtected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
writer.Write("<asp:TextBox ID = ""TextBox1"" runat=""server"" Width=""40px""></asp:TextBox>")
writer.Write("<asp:DropDownList ID = ""DropDownList1"" runat=""server""></asp:DropDownList>")
End SubEnd Class
--------------------------------------------------
WebCustomControl2.vbファイル-----------------------
Public Class WebCustomControl3
Inherits WebControl<Bindable(True), Category("Appearance"), DefaultValue(""), Localizable(True)> Property Text() As String
Get
Dim s As String = CStr(ViewState("Text"))
If s Is Nothing Then
Return String.Empty
Else
Return s
End If
End GetSet(ByVal Value As String)
ViewState("Text") = Value
End Set
End PropertyProtected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
writer.Write("<%@ Register assembly= ""CommonUILib"" Namespace=""CommonUILib.BaseControls"" tagprefix=""cc1"" %>")
writer.Write("<cc1:AppBaseInpAll ID=""AppBaseInpAll1"" runat=""server"" Width=""30px""></cc1:AppBaseInpAll>")
writer.Write("<cc1:AppBaseComboBox ID=""AppBaseComboBox1"" runat=""server""></cc1:AppBaseComboBox>")
End SubEnd Class
--------------------------------------------------
WebForm1.aspxファイル-----------------------
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="WebForm1.aspx.vb" Inherits="UserControls調査.WebForm1" %><%@ Register Assembly="CommonUILib" Namespace="CommonUILib" TagPrefix="cc1" %><!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
標準コントロールでつくったカスタムコントロール<br />
→<cc1:WebCustomControl2 ID="WebCustomControl21" runat="server" />←<br />
<br />
継承コントロールでつくったカスタムコントロール<br />
→<cc1:WebCustomControl3 ID="WebCustomControl31" runat="server" />←<br />
<br />
</div>
</form>
</body>
</html>
--------------------------------------------------
WebForm1.aspx.vbファイル-----------------------
※このVBファイルには、まだ何も記述していません
Public Class WebForm1
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
End Class
--------------------------------------------------
目標としているのは、以下のようなコントロールです
(参考として、ユーザーコントロールで作成したデザインを載せています)
画面のaspxファイル編集画面です
この時点ですでに、継承コントロールでつくったカスタムコントロールが表示されません。
また、標準コントロールでつくったカスタムコントロールを2つ以上貼るとID重複によりエラーになります。
(WebCustomControl2.vbファイルのRenderContentsにて書いたHTMLにて、
TextBoxのIDを固定書きしているのが原因と思われます。
ゆくゆくは、この事象も解決しないといけないと思っています)
実行した際のIEです
カスタムコントロールは両方とも表示されません。
また、頻繁に以下のエラー画面が表示されます。
(上記のIEの画像は、稀にうまくいったときの画面です。
これについてもエラーになる理由がわかりません)
お詳しい方にとっては、わけのわからないコーディングに見えてしまうと思いますが、
何卒、引き続きのご指導のほどよろしくお願いします。
-
> また、頻繁に以下のエラー画面が表示されます。
そこがそもそも全く変です。カスタムコントロール作成以前の問題があるようで、そういうことが起こるのをまず解決しないと、どうしようもないと思うのですが?
- 編集済み SurferOnWww 2020年5月8日 11:30 追記
-
前に書きました、
コントロールのインスタンスがユーザーの PC に存在する Windows Forms アプリと、コントロールのインスタンスはサーバーにしかなく(それもユーザーがブラウザを見ているときは消去されてます)ユーザーの PC にあるのはサーバーが送信した html, css, javascript だけという Web Forms アプリの違いを認識してますか?
・・・ということはしっかり認識できましたでしょうか?
ASP.NET Web Forms アプリ開発では、サーバー側とクライアント側で起こっていることは違うということを強く意識してください。その意識を持って開発しないと、今後何度も同じ問題にはまって悩むことになります。
で、本題ですが、質問者さんのコード、> Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
> writer.Write("<asp:TextBox ID = ""TextBox1"" runat=""server"" ...以下省略...が目的に合ってません。
その writer.Write が何をしているのかというと、ブラウザに送る html コードをそのまま書き込んでいるのです。
なので、ブラウザは、
<asp:TextBox ID = "TextBox1" runat="server" ...
というコードを受け取ることになります。それはブラウザにとって有効な html コードではなく、ブラウザとしては何ともできないのは分かりますか?
RenderContents メソッドの writer.Write の引数に設定する文字列はブラウザが受け取って解釈できる有効な html コードでないとダメです。
具体例は以下の記事を見てください。記事のタイトルは置いといて、基本的な書き方、特に RenderContents の writer.Write の引数が html ソースの文字列であることに注目してください。TagKey メソッドも必要ですので注意してください。
多言語対応カスタムコンロトール
http://surferonwww.info/BlogEngine/post/2010/12/06/Multi-Languages-Web-Custom-Control.aspxしかし、RenderContents メソッドでは、今回の質問者さんの課題であるサーバーコントロールをカスタムコントロールに含めることができません。
サーバーコントロールをカスタムコントロールに含めるには CreateChildControls メソッドを使ってください。具体例は以下の記事を見てください。
リソース埋め込みカスタムコントロール
http://surferonwww.info/BlogEngine/post/2012/05/19/Web-custom-control-with-embedded-resources.aspx- 回答としてマーク ささごん 2020年5月10日 22:54
-
SurferOnWwwさま
ご返信ありがとうございます。
最初に、
エラーを解決せず、ただ質問だけをしてしまったこと、反省しております。以後、気を付けます。
また、Webフォームアプリケーションをつくる上で、サーバー側とクライアント側では動きが別であることを理解し、今後の開発を行っていこうと思います。
本題ですが、
自分の書いていたコードでは根本的に間違っており、その理由もご説明していただき、ありがとうございます。
また、教えていただいたサイトを元に、つくってみたところ、理想としているものにかなり近づくことができました。
以下、新たに作成したコードです。
CodeSelectComboBase.vbファイル-----------------------
(新たに作成したカスタムコントロールのクラスです)
Public Class CodeSelectComboBase
Inherits WebControl
Private oblTextBox As BaseControls.AppBaseInpAll = New BaseControls.AppBaseInpAll
Private oblComboBox As BaseControls.AppBaseComboBox = New BaseControls.AppBaseComboBox
Protected Overrides Sub CreateChildControls()
Dim cs As ClientScriptManager = Page.ClientScript
'リテラル(固定文字列)の設定
Dim tableStart As Literal = New Literal
tableStart.Text =
"<table cellspacing='0' cellpadding='0'>" +
"<tr><td valign='middle'>"
Me.Controls.Add(tableStart)
'テキストボックスの設定
'Dim oblTextBox As BaseControls.AppBaseInpAll = New BaseControls.AppBaseInpAll
oblTextBox.ID = "MyTextBox1"
Me.Controls.Add(oblTextBox)
'コンボボックスの設定
'Dim oblComboBox As BaseControls.AppBaseComboBox = New BaseControls.AppBaseComboBox
oblComboBox.ID = "MyComboBox1"
Me.Controls.Add(oblComboBox)
'リテラル(固定文字列)の設定
Dim tableEnd As Literal = New Literal
tableEnd.Text = "</td></tr></table>"
Me.Controls.Add(tableEnd)
MyBase.CreateChildControls()
End Sub
Public Sub subComboSetting()
'ドロップダウンリストへのデータ設定
'(AppBaseComboBoxで設定したメソッドを読み出し、データの設定をする)
End Sub
End Class
'-----------------------------------------------------IEの画面です
ドロップダウンリストへのデータ設定などは、ページのロードイベント等で、CodeSelectComboBaseクラスの自作のメソッドを呼び出すことで解決することができそうです。
ただ、現状で、1点困っていることは、VisualStudioにてWebフォームのデザインをする際に、作成したカスタムコントロールが表示されないことです。教えていただいたように、カスタムコントロールのクラス内で書いたHTMLは、ブラウザにとって有効なHTMLコードであるので、逆にVisualStudio側では無効なHTMLとなってしまったのでしょうか?
ともあれ、ここまで作成することができ、非常に助かりました。
ありがとうございました。 -
> 現状で、1点困っていることは、VisualStudioにてWebフォームのデザインをする際に、作成したカスタムコントロールが表示されないことです。
RenderContents メソッドで作ったカスタムコントロールは Visual Studio のデザイン画面で表示されますが、CreateChildControls メソッドを使ってサーバーコントロールを組み立てて作ったものは質問者さんが試した通りデザイン画面で表示されません。
コードビハインドで動的に配置したコントロールはデザイン画面では表示されないのと同じことです。
> カスタムコントロールのクラス内で書いたHTMLは、ブラウザにとって有効なHTMLコードであるので、逆にVisualStudio側では無効なHTMLとなってしまったのでしょうか?
見当違いです。
Visual Studio は Page と共にそれに配置したコントロールをメモリにロードして、ブラウザで見た状態にできるだけ近いような形でデザイン画面に表示してくれるということです。(上に書いた様にコードで動的に配置したコントロールは除く)
一つの疑問の解決が、次の疑問を呼んで質問が続いているという感じですが、そういうことはもうやめてもらって、表題の「ASP.NET Webアプリケーションにおけるカスタムコントロールの作成方法について」は解決できたようですので、このスレッドは役に立った回答にマークしてクローズしてください。
- 編集済み SurferOnWww 2020年5月10日 0:55 引用追加