- PR -

WEBカスタムコントロールにデザイン時でも設定可能なString配列型のプロパティを作成したい

投稿者投稿内容
EOSB200
会議室デビュー日: 2004/04/01
投稿数: 18
投稿日時: 2005-05-09 15:08
Jitta様、ご返答ありがとうございます。
教えて頂いたリンク先の情報を試してみましたが、デザイン時に設定可能なコレクションのプロパティは実現されていませんでした。残念です・・

String配列型がダメなら、StringCollection型だ!と思い、同じプログラムで型を変更して試してみましたが、デザイン時に値を設定しようとするとエラーになってしまいました。
具体的には、この様な動作になりました。以下の操作は全てVS.NETで行っています。
(1)作成したWebカスタムコントロールをWebフォームに配置。
(2)プロパティウインドウのDataColumnNameプロパティ欄にカーソルを持っていくと[...]ボタンが表示されるのでクリック。
(3)「Stringコレクションエディタ」ウインドウが表示されるので、[追加]ボタンをクリックすると、「型 System.String にコンストラクタが見つかりませんでした。」が表示される。
原因がわからない・・・

別の考え方ですが、HTMLコードにプロパティの値が自動的に記述されるのではなく、WindowsFormsの様に、プロパティに値を設定すればInitializeComponentに自動的にプログラムを書いてくれるなんて事が出来ないでしょうか。

本当に実現可能なのか、疑問になってきました。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-05-09 19:16
EOSB200さん、こんばんは。

#未確認ですが…。

引用:

(3)「Stringコレクションエディタ」ウインドウが表示されるので、[追加]ボタンをクリックすると、「型 System.String にコンストラクタが見つかりませんでした。」が表示される。


フォームデザイナのコレクションエディタでプロパティを編集可能にするためには、対象のコレクション型が IList をインプリメントしていて、コレクションメンバが、引数なしの Public なコンストラクタで生成可能でなければ駄目だったような気がします。

ということで、上記の要件を満たす独自のコレクション型とメンバ型を定義してプロパティとして公開するとか。
コード:
' コレクションメンバクラス
<ToolboxData("<{0}:MyString></{0}:MyString>")> _
Public Class MyString
    Public Sub New()
    End Sub

    Public Property [Value]() As String
    End Property
End Class

' コレクション
<DefaultProperty("Item")> _
Public Class MyStringCollection
    Implements IList

    <PersistenceMode(PersistenceMode.InnerProperty)> _
    Default Public Overridable ReadOnly Property Item(ByVal Index As Integer) As MyString
        Get
            Return CType(IList_Item(Index), MyString)
        End Get
    End Property

    Private Property IList_Item(ByVal Index As Integer) As Object Implements IList.Item
    End Property
End Class

' コントロール
Public Class MyControl
    Inherits WebControl

    <PersistenceMode(PersistenceMode.InnerDefaultProperty)> _
    Public ReadOnly Property MyStrings() As MyStringCollection
    End Property
End Class



あるいは、EditorAttribute 属性に System.ComponentModel.Design.ArrayEditor を指定してみるとか、でなければ、独自のコレクションエディタを使うという手はどうでしょう?
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-05-09 22:38
コード:
引用
Public Class ExTextBox
	Inherits System.Web.UI.WebControls.TextBox

	Dim _DataColumnName As String()
	Public Property DataColumnName() As String()
		Get 
			Return Me._DataColumnName
		End Get 
		Set(ByVal Value As String())
			Me._DataColumnName = Value
		End Set
	End Property

	Public Sub New()

	End Sub
...
End Sub


 このコードですが、_DataColumnNameは、配列として宣言されています。配列には添え字が必要なのですが、それが定義されていません。では、その添え字はいったいいつ定義されるのでしょうか。
 つまり、『宣言しただけでは空です』。『ない物はありませんから』、参照できません。

 それで、StringCollectionに変えたということは、ここ(太字)を変えたと思います。
コード:
Public Class ExTextBox 
	Inherits System.Web.UI.WebControls.TextBox 

	Dim _DataColumnName As StringCollection
	Public Property DataColumnName() As StringCollection
		Get 
			Return Me._DataColumnName
		End Get 
		Set(ByVal Value As StringCollection)
			Me._DataColumnName = Value
		End Set
	End Property

	Public Sub New()

	End Sub
...
End Sub


 これも同じく、_DataColumnNameの宣言はしています。では、実体はいつ作られるのでしょうか?

 これが原因なら、コンストラクタを次のようにすれば、うまくいくと思います。これで、ないものを作り、宣言だけでなく、実体も作ることになります。
コード:
	Public Sub New()
		' 引数はなかったかな?調べてね
		_DataColumnName = New StringCollection()
	End Sub



 あと、私はコレクションなプロパティは作っていないので推測ですが。
 このコードも、おそらくダメでしょう。Setアクセスメソッドで、_DataColumnName自身を書き換えようとしています。おそらく、このプロパティはReadOnlyでよいと思います。[...]ボタンが表示されて、設定用のウインドウが表示されたのですよね?それなら、Setはいらないでしょう。指定する文字列は、StringCollectionの中に追加されなければならず、StringCollectionを外から指定できるわけではないですから。

_________________
EOSB200
会議室デビュー日: 2004/04/01
投稿数: 18
投稿日時: 2005-05-10 12:26
きくちゃん様、Jitta様、ご返答ありがとうございます。
結論から言うと、まだ実現できていません。
時間がかかりそうなので、途中経過を記載します。

(1)独自のコレクション型とメンバ型を定義してプロパティとして公開する方法
  調査中。
(2)EditorAttribute 属性に System.ComponentModel.Design.ArrayEditor を指定してみる方法
  プロパティウインドウで[...]ボタンを押すと、「オブジェクト参照がオブジェクト インスタンスに設定されていません。」が表示され、設定画面が開かない。
