- PR -

CodeDomの逆シリアル化

投稿者投稿内容
JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-09-27 21:38
お世話になります。
VB.net,Framework1.1で開発を行っています。

現在、自作のコントロールを作成しています。
このコントロールには自作の型のプロパティがあります。
これをCodeDomを用いてInitializeComponentに記述しようとしました。

調べているとCodeDomSerializerというクラスを継承して、これを自作コントロールの
シリアライザとすれば可能とわかりました。

いろいろ試していると、SerializeとDeserializeというメソッドをオーバーライドする事で制御するとこまでは行き着きました。

まず、Serializeメソッドについて試してみると、悪戦苦闘の末にヴィジュアルデザイナでの変更がInitializeComponentに記述されるようになりました。
これでシリアル化は成功したと思います。

しかし、ここからが問題なのですが、ソースコードでシリアル化の成功を確認したのちにビルドをしてみると「タスク一覧」にエラーが発生してしまいます。
内容は「変数 'temp' は宣言されていないか、または割り当てられていません。」というものです。

この変数 'temp' とはSerializeメソッドで作成した
Dim temp as New SampleClass1 'SampleClass1は適当な自作クラス
というステートメントです。
これを自作コントロールのプロパティに代入するところでエラーが発生しているようです。
MyControl1.MyProperty = temp 'MyControl1は自作コントロールのインスタンス、MyPropertyは自作コントロールのプロパティの一つでSampleClass1型

さらに調べてみると、DeserializeメソッドでInitializeComponetから逆シリアル化を行おうとする際に、逆シリアル化の対象となるコードは「MyControl1.〜」に限定されている点に問題があるようです。
つまり、「変数’temp’の宣言部が逆シリアル化の際の元コードに含まれない為、未定義の変数’temp’をMyPropertyプロパティへ代入してますよ」というエラーだと判断します。

ここまで来ればも解決しそうだと思ったのですが、完全に行き詰ってしまいました。
原因はわかっても解決のしようがないのです。
そもそも、シリアル化がすんなり出来たのは、ネットを探せばサンプルがたくさんあるからです。
しかし、逆シリアル化に関してはまともな情報がありません。

MSDNでCodeDomやSerializer関連を調べても、あるのはコードの自動生成ばかりです。
自動生成したコードを元に戻すようなサンプルがありません。

だらだらと長くなってしまいましたが、どなたかこの件に関して解決の情報をいただけないでしょうか?
どうぞ宜しくお願いいたします。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-09-28 13:49
JUNJUNさん、こんにちは。

なんとなく、「カスタムコントロールに独自の型のプロパティを持たせて、フォームデザイナのプロパティエディタから指定できるようにしたい」んじゃないかな、と思ったんですが、違います?

もしもその通りなら、こっちのスレッドが参考になるかも知れません。

#もしかして、独自の型をクラスじゃなくて構造体にすればOK?
#あるいは現象を再現する最小限のコードを提示してもらった方が良いかも知れませんね…。
JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-09-28 13:55
よりわかりやすい説明が思いついたので追記します。

自作ユーザーコントロール(TextBoxを継承)を定義します。
Public Class MyTextBox
Inherits System.Windows.Forms.TextBox
End Class

このMyTextBoxはTextBoxを継承を継承しているのでTextプロパティをもっています。

新しく作成したWindowsFormでForm1上にMyTextBoxを貼り付けます。(名前はMyTextBox1)
ヴィジュアルデザイナでMyTextBox1を選択すれば、プロパティウインドウにはTextプロパティが表示されます。
このプロパティに「ABC」と入力します。

ソースコードに切り替えて、InitializeComponent内を検索すると以下のコードがあります。
MyTextBox1.Text = "ABC"

ここで、以下のように修正した後にヴィジュアルデザイナを表示すると問題の
「変数 '***' は宣言されていないか、または割り当てられていません。」
と表示されるのです。
Dim s As String = "ABC"
MyTextBox1.Text = s

どう考えてもMyTextBox(元をたどれはTextBox。さらにはControl...)のデフォルトのシリアライザではこの様なコードの逆シリアル化はできないようです。

現在もネットなどで調査しているのですが進展なしです。

ご助言宜しくお願いします。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-09-28 20:49
JUNJUNさん、こんばんは。

引用:

Dim s As String = "ABC"
MyTextBox1.Text = s


これ、別にカスタムコントロールでなくてもエラーになりますね。
しかし、こういう書き方↓をするとエラーになりません。

コード:
Me.TextBox1.Text = New System.Text.StringBuilder("TextBox1").ToString()



テキストボックスのフォントサイズとかをデフォルト以外に変更して、それによってどのようなコードが生成されるかを見てみると、参考になるのではないでしょうか。
JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-09-30 09:20
きくちゃん様、レスありがとうございます。

