DOBON.NET DOBON.NETプログラミング掲示板過去ログ

クラスの配列をプロパティとして持ちたい

分類:[.NET]

Nicolasです。いつも参考にさせていただいております。
不躾ながら早速質問させていただきます。
WindowsApplicationでWindowsFormクラスを継承して使いやすいクラスを作成しています。
継承クラスにプロパティ項目としてString型とInteger型をひとかたまりにしたクラスを追加したいのですが、項目数が動的に変化するため、配列としてプロパティに追加したいのです。

例で言うと、DataGridクラスで、
列名、列幅をプロパティとして持つクラスを作り
列数は変更することがあるので、このクラスを配列にする。
配列にした物を、継承クラスのプロパティとして追加する。
と言った感じです。
ライブラリにした後、Formに追加でき、デザインビューでプロパティを見ると項目は追加されており、配列にもなっています。(追加したり変更したりできる。)
問題は、プロパティを変更後、親Formのコードを見ると、値が変更されていない為、読み取りできずにいます。

具体的には、(子)MyDataGrid クラスを作成し、これを(親)Formに追加する。
追加された(子)MyDataGridのプロパティをデザイナで変更する。
ここで、(親)Formのコード内でMe.MyDataGrid.Members
のプロパティが変更されていない。
ほかのプロパティは変更できるのでクラスの配列の追加方法に問題があると思われます。

コードの一部ですが以下の物です。
Setの仕方が悪いのはわかっているのですが、どう追加したらいいのかわからずに困っていますので案のある方、ご教授お願いいたします。

-------以下コード--------------

Public Class MyDataGrid
Inherits DataGrid
:
:
Public Class aMember
Private _str As String
Private _int As Integer

Sub New()
MyBase.new()
End Sub
Public Property STR() As String
Get
Return _str
End Get
Set(ByVal Value As String)
_str = Value
End Set
End Property

Public Property INT() As Integer
Get
Return _int
End Get
Set(ByVal Value As Integer)
_int = Value
End Set
End Property
End Class

Private _members() As aMember = {New aMember, New aMember}
Public Property Members() As aMember()
Get
Return _members
End Get
Set(ByVal Value As aMember())
_members = Value
End Set
End Property
:
:
End Class 'MyDataGrid
■No969に返信(Nicolasさんの記事)

Private _members() As aMember = {New aMember, New aMember}
Public Property Members(Index as Integer) As aMember()
Get
Return _members(Index)
End Get
Set(ByVal Value As aMember())
_members(Index) = Value
End Set
End Property

こういうことですか? 検証してないけど...
どらごらさん ヒントありがとうございます!
お返事遅くなってしまって申し訳ありません。


> Private _members() As aMember = {New aMember, New aMember}
> Public Property Members(Index as Integer) As aMember()
> Get
> Return _members(Index)
> End Get
> Set(ByVal Value As aMember())
> _members(Index) = Value
> End Set
> End Property
>
> こういうことですか? 検証してないけど...

そういうことなんです。
ただ早速試して見たところ残念ながらプロパティに引数を持っているせいかプロパティに追加されませんでした。(Getで返すのが配列になってないのでちょっと変更しましたが。)

とりあえず検証する方法として、
1>プロジェクトに新しくUserControlなどを追加する。
2>コードに上記のプロパティを追加。
3>ビルドして、呼び出し側になるフォームなどに追加してみる。
4>デザインビューからF4でプロパティを表示してプロパティとして追加されているか確認。
5>プロパティを変更(配列を追加など)した時に、親フォームに追記されているか確認
もしくは開始してみたあとに、先ほどの変更が反映されているか確認。

と言った手順です。
本来、親フォームに貼り付けてからプロパティを変更すると親フォーム内で、Locationとか、TabIndexとか、Nameなどと同様に追記されないとおかしいのですが、ここに変更されないのが今回の問題なのです。

関係ないクラスで、
Public Sub New(Container As System.ComponentModel.IContainer)
MyClass.New()
Container.Add(me)
End Sub
と言うのを見たのですが、関係ないでしょうか?