(3)独自のコレクションエディタを使う方法
  調査中。
(4)StringCollectionを使用したソースを修正(Jitta様からご指摘頂いた内容)
  現象は変わらず、[追加]ボタンをクリックすると、「型 System.String にコンストラクタが見つかりませんでした。」が表示される。
  また、ReadOnlyプロパティにしてしまうと、ソース上でプロパティに配列ごとセットできなくなるので、使用上不便になってしまう。
  使い方の例)
   OK:ExTextBox.DataColumnName.Add = "DATA1"
   NG:ExTextBox1.DataColumnName = ExTextBox2.DataColumnName


[ メッセージ編集済み 編集者: EOSB200 編集日時 2005-05-10 12:33 ]
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-05-10 17:19
EOSB200さん、こんにちは。

引用:

(3)「Stringコレクションエディタ」ウインドウが表示されるので、[追加]ボタンをクリックすると、「型 System.String にコンストラクタが見つかりませんでした。」が表示される。


済みません、ここ↑だけに気を取られて、前回の投稿ではコレクション型のプロパティを入れ子になったタグとして永続化する方法を考えていました。

しかし良く見直してみたら、問題は

引用:

<cc1:ExTextBox id="ExTextBox2" runat="server" DataColumnName="String[] 配列">


ここ↑だったんですね…。

これってつまり、標準では String 配列をコントロールタグの属性として永続化する方法が存在しない、って事ですよね。

なので、「String 配列」と「String」との間で相互に変換する型コンバータを実装して、DataColumnName プロパティの TypeConverter 属性に指定してあげれば解決する筈です。

以下サンプル。

コード:

Public Class ExTextBox
    Inherits System.Web.UI.WebControls.TextBox

    Private _DataColumnName As String()

    <TypeConverter(GetType(MyStringConverter))> _
    Public Property DataColumnName() As String()
        Get
            Return _DataColumnName
        End Get
        Set(ByVal Value As String())
            _DataColumnName = Value
        End Set
    End Property

End Class

'String 配列 コンバータ
Public Class MyStringConverter
    Inherits StringConverter

    Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, _
                                                  ByVal culture As Globalization.CultureInfo, _
                                                  ByVal value As Object, _
                                                  ByVal destinationType As Type) As Object
        If (destinationType Is GetType(String)) AndAlso (TypeOf value Is String()) Then
            Return String.Join(Chr(&H7F).ToString(), CType(value, String()))
        Else
            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End If
    End Function

    Public Overloads Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, _
                                                    ByVal culture As Globalization.CultureInfo, _
                                                    ByVal value As Object) As Object
        If (TypeOf value Is String) Then
            Return CType(value, String).Split(New Char() {Chr(&H7F)})
        Else
            Return MyBase.ConvertFrom(context, culture, value)
        End If
    End Function

End Class


カスタム型コンバータの実装については、手っ取り早く StringConverter を継承して、ConvertTo() および ConvertFrom() メソッドをオーバーライドしています。
要は、適当なセパレート文字でくっつけたりバラしたりしているだけです。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-05-10 22:51
 ああ!!ずっとクラスの中でオブジェクトが存在できないことが問題だと思っていました!!

 じゃ、『型 System.String にコンストラクタが見つかりませんでした。』って…ああ!!new string(StringCollection)か!!

# 今日はチョンボのオンパレード...orz

_________________
EOSB200
会議室デビュー日: 2004/04/01
投稿数: 18
投稿日時: 2005-05-11 15:11
きくちゃん様、Jitta様、ご返答ありがとうございます。
つ、ついに実現できました!!

きくちゃん様のソースをそのままコピペすると動きました。
TypeConverterには目を付けていたのですが、適用出来る型が見つからず、あきらめていました。カスタム型コンバータを作る技量もなく・・・

動作としては次の通りとなりました。以下の操作は全てVS.NETで行っています。
(1)作成したWebカスタムコントロールをWebフォームに配置。
(2)プロパティウインドウのDataColumnNameプロパティ欄にカーソルを持っていくと[...]ボタンが表示されるのでクリック。
(3)「文字列コレクションエディタ」ウインドウが表示されるので、文字列を入力する。(改行で複数行入力)最後に[OK]ボタンをクリックして確定。
(4)プロパティウインドウのDataColumnNameプロパティに、入力した文字列が表示される。 (階層表示ではなく単一の文字列として表示される。)
(5)WebフォームのHTMLソースを見ると、次の通りとなっている。
  <cc1:ExTextBox id="ExTextBox" runat="server" DataColumnName="DATA1 DATA2 DATA3"></cc1:ExTextBox>
(6)実行すると、デザイン時に設定したプロパティの値が配列でセットされている。


カスタム型コンバータで使用しているセパレータ[&H7F]が気になっているのですが、そのまま使用して何か問題がないでしょうか?これまでテキストとして目に見えるコードをセパレータに使用していたのですが、当然、実データにはこの文字を使用することを禁止しなければなりません。特殊なコードなので、実データとして使用することはあり得ないと思うので、このまま使おうと思っています。もし注意すべき点がありましたらお教え下さい。

私の質問の書き方が悪く、皆さんにご迷惑をおかけして申し訳ございませんでした。
貴重な時間を使ってご教授頂き、本当にありがとうございました。


[ メッセージ編集済み 編集者: EOSB200 編集日時 2005-05-11 15:13 ]
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-05-11 15:40
EOSB200さん、こんにちは。

引用:

カスタム型コンバータで使用しているセパレータ[&H7F]が気になっているのですが、そのまま使用して何か問題がないでしょうか?


他に思いつかなかったので…。
例えば「┃」とかどうでしょう?
→ ChrW(&H2503)

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