C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: コンポーネントから配置されているFormを参照するには?


(過去ログ 104 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■61808 / inTopicNo.1)  コンポーネントから配置されているFormを参照するには?
  
□投稿者/ Atent (1回)-(2011/09/06(Tue) 17:47:42)

分類:[.NET 全般] 

2011/09/06(Tue) 17:56:16 編集(投稿者)

こんにちは
質問するのは初めてですので、情報が足らないかもしれませんが、その時はご指摘をお願いします
なるべく簡潔にかつ丁寧に、やったことも含め説明いたします

【質問の内容】

Windows Formsアプリケーション開発において、System.ComponentModel.Componentを継承した自作のコンポーネントを作成しております
この継承されたコンポーネント内から、このコンポーネントが配置されているフォームを参照するにはどうしたらよいでしょうか?
(とても簡単にできそうに思っていたのですが・・・)

【試したこと】

試したことは4点です

1・・・ComponentにあるメンバーからFormへ辿りつけないか?
親クラスであるComponentや、IContainerなどなどのメンバーや実際の値を見渡しましたが無理でした
Containerは親コントロール側が管理しているコンテナーの一覧ですし、フィールドとして存在するcomponentは自分自身をコンテナーとした場合のコンポーネントの管理のようでした
見落としがあったらご指摘ください

2・・・コンストラクターの引数で参照を頂けないか?
当たり前ですがコード上はコンストラクターの引数はどうとでも書けます
しかし問題はフォームデザイナー(ジェネレーター)がこのタイプの引数を持つコードを自動で吐いてくれないことです
フォームデザイナーのコードジェネレートで対応できなければコンポーネント化する意味はないですので、手動でデザイナーコードを弄るなんて方法は却下せざるをえません
コンストラクターの中の特定のオーバーロードをチョイスする方法があれば、この方法が望ましいと考えています

3・・・拡張プロバイダーを作成する方法で誤魔化してみる
CanExtendメソッドでターゲットの型をFormにすれば、唯一のFormを取得できます
この方法でなら目的自体は達成できますが、美しくありませんし、何より正しい方法があると思うのです

4・・・リフレクションを利用する
この方法は試してませんが、上記と同じで目的は達成できても正規な方法でない気がします
とはいえ、リフレクションを利用するのは初回だけですし、3よりはマシかなという気がします

コンポーネント開発をしたことがある方からすると、レベルの低いくだらない質問かもしれませんが、よろしくお願いします
引用返信 編集キー/
■61809 / inTopicNo.2)  Re[1]: コンポーネントから配置されているFormを参照するには?
□投稿者/ Hongliang (802回)-(2011/09/06(Tue) 19:01:51)
それは、デザイン時の話でしょうか? 実行時の話でしょうか?

デザイン時であれば、自身の GetService メソッドで IDesignerHost を取得し、その RootComponent を参照すれば、そのコンポーネントが張られているコンポーネントが取得できます(Form とは限りません。たとえば UserControl かもしれません)。
これは一般にデザイナで貼り付けたときに自動的に設定されるプロパティ(SynchronizingObject のような)の get アクセサで使用する処理です。

実行時の話なら、一般的な手法はないでしょう。
親を参照してしまうこと自体を見直す必要があるかもしれません。
引用返信 編集キー/
■61812 / inTopicNo.3)  Re[1]: コンポーネントから配置されているFormを参照するには?
□投稿者/ shu (976回)-(2011/09/07(Wed) 07:37:12)
No61808 (Atent さん) に返信

コンポーネントの派生クラスは作ったことがないですが
アクセス出来る方法がないならプロパティ作るかイベントで
フォーム側に処理を依存させるとかどうですか?
引用返信 編集キー/
■61814 / inTopicNo.4)  Re[2]: コンポーネントから配置されているFormを参照するには?
□投稿者/ ダッチ (2回)-(2011/09/07(Wed) 09:51:41)
ErrorProvider.ContainerControl プロパティは Form に配置したときに、
デザイナが Form の参照を設定しています。

これがどういった仕組みかがわかればいいんですが、
私も以前から疑問に思っていました。

MSDN の ErrorProvider.ContainerControl プロパティの解説にも
「通常、これはデータ連結コントロールが配置される Form です。」
と記載されているため、バインディングか何かをやっているのかもしれませんが、
詳細は、公開されているソースコードを見ればなにかわかるかもしれません。
引用返信 編集キー/
■61815 / inTopicNo.5)  Re[3]: コンポーネントから配置されているFormを参照するには?
□投稿者/ todo (165回)-(2011/09/07(Wed) 10:06:31)
コンストラクタでStackFrameを見るとか。