説明下手で本当にすみません。

私がしたい事は、CodeDomを使用してInitializeComponentへのシリアル化と逆シリアル化を行う事です。

通常はMyTextBox1のTextプロパティに値をセットしたければヴィジュアルデザイナで"ABC"と入力します。そうすれば当然
MyTextBox1.Text = "ABC"
というコードも自動生成されます。

ですが、これをあえて
Dim s As String = "ABC"
MyTextBox1.Text = s
という形でコード生成したいのです。

これはCodeDomを勉強したいという唯一の理由からです。

そこで問題となるのが上記のようなコードの自動生成には成功したのですが、
デザイナへ戻ると逆シリアル化で失敗するという点です。

これに関する解決策を御教授いただけると幸いです。
どうぞ宜しくお願いいたします。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-09-30 13:23
JUNJUNさん、こんにちは。

引用:

私がしたい事は、CodeDomを使用してInitializeComponentへのシリアル化と逆シリアル化を行う事です。


いやまあ、それは良いんですけど。
本来の目的、というか、それによって達成したいことは何なんだろうか、と思ったわけです。

引用:

これはCodeDomを勉強したいという唯一の理由からです。


そういう事でしたら…。

CodeDomSerializer の Deserialize メソッドは、フォームの InitializeComponent メソッドに記述されたステートメントのコレクションを引数として受け取り、それを解析してコンポーネントの各プロパティへ値をセットします。
しかし、このステートメントのコレクションには該当するコンポーネントに直接関係のあるもの、恐らくは代入式の左側にコンポーネントへの参照が存在するものだけが含められているようです。

つまり「Dim s As String = "ABC"」という行は MyTextBox1 とは無関係であると判断され、Deserialize メソッドに渡される引数からは除外される、という事のようです。
従って、Deserialize をオーバーライドしても、結局どうしようもないような気がします。

【追記】
ちなみに上記の発言、以下のコードで確認した結果が根拠になってます。

コード:

Public Overrides Function Deserialize(ByVal manager As _
IDesignerSerializationManager, ByVal codeObject As Object) As Object
Dim State As CodeStatement

For Each State In CType(codeObject, CodeStatementCollection)
manager.ReportError("行:" & State.LinePragma.LineNumber.ToString())
Next

Dim baseClassSerializer As CodeDomSerializer = _
CType(manager.GetSerializer(GetType(TextBox), _
GetType(CodeDomSerializer)), CodeDomSerializer)
Return baseClassSerializer.Deserialize(manager, codeObject)
End Function



で、最初の投稿にあった、

コード:

Dim temp as New SampleClass1
MyControl1.MyProperty = temp


は、

コード:

MyControl1.MyProperty = New SampleClass1("aaaa", 1, "bbb", False)


みたいな感じで、一気に子プロパティをセット出来るようにしたら良いんじゃないですか? というのが前回の投稿です。


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

引用:

きくちゃんの書き込み (2005-09-30 13:23) より:
つまり「Dim s As String = "ABC"」という行は MyTextBox1 とは無関係であると判断され、Deserialize メソッドに渡される引数からは除外される、という事のようです。


って、済みません、良く見たらJUNJUNさんの最初の投稿にある以下の文章↓、実は同じ事を言ってたんですね…orz。
最初に読んだときは意味が理解できませんでした。申し訳ない。

引用:

さらに調べてみると、DeserializeメソッドでInitializeComponetから逆シリアル化を行おうとする際に、逆シリアル化の対象となるコードは「MyControl1.〜」に限定されている点に問題があるようです。


しかし、これは「問題がある」んじゃなくて、そういう「仕様」なんじゃないでしょうか。まあ、それについて明記してある文書とかは見つけてませんけど、納得出来る仕様じゃありません?

もしかすると、codeObject 引数以外のところからローカル変数の宣言部にアクセスする手段もあるかも知れませんが、私には判りません。

ちなみに、JUNJUNさんが目指しているような初期化コードを、実際に吐くコントロールってあるんですか?
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-09-30 21:13
 たとえば XML ファイルで、

<変数名>実態</変数名>
<プロパティ名>変数名</プロパティ名>

としても、プロパティ名は「変数名」という値が入るようなものではないでしょうか。


 ってか、手段が目的になっているような気がする。
 カスタムクラスのプロパティデザイナを作る方法は、チュートリアルがありますよね。そこから、なぜ CodeDOM に飛んだんだろう?


 また、複数のコントロールを配置したとき、一時的な変数が一意であることは、どうやって保証するのでしょう?
___________________________________________________________________
□ written by JittaJitta on 2005/09/292005/09/30
じったのノート
□ Microsoft MVP :Visual Developer ASP/ASP.NET Oct.2004-Sept.2005
_________________

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