些細なヒントでも結構です。案がありましたらResお願いいたします。
■No982に返信(Nicolasさんの記事)
体調崩してました。すみません。

> と言った手順です。
> 本来、親フォームに貼り付けてからプロパティを変更すると親フォーム内で、Locationとか、TabIndexとか、Nameなどと同様に追記されないとおかしいのですが、ここに変更されないのが今回の問題なのです。

そもそも配列のプロパティは、標準では表示されません。
独自でプロパティエディタをOverrideする必要があります。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/mwdesign/html/mwdlgpropertyoverrides.asp

それ以外としてはIndex自体を、別のプロパティにしちゃうってのも
手かも知れません。お勧めしませんが(--;

Private _members() As aMember = {New aMember, New aMember}
Public Property Members As aMember
  Get
    Return _members(_membersIndex)
  End Get
  Set(ByVal Value As aMember)
    _members(_membersIndex) = Value
  End Set
End Property

Private _membersIndex As Integer
Public Property MemberIndex As Integer
  Get
    Return _membersIndex
  End Get
  Set(ByVal Value As Integer)
    _membersIndex = Value
  End Set
End Property
参考意見ありがとうございます。
とても参考になります。

> そもそも配列のプロパティは、標準では表示されません。
> 独自でプロパティエディタをOverrideする必要があります。

> http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/mwdesign/html/mwdlgpropertyoverrides.asp
>
> それ以外としてはIndex自体を、別のプロパティにしちゃうってのも
> 手かも知れません。お勧めしませんが(--;

これとは別の方法ですが一応実現する事はできました。
しかしまだ少々問題が残っており、これが解消できないとちょっとばかり安定しないことがありましてまだ悩んでおります。

解決方法ですが、
Member クラスと
MemberCollection クラスを作成し、
これを MyDataGrid クラスのプロパティとして追加しました。

ポイントは、MemberCollectionClassを
System.Collections.ArrayList から派生させ、Memberクラスの配列として
MemberCollectionClass自体を配列クラスにした事です。
また、AddRangeメソッドも明示的に変更しています。


---------以下コードです。が・・・・・

new public ColumnMember this[int index]
{
  get
  {
  return this[index];
  }
  set
  {
    this[index] = value;
  }
}

// AddRange を明示的に実装します。
public void AddRange(ColumnMember[] obj)
{
  base.AddRange(obj);
}

参考にした文書が英語サイトで、C#のものだったのでこれをDllとして使用してしまいました。
このコードをVBに直せずここだけC#なんです。トホホ
この文章VBに直せるなら書き込みお願いできませんでしょうか・・・

詳しいコードはまだまとまっていないので
でき次第のせるつもりです。

また、これは動作するので良いのですが、
デフォルトで配列のメンバを持っていたい場合の初期追加方法がわかりません。

どういうことかと言うと、
まずMyDataGridクラスをDllとして参照した呼び出しFormに
MyDataGridをMyDataGrid1としてインスタンスを作成します。
この時点でFormのメンバとしてMemberクラスのインスタンスデフォルトとして2-3個もち、さらにそれが
MyDataGrid1にAddRangeされている状態にしたいのです。

これができないので、MyDataGrid1を作成した時点でFormにグリッドを描画できないのです。


これも検証できるか試してみようと思います。

>
> Private _members() As aMember = {New aMember, New aMember}
> Public Property Members As aMember
> Get
> Return _members(_membersIndex)
> End Get
> Set(ByVal Value As aMember)
> _members(_membersIndex) = Value
> End Set
> End Property
>
> Private _membersIndex As Integer
> Public Property MemberIndex As Integer
> Get
> Return _membersIndex
> End Get
> Set(ByVal Value As Integer)
> _membersIndex = Value
> End Set
> End Property

技術的な情報を共有することができてうれしく思います。
もう少しがんばってみます。
とりあえずまでに。
2003/10/23(Thu) 18:10:18 編集(投稿者)

どらごらです。
System.Collections.ArrayListから派生させてみました。
確かにこれだと、プロパティダイアログ要らないんですね。
僕も参考になりました。


-----
Public Class UserControl1
    Inherits System.Windows.Forms.UserControl

#Region " Windows フォーム デザイナで生成されたコード "
...
#End Region

    'プロパティ
    Private FMembers As MyMemberCollection
    Public Property Members() As MyMemberCollection
        Get
            Return FMembers
        End Get
        Set(ByVal Value As MyMemberCollection)
            FMembers = Value
        End Set
    End Property

End Class

Public Class MyMember

    '***プロパティ
    'DataGrid
    Private FDataGrid As System.Windows.Forms.DataGrid
    Public Property DataGrid() As System.Windows.Forms.DataGrid
        Get
            Return FDataGrid
        End Get
        Set(ByVal Value As System.Windows.Forms.DataGrid)
            FDataGrid = Value
        End Set
    End Property

    'String
    Private FStr As String
    Public Property Str() As String
        Get
            Return FStr
        End Get
        Set(ByVal Value As String)
            FStr = Value
        End Set
    End Property

    'Integer
    Private FInt As Integer
    Public Property Int() As Integer
        Get
            Return FInt
        End Get
        Set(ByVal Value As Integer)
            FInt = Value
        End Set
    End Property

    Public Sub New()
        FDataGrid = New System.Windows.Forms.DataGrid
    End Sub

    Protected Overrides Sub Finalize()
        FDataGrid = Nothing
        MyBase.Finalize()
    End Sub
End Class

Public Class MyMemberCollection
    Inherits System.Collections.ArrayList

    Default Public Shadows Property Item(ByVal Index As Integer) As MyMember
        Get
            Return MyBase.Item(Index)
        End Get
        Set(ByVal Value As MyMember)
            MyBase.Item(Index) = Value
        End Set
    End Property

End Class
Resありがとうございました。
返答が遅くなってしまって申し訳ありませんでしたが、
自分なりに考えてみました。

> どらごらです。
> System.Collections.ArrayListから派生させてみました。

このArrayListではひょっとして問題があるかもしれません。
UserControlクラスの作成自体はでき、プロパティとして
MyMemberCollectionをもち、これがArrayなのでデザインでうまく表示されます。
問題は前回これを使用して
1 Formに貼り付け、
2 プロパティのコレクションに追加、 
3 一度コレクションの小窓を閉じてもう一回開いたときに残っているか確認、
4 ちゃんと追加されており、コレクションに追加されていることを確認。

ここで、コードでこのコレクションのメンバを参照しようとした際に
見れなかったことがありました。
これは派生元が悪かったように記憶しています。

ForEachが使えたので、一応ループするときに中は参照できたのですが、
インデックスを指定することができなかったのです。
ちなみにItem.Countは追加した数はあります。


> Public Class MyMemberCollection
> Inherits System.Collections.ArrayList
>
> Default Public Shadows Property Item(ByVal Index As Integer) As MyMember
> Get
> Return MyBase.Item(Index)
> End Get
> Set(ByVal Value As MyMember)
> MyBase.Item(Index) = Value
> End Set
> End Property




このコードありがとうございました。
Shadowが必要かどうか(隠さないと正常に動かないのか)よくわかりませんが、
自分なりに納得できました。

書き込んで頂きましたコードに少々不足があるように思われますの(省略部分でしょう)で、
コレクション追加できませんでしたが、この辺を直して見たいと思われます。
忘れそうなところとして、

---------UserControlクラス内
components = New System.ComponentModel.Container

Newのとき
  container.Add(Me)

<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Property Fmembers As・・・・

--------MyMemberCollectionクラス内
Public Overloads Sub AddRange(ByVal obj() As Object)
MyBase.AddRange(obj)
End Sub

この辺がポイントでしょうか。

検証してみますが、どうもAllayListが怪しい用の思われます・・・

追って書き込むようにします。
ありがとうございました。
取り合えずまでに。

DOBON.NET | プログラミング道 | プログラミング掲示板