- PR -

Web複合コントロールのデザイン時の表示について

1
投稿者投稿内容
もこな
会議室デビュー日: 2005/07/07
投稿数: 12
投稿日時: 2007-08-16 16:17
Visual Studio 2003 の ASP.NET で Web カスタム コントロールを作ってみています。

複数のコントロールを使用した複合コントロールを試しているのですが、
Visual Studio のデザイナ上でコントールが描画されません。

実はこの会議室で 3 年前に全く同じ質問をされていた方がいて
どうやらその方は解決されたようですが
その方が参考にされたと思われるページを読んでみてもよく分かりませんでした。

以前のやりとりのスレッド
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=8759&forum=7

そのスレッドで紹介されていた参考ページ
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/Vbcon/html/vbwlkWalkthroughCreatingCustomWebControls.asp
#掲載当時のリンクは切れていたので現行の URL に変更してあります。

以前のやりとりではカスタム デザイナが必要かも、というような流れでしたが、
上記の参考ページでは
カスタム デザイナを使ってテキストをハイパーリンクに置き換えています。

今回は、複合コントロールの中に入れたテキストボックスはテキストボックスのままで、
コマンドボタンはコマンドボタンのままで描画したいだけなのですが、
やはり、カスタム デザイナが必要なのでしょうか?
だとしたらどのようなデザイナを実装する必要があるのでしょうか…

ご存知の方がいらっしゃいましたら教えてください。
よろしくお願いします。
かめたろ
ぬし
会議室デビュー日: 2003/03/20
投稿数: 255
投稿日時: 2007-08-16 17:43
ども、3年前の質問者です。
リンクを開いたら私の名前が出てきたので、びっくりしました。

手順としては、
1. 普通にカスタムコントロールを作る。
2. ControlDesignerを継承したクラスを作る。
3. 2のクラスでGetDesignTimeHtmlメソッドをオーバーライドし、デザイン時に表示したいHTMLをリターンする。
4. 1のクラス属性に、2を参照するDesignerAttributeをくっつける。
こんなところでしょうか。

チュートリアルではハイパーリンクに置き換えられちゃってますが、そのまんま表示したければテキストボックスを使えばいいだけのことです。

私も、そのまんま表示したいだけなんだからカスタムデザイナを使わずにどうにかならんのかと徘徊してみましたが、結局分からずじまいでした。

2005だともしかしたら改善されてるのかも・・・
ユーザーコントロールの方はビジュアルに表示されるようになったと聞いてますが。
もこな
会議室デビュー日: 2005/07/07
投稿数: 12
投稿日時: 2007-08-16 18:52
>ども、3年前の質問者です。
>リンクを開いたら私の名前が出てきたので、びっくりしました。

すみません、昔のスレッドを引用させて頂きました。
名指しは失礼だと思い明記しませんでしたが、
ご本人様からコメントいただけて嬉しい限りです。

教えていただいた手順にはなんとなく辿りつけていたのですが、
3.の部分で悩んでしまいました。
子コントロールの HTML をどうやって取得するのか?
それが複数ある場合はどうするのか?などなど…。

サンプル ページの記述では StringWriter や HtmlTextWriter なんかを New していて
この辺を使わないといけないのかどうなのか…とか。