引用返信 編集キー/
■61825 / inTopicNo.6)  Re[2]: コンポーネントから配置されているFormを参照するには?
□投稿者/ Atent (2回)-(2011/09/07(Wed) 13:29:54)
みなさん、ご返信ありがとうございます。

No61809 (Hongliang さん) に返信
> デザイン時であれば、自身の GetService メソッドで IDesignerHost を取得し~
ありがとうございます。こういった情報も非常に勉強になります。
ところでUserControlの可能性があるとのことですが、通常コンポーネントの場合、UserControlに含まれることがあるのでしょうか?
関係ないところですが、少なくとも今回のつくりではForm側のIContainer上で管理される以外ないと思っておりましたが・・・

> 実行時の話なら、一般的な手法はないでしょう。
> 親を参照してしまうこと自体を見直す必要があるかもしれません。
これは指摘されるやも?と昨晩ベッドの上で思っていました^^
背景として拡張プロバイダーのようなものを望んではいますが(実際、最初はそのように作りました)
Targetの型がFormだけ、つまり配置されたフォーム限定なのですから、拡張プロバイダではなく一般的なコンポーネントでよいのでは?と思ったのがきっかけです
配置も理屈上、FormにされているわけですからFormなんて簡単に参照・・・あれ?みたいな状況です

これはあくまで例ですが、FormのControlAddedやRemovedを監視したいといった場合、拡張プロバイダーで作れば簡単です
なぜならextendeeから参照が取れるわけですから、ただ同じようなことをコンポーネントでやろうとすると今回のようなFormの参照の問題が出てきます
拡張プロバイダのままの方が良かったのかもしれませんね・・・

No61812 (shu さん) に返信
> コンポーネントの派生クラスは作ったことがないですが
> アクセス出来る方法がないならプロパティ作るかイベントで
> フォーム側に処理を依存させるとかどうですか?
まずプロパティについてですが
その代替案が出てくることを予想して、最初の「試したこと」の書き込みをしました
繰り返しになりますが「フォームデザイナーのコードジェネレートで対応できなければコンポーネント化する意味はない」という理由があります

次にイベントですが、すでにフォームへのイベント提供を実装していますが・・・
ただその提供元としてのタイミングや情報でフォームの参照が元より必要となる機能の提供を実装しようとしています
ちょっとこのあたりの背景が説明不足で申し訳ないです・・・

No61814 (ダッチ さん) に返信
> ErrorProvider.ContainerControl プロパティは Form に配置したときに、
> デザイナが Form の参照を設定しています。
>
> これがどういった仕組みかがわかればいいんですが、
> 私も以前から疑問に思っていました。
>
> MSDN の ErrorProvider.ContainerControl プロパティの解説にも
> 「通常、これはデータ連結コントロールが配置される Form です。」
> と記載されているため、バインディングか何かをやっているのかもしれませんが、
> 詳細は、公開されているソースコードを見ればなにかわかるかもしれません。
ErrorProviderは拡張プロバイダーですから、すごい雑な話をしますと(極論です!)
extendeeで得たControlの参照からFindFormメソッドもしくはParentなりで参照は取れるかと思います
ErrorProviderが内部的に上記なことをやっていると名言するわけではありませんが、拡張プロバイダーなら方法はいろいろあるかと思います

No61815 (todo さん) に返信
> コンストラクタでStackFrameを見るとか。
なるほど、そういう方法もありますね

うーむ、やはり一般的な手法はなさそうなのでしょうか・・・
3rdパーティ製品でフォームの状況からにイベントでタイミングと情報を提供をしている類のコンポーネントがあったりしますが、それを参考にできるなら(法的に問題がないなら)参考にした方がいいかもしれませんね

みなさん、ありがとうございました
私ももう少し調べてみます
引用返信 編集キー/
■62117 / inTopicNo.7)  Re[3]: コンポーネントから配置されているFormを参照するには?
□投稿者/ じゃんぬねっと (82回)-(2011/09/21(Wed) 21:30:10)
じゃんぬねっと さんの Web サイト
No61825 (Atent さん) に返信
> みなさん、ありがとうございました
> 私ももう少し調べてみます

あれれ?? 何か勘違いされているように思えますが...
配置した時にデザイナで設定されていれば (Code Generator が InitializeComponent で Form の参照を渡すコードを吐いてくれれば) 良いのですよね?
であれば Hongliang さんの回答は実に的を射た回答ですよ。

