- PR -

デザイナのないクラス(WindowsFormでない)でのコントロールの扱い

投稿者投稿内容
108
常連さん
会議室デビュー日: 2007/12/12
投稿数: 45
投稿日時: 2008-04-09 15:54
失礼いたします。
VB2005にて開発を行っています。

********************************************************************************************
'Windowsフォーム
Public Class Main
'マウスダウンイベント------------------------------
Using frmDialog As New DialogP
  If frmDialog.ShowDialog(Me) = Windows.Forms.DialogResult.Cancel Then
    Exit Sub
  End If
End Using
'---------------------------------------------------
Public Shared Sub PartsItemValidating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs)
  '数値か?など
End Sub
********************************************************************************************
'Windowsフォーム
Public Class DialogP
'Load イベント------------------------------
Me.Panel.Controls.Add(Parts.PartsText)
--------------------------------------------
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
    Me.DialogResult = Windows.Forms.DialogResult.Cancel
     Me.Close()
End Sub

********************************************************************************************
'デザイナのないクラス
Public Class Parts 
   Public Shared PartsText As New TextBox
Public Sub New()
   'PartsTextに対してプロパティ指定
   PartsText.Text = "あああ" ・・・・
AddHandler PartsLabel.Validating, AddressOf PartsItemValidating
*********************************************************************************************

1回目に frmDialog.ShowDialogする時は問題ないのですが、
2回目以降、宣言部分の
Public Shared PartsText As New TextBox をとおらないため
DialogPクラスのMe.Panel.Controls.Add(Parts.PartsText)で
"破棄されたオブジェクトにアクセスできません。"となります。

またUsingで破棄しないと、AddHandlerのイベントが追加した回数分だけ実行されてしまします。

どうすればいいのか悩んでいます・・・
そもそもPartsクラスでコントロール宣言、プロパティ設定など
しているのが間違いなのでしょうか?
何か解決法をご教授願います。 



[ メッセージ編集済み 編集者: 108 編集日時 2008-04-09 15:58 ]

[ メッセージ編集済み 編集者: 108 編集日時 2008-04-09 16:00 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-04-09 16:01
引用:

108さんの書き込み (2008-04-09 15:54) より:

そもそもPartsクラスでコントロール宣言、プロパティ設定などしているのが間違いなのでしょうか?


そう思います。 今回の問題は共有メンバになっていることから起因している問題と言えます。 共有メンバのままにしたいなら PartsLabel は Dispose および参照の解放をしてはいけません。 逆にインスタンス メンバであればコンストラクタでインスタンスの生成を強制できます。

と思ったのですが、コンストラクタで共有メンバ PartsLabel に何かしていますね。 これだと共有メンバの意味がない (整合性が取れていない) ような気がします。 何をしたいがためのコードなのでしょうか? 代替案が提示できやもしれません。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
108
常連さん
会議室デビュー日: 2007/12/12
投稿数: 45
投稿日時: 2008-04-09 16:46
ご返答ありがとうございます。

引用:

じゃんぬねっとさんの書き込み (2008-04-09 16:01) より:

何をしたいがためのコードなのでしょうか?




実際のクラスはPartsを継承したPartsText、PartsWindowなど15種ほどあります。
目下のしたいことは、
Mainから部品ボックスダイアログを表示(Show)し、PartsTextを選択するとします。
そのままMainのパネルにマウスダウンするとさらにDialogPが開きます。
(PropertyGridではない)
PartsTextクラスの情報によって
マウスダウン時の座標TextBox、フォントカラーComboBox
などのコントロールを動的に追加します。

↓DialogP
-----------------------------------------
項目名         |   値
]座標(ラベル)     |  TextBox
フォントカラー(ラベル) |  ComboBox ▼
・・・・など
  OK           キャンセル
------------------------------------------

OK押下時に、入力された情報をPartsTextクラスに保持し、
DialogPを閉じて、その情報からMainのパネル上
にTextBoxを追加します。
既に追加されたコントロール上でマウスダウンした場合はどのクラスの部品かを特定して
Mainのプロパティ表示域に値をセット
→レイアウトはボタンがないだけでほぼ同じ。

このためPartsTextクラス内で先にプロパティをセットしておけば
Mainでは追加するだけでいいかなぁと考えてしまいました。

何かいい方法はございますでしょうか・・・
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-04-09 17:21
引用:

108さんの書き込み (2008-04-09 16:46) より:

実際のクラスはPartsを継承したPartsText、PartsWindowなど15種ほどあります。
目下のしたいことは、
Mainから部品ボックスダイアログを表示(Show)し、PartsTextを選択するとします。
そのままMainのパネルにマウスダウンするとさらにDialogPが開きます。
(PropertyGridではない)
PartsTextクラスの情報によって
マウスダウン時の座標TextBox、フォントカラーComboBox
などのコントロールを動的に追加します。


外しているかもしれませんが、選択した結果を返すようなダイアログがいくつか組み合わさり、さらにダイアログごとに GUI が動的に変わるようなものを想像しました。 であれば、やはりダイアログとして表示される Form 自体で実装すべき機能 (Label や TextBox のことです) だと思います。 分離すべき場所が誤っているように思います。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
108
常連さん
会議室デビュー日: 2007/12/12
投稿数: 45
投稿日時: 2008-04-09 17:40
じゃんぬねっと様。
ご返答ありがとうございます。

ではParts***クラスでは、単純に値のみ保持するような作りで、
ダイアログFormの方でCase分岐によりそのつど
Dim XPosition as New TextBox などとしてADDすることにします。

誤っていると指摘されたことで助かりました。

ありがとうございました。
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-04-09 17:48
引用:

108さんの書き込み (2008-04-09 17:40) より:

ではParts***クラスでは、単純に値のみ保持するような作りで、


Parts*** がダイアログから返却されるようなデータであれば、それで良いと思います。 俗に言う 「データクラス」 というやつですね。 ダイアログによって扱うデータが違うのであればこのクラスを継承して利用することもできます。

ただ GUI (ダイアログ自体) の方が少々厄介ですね。 可変的な拡張でなければ、最初から Control を追加しておいて非表示にしておくとか、入力項目のみユーザーコントロールを利用してコンテナ化するとかしないとコードが無駄に膨れ上がってしまいます。 いくらダイアログであっても GUI に関するコードは極力抑えた方が保守しやすいです。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
テッテ
ベテラン
会議室デビュー日: 2008/03/16
投稿数: 91
投稿日時: 2008-04-09 18:12
補足ですが、今回でいう Parts クラスのような、フォームからフォームに受け渡すクラスに
コントロールのインスタンスを持たせてしまうと、
リソースの破棄(Dispose の呼び出し)の責任の所在があやふやになりやすいです。
また、再利用性の低い設計(そのフォームとの組み合わせでないと使えない)とも言えます。

1つの案として、こんな設計はどうでしょうか?

コード:

Public Class Parts

    Public MustOverride Function CreateControl() As Control

End Class


Public Class PartsText
               Inherits Parts

    Public Property ForeColor・・・(省略)
    Public Property Location・・・(省略)

    Public Overrides Function CreateControl() As Control

        Dim tb As New TextBox()
        tb.ForeColor = Me.ForeColor
        tb.Location = Me.Location

        Return tb

    End Function

End Class




これでダイアログが Parts のインスタンスを返せば、
MainForm 側で CreateControl を呼び出してコントロールを作成することができます。
この場合 Dispose は当然、MainForm 側で行うことになります。

UIは、じゃんぬねっとさんもおっしゃっていますが、
コードで1つずつ追加するよりは、UserControl などにまとめたほうがいいような気がします。
15個も作るのは面倒かも知れませんが、デザイナで修正できる方が、
あとから配置を少し変えたいといったときに、柔軟に対応できます。
108
常連さん
会議室デビュー日: 2007/12/12
投稿数: 45
投稿日時: 2008-04-09 18:31
引用:

じゃんぬねっとさんの書き込み (2008-04-09 17:48) より:

可変的な拡張でなければ、最初から Control を追加しておいて非表示にしておくとか、入力項目のみユーザーコントロールを利用してコンテナ化するとかしないとコードが無駄に膨れ上がってしまいます。 いくらダイアログであっても GUI に関するコードは極力抑えた方が保守しやすいです。




そうなんです。
えらい膨れ上がってしまいまして・・・
最初は、とりあえず重複しない最大部品数分追加しておいて表示・非表示を切り替えて、
Locationをずらして表現しようかなと思ったのですが、
それを試す前に、先にやっとけないかと思った結果上記のようにハマリました。

余談ですが、
XML読込みから部品追加、TreeView・DOMとの同期、最背(前)面へ移動、背(前)面へ移動、XML出力、二重起動での切り取り・貼り付け等々未知の機能が満載です・・・
先が思いやられます。
またお力をお借りするかと思いますが、宜しくお願いします。

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