で、先ほど頂いたレスを見ていて、ふと
「ひょっとして、GetDesignTimeHtml の戻り値に直接 HTML を記述するのか!?」
と思いやってみたら・・・、表示されました (^^;
でも、こういうことなのでしょうか??? (^^;;;

ちなみに、子コントロールが Textbox でも、
GetDesignTimeHtml の戻り値に input type=button のタグを書くと
デザイン時はボタンで実行時はテキストボックスという
なんともいえない部品ができてしまいました(笑)。

つまり、子コントロールが何であってもいくつあっても、
デザイン時はそれとは無関係な表示をすることもできる、ということなのかな。

で、画面に貼った部品のプロパティなどは
カスタム コントロールで Public 指定したプロパティなどが表示されるようなので、
表示されているイメージとは無関係、ということでいいんですかね (^^;
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2007-08-16 19:21
引用:

つまり、子コントロールが何であってもいくつあっても、
デザイン時はそれとは無関係な表示をすることもできる、ということなのかな。



そうです。
ただ、そんなことをする意味はないと思いますが。

引用:

で、画面に貼った部品のプロパティなどは
カスタム コントロールで Public 指定したプロパティなどが表示されるようなので、
表示されているイメージとは無関係、ということでいいんですかね (^^;



プロパティグリッドの表示はBrowsable属性で制御可能です。
また、設定されたプロパティに応じて、描画ロジック(GetDesignTimeHtmlの戻り値)を変えるのが普通です。
todo
ぬし
会議室デビュー日: 2003/07/23
投稿数: 682
投稿日時: 2007-08-16 19:50
引用:

複数のコントロールを使用した複合コントロールを試しているのですが、
Visual Studio のデザイナ上でコントールが描画されません。



デザイン時に CreateChildControls が呼ばれないのでしょう。
なので、複合コントロールのライフサイクルの何処かでEnsureChildControlsを呼ぶとか。
# 何処が適切なのかは分かりません。

コード:
protected override void OnLoad(EventArgs e) {
	base.EnsureChildControls();
	base.OnLoad (e);
}

もこな
会議室デビュー日: 2005/07/07
投稿数: 12
投稿日時: 2007-08-17 13:50
burton999 さん、コメントありがとうございます。

引用:
引用:
つまり、子コントロールが何であってもいくつあっても、
デザイン時はそれとは無関係な表示をすることもできる、ということなのかな。


そうです。
ただ、そんなことをする意味はないと思いますが。


確かに(^^;。試してみたらそのとおりになって、面白かったので書いてみました。
実際に使う用途は思いつきません(笑)。

引用:
プロパティグリッドの表示はBrowsable属性で制御可能です。


なるほど、この属性で制御するのですね。実際にやってみてうまくいきました。

引用:
また、設定されたプロパティに応じて、描画ロジック(GetDesignTimeHtmlの戻り値)を変えるのが普通です。


GetDesignTimeHtml の中でプロパティをみて
それに応じた HTML タグを生成するのが普通のやり方なのですね。
勉強になります(^^)。実際やるとなると、結構大変そうですが(^^;
かめたろ
ぬし
会議室デビュー日: 2003/03/20
投稿数: 255
投稿日時: 2007-08-17 13:52
引用:

すみません、昔のスレッドを引用させて頂きました。


いえいえ、過去ログの検索は基本ですからね。
うぉ、オレじゃんwww って驚いただけです。

引用:

子コントロールの HTML をどうやって取得するのか?
それが複数ある場合はどうするのか?などなど…。

サンプル ページの記述では StringWriter や HtmlTextWriter なんかを New していて
この辺を使わないといけないのかどうなのか…とか。


実行時に表示されるであろう姿を"真似して"デザイン時に表示させるかんじです。
実際に作ったコードを見てみると、例えばTextBoxとHyperLinkが入ってるカスタムコントロールに対するデザイナクラスは、
コード:
Public Class ccTextDesigner
    Inherits System.Web.UI.Design.ControlDesigner

    Public Overrides Function GetDesignTimeHtml() As String

        Dim ctl As ccText = CType(Me.Component, ccText)
        Dim sw As New StringWriter
        Dim tw As New HtmlTextWriter(sw)

        Dim _txt As TextBox
        Dim _hl As HyperLink

        '入力テキスト
        _txt = New TextBox
        _txt.Text = ctl.Text
        _txt.Width = ctl.TextWidth
        _txt.CssClass = ctl.CssClassText
        _txt.Enabled = ctl.Enabled
        _txt.RenderControl(tw)

        'ハイパーリンク
        If ctl.HyperLinkVisible Then
            _hl = New HyperLink
            _hl.Text = ctl.HyperlinkTextForDesigner
            _hl.NavigateUrl = ctl.HyperlinkNavigateUrl
            _hl.CssClass = ctl.CssClassHyperlink
            _hl.RenderControl(tw)
        End If

        Return sw.ToString()

    End Function

End Class


こんなんなってました。
ccTextてのが元になるカスタムコントロールで、それに設定されている表示関係のプロパティを転記してHTMLを生成していました。(コードには独自プロパティ含んでます)
HTMLを手書きでゴリゴリ書くのは手間がかかるので、このようにコントロールを作ってレンダーしてHTMLを生成するのが楽だと思います。

コード見直してて、もしかしたら
コード:
    Dim ctl As ccText = CType(Me.Component, ccText)
    Dim sw As New StringWriter
    Dim tw As New HtmlTextWriter(sw)

    ctl.RenderControl(tw)

    Return sw.ToString()


こんなんでもいけるんじゃね?って思ったんですが、試してないので分かりません。
余計なイベントが実行されちゃうような予感がしないでもないですが・・・。
よかったら試してみてください。
もこな
会議室デビュー日: 2005/07/07
投稿数: 12
投稿日時: 2007-08-17 14:13
todo さん、コメントありがとうございます。

引用:
デザイン時に CreateChildControls が呼ばれないのでしょう。
なので、複合コントロールのライフサイクルの何処かでEnsureChildControlsを呼ぶとか。
# 何処が適切なのかは分かりません。


カスタム デザイナを作成して使う方法でいいかな、と思っていたのですが、
上記のヒントを頂いたのでもう少し考えてみました。

で、↓このようなコードを作ってみました。

コード:
<DefaultEvent("Click"), _
 DefaultProperty("Text")> _
Public Class NonDesignerCustomControl
    Inherits System.Web.UI.WebControls.WebControl
    Implements INamingContainer

    ' 子コントロール描画
    Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
        MyBase.EnsureChildControls()
        MyBase.RenderContents(writer)
    End Sub

    ' 公開用イベント
    <Browsable(True)> _
    Public Event Click As EventHandler

    ' イベント処理
    Protected Overridable Sub OnClick(ByVal source As Object, ByVal e As EventArgs)
        Text = "押された"
        RaiseEvent Click(source, e)
    End Sub

    Private Sub Button1_Click(ByVal source As Object, ByVal e As EventArgs)
        OnClick(source, e)
    End Sub

    ' 子コントロール追加
    Protected Overrides Sub CreateChildControls()
        Controls.Add(New LiteralControl("<h2>"))

        Dim Label1 As New System.Web.UI.WebControls.Label
        Label1.Text = "入力→"
        Controls.Add(Label1)

        Dim TextBox1 As New System.Web.UI.WebControls.TextBox
        TextBox1.Text = "999"
        Controls.Add(TextBox1)

        Dim Button1 As New System.Web.UI.WebControls.Button
        Button1.Text = "押す"
        AddHandler Button1.Click, AddressOf Button1_Click
        Controls.Add(Button1)

        Controls.Add(New LiteralControl("</h2>"))
    End Sub

    ' 公開用プロパティ
    <Browsable(True)> _
    Public Property Text() As String
        Get
            Return DirectCast(Controls(2), System.Web.UI.WebControls.TextBox).Text
        End Get
        Set(ByVal Value As String)
            DirectCast(Controls(2), System.Web.UI.WebControls.TextBox).Text = Value
        End Set
    End Property

End Class


上記のような RenderContents メソッドで
カスタム デザイナを追加しなくてもデザイナ上に表示されました。
正しい方法なのかどうか分かりませんが。
#子コントロールにラベルとテキストボックスとボタンを持ったカスタムコントロールで、
#それぞれの子コントロールがデザイナ上に描画されました。

カスタム デザイナなしで表示できた感じなので、
burton999 さんに教えていただいた Browsable 属性を使用して、
Click イベント(実際は子ボタンの Click イベントからのイベント)と
Text プロパティ(実際は子テキストボックスの Text プロパティ)を
公開してみました。
ボタンを押すとテキストボックスに「押された」と表示されます。

このような実装の仕方でよいのですかね?
よろしければアドバイスいただければ幸いです>ALL。
1

スキルアップ/キャリアアップ(JOB@IT)