まず以下のように ContainerControl プロパティを定義してください。
・get アクセサにて ContainerControl の実体 (プロパティ変数) にインスタンスがある場合はそれをそのまま返す
・DesignMode でない場合、もしくは IDesignerHost が取得できない場合もプロパティ変数を返す
・ContainerControl が設定されておらず、DesignMode かつ IDesignerHost が取得できる場合は、RootComponent を ContainerControl 型で try cast する
・キャストに成功した (null or Nothing でない) 場合は ContainerControl に設定する

> ところでUserControlの可能性があるとのことですが、通常コンポーネントの場合、UserControlに含まれることがあるのでしょうか?

普通にあります。
UserControl に限らず、画面デザイナで表示可能な 'コンテナ' は、コンポーネントを含ませることが可能です。
ためしにコンテナ (たとえば UserControl) を継承して、コンポーネントを配置してみてください。

そういう意味では、Atent さんの場合は ContainerControl が取得できたら、FindForm メソッドを呼び出し、なおかつ Form の参照が取得できない場合は例外を発生させるべきでしょうね。
UserControl に追加した時にデザイナで処理を中断することができます。
その場合 ContainerControl という名前はやめて、Form だとわかる名前にした方が良いでしょう。
もちろん型も System.Windows.Forms.Form とします。

もし Form で固定したいなら上の方法に加えて BrowsableAttribute を false にしてデザイナから見えなくしても良いでしょう。
変更できるようにしたいのであれば、IsInitialized のようなものを作り、CodeGenerator が指定した初期値から意図的に変更したかどうかを判断する仕組みを入れれば良いです。
まあ、今回の目的ならば固定で良いでしょう。
(固定で良い == null か親 Form しか取り得る値がないですので、Enabled プロパティのみで制御してもよろしいかと思います)

これで ErrorProvider と同じように CodeGenerator で ContainerControl = this (Me) というコードが吐き出されるようになります。
ただ、これが正規の方法 (FCL の他のコンポーネントと同等の方法) かどうかはわかりません (おいw)。
ですので、ダッチさんのアドバイスどおり、ErrorProvider の中身を見た方がいいかもしれません。

# ダッチさんのアドバイスも解決に繋がるものですよ?
引用返信 編集キー/
■62190 / inTopicNo.8)  Re[4]: コンポーネントから配置されているFormを参照するには?
□投稿者/ ダッチ (4回)-(2011/09/27(Tue) 10:55:55)
Hongliang さんと じゃんぬねっと さんの回答を元にして試してみました。

コードは VB でもいいですよね。

Public Class CustomComponent

    Private _ContainerControl As ContainerControl
    Public Property ContainerControl() As ContainerControl
        Get
            If _ContainerControl IsNot Nothing Then
                Return _ContainerControl
            End If

            If Me.DesignMode = False Then
                Return Nothing
            End If

            Dim dh As IDesignerHost = Me.GetService(GetType(IDesignerHost))
            If dh Is Nothing Then
                Return Nothing
            End If

            _ContainerControl = TryCast(dh.RootComponent, ContainerControl)
            Return _ContainerControl
        End Get
        Set(ByVal value As ContainerControl)
            _ContainerControl = value
        End Set
    End Property

End Class

これで、コンポーネントを Form なり UserControl なりに貼り付けたときに ContainerControl プロパティにコンテナのインスタンスを設定するコードが出力されていました。
こんなに簡単にできるとは驚きです。

一応 ErrorProvider の ContainerControl プロパティがどういう実装になっているかソースを確認したところ、
Site プロパティをオーバーライドし、そこで ContainerControl プロパティを設定するようになっていました。

こんな感じです (あくまでもソースと同様のコードです。引用ではありません) 。

Public Class CustomComponent

    Private _ContainerControl As ContainerControl
    Public Property ContainerControl() As ContainerControl
        Get
            Return _ContainerControl
        End Get
        Set(ByVal value As ContainerControl)
            _ContainerControl = value
        End Set
    End Property

    Public Overrides Property Site() As ISite
        Get
            Return MyBase.Site
        End Get
        Set(ByVal value As ISite)
            MyBase.Site = value

            If value Is Nothing Then
                Return
            End If

            Dim dh As IDesignerHost = Me.GetService(GetType(IDesignerHost))
            If dh Is Nothing Then
                Return
            End If

            _ContainerControl = TryCast(dh.RootComponent, ContainerControl)
        End Set
    End Property

End Class

ContainerControl プロパティの Get で設定するのがいいのか
Site プロパティの Set で設定するのがいいのか
どちらが適切かは私には判断できませんが、
このようにして ContainerControl プロパティに設定することができました。

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -