none
静的に標準ライブラリのコントロールをコントロール配列として提供するには RRS feed

  • 質問

  • いつもお世話になっております。

     

    実現したい機能は独自パネルに貼り付けたコントロールを配列のようにアクセスできることです。

     

    Controlが貼り付けられたときに配列化する機能をCodeDomSerializerで実現できないかと色々と試してみましたが情報も少なくできませんでした。

    別の方法がないのかと検討中です。

     

    コントロール配列

    UserPanel panel1 = new UserPanel();

    Button btn1 = new Button();

    Button btn2 = new Button();

    panel1.Controls.AddRange(new Control[] { btn1, btn2 });

     

    // 以下正常に通ることが望み

    Debug.Assert(btn1[0] == btn1, "オブジェクトは別物です。");

    Debug.Assert(btn1[1] == btn2, "オブジェクトは別物です。");

    Debug.Assert(btn2[0] == btn1, "オブジェクトは別物です。");

    Debug.Assert(btn2[1] == btn2, "オブジェクトは別物です。");

     

     

     

    以下のURLに参考になりそうなサンプルがありましたが、コンポーネントを呼び出す形なのと、Controlとしてコレクションに追加しているため、キャストが必要なのが不満要素です。

    GotDotNet User Sample ControlArray

     

     

    拡張プロパティなる拡張インデクサを提供でき、かつそれらを提供先コントロールで扱えたらよいんですが、提供元でしかプロパティを呼び出せませんでした。

    プロパティでもよいかとも思いましたが、キャストが必要だと好ましくありません

     

     

    実現は可能なのでしょうか?

    泥臭くいコードになってもよいですが、キャストが必要になるような書き方はあまりしたくありませんが、最終手段で考えています。

     

     

    実現可能な方法をご存じでしたら、教えていただけると幸いです。

    よろしくお願いします。

     

    2007年9月14日 3:23

すべての返信

  • レゲレゲさんこんにちは

    どこまで汎用化したいかにもよりますが、ジェネリックは使えませんか。キャストしたくなくて汎用化したいのであればジェネリックを使うことを考えても良いのではと思います。

    2007年9月14日 5:08
  • レスありがとうございます。

     

    具体的にどのように使えば可能ですか?

     

    一応自分の中で考えられる事は考えてはいるんですが、結局はキャストが必要になるんじゃないかと思いました。

    理想的な動作はVSのデザイナ上で標準コントロールや自作コントロールを複数置いた場合に、配列化の機能を追加したいということです。

    button1という名前で貼り付けたボタンが作られた場合、その名前で配列みたいに扱いたいのですが、自分の中ではデザイン時のシリアル化を制御する他ないと行き当たりました。

     

    既存の物を拡張したコントロールを作るだけなら難しくはありません。。

    ですが、標準コントロールをそのまま扱えるのが望ましいとの要望がありまして・・・・

    どんなコードになってもいいので実現できれば良いと思っています。

     

    ふと思ったのですがキャスト以前の問題ですよね・・・

    既存のクラスは変更しないでインデクサを追加するって不可能じゃないかと

    javascriptみたいなプロトタイプチェーンが可能でしたら好き放題できたんですが orz

     

     

    可能な手段があるようでした何でもよいので教えていただけると助かります。

    よろしくお願いします。
    2007年9月14日 10:55
  • 標準コントロールといっても、コンテナ側は独自パネルと書いておられるから、カスタムコントロールを作っても良いのでしょう?

    レゲレゲさんのやりたいことを把握できていないかもわかりませんが、独自パネルならシリアライズまでいじらなくても良いのではないかと思います。javascriptのプロトタイプチェーンは継承に相当するものだと思っていました。

     

    2007年9月14日 14:07
  • 他の方も書かれていますが、文章からは要求されていることが曖昧でわかりにくいので、もうすこし文章量や具体例を増やされるとよいのではないかと思います。
    最初のコード例にしても btn1 と btn2 という変数が配列であるのか非配列であるのかわからずコンパイルエラーになるようにしかみえません。
     

    CodeDomSerliazer ですが、CodeDomSerializer はコンポーネントをコードに保存する際の動作をカスタマイズできるものですが、保存されるコンポーネントの内容となるコードを追加/変更ができますが、保存先であるストアとなる 変数 をカスタマイズする用途に使えたかは記憶にありません。
     
    Form や UserControl の上のコンポーネントを具体的な型を指定して、
     
    Code Snippet
    Button btn1 = this.buttons[1]; // this = Form
    TextBox textbox1 = this.textboxes[2]; // this = Form

     

     

    のような形でアクセスするためには、buttons や textboxes のインデクサが自身を保持する型を知らなければならないので、Visual Studio 2005 では IDE が ParameterizedType を理解できないので、ちょっと難しかったと思います。
     
    Code Snippet
    Button btn1 = this.array.Get<Button>(1); // this = Form
     
    TextBox textbox1;
    this.array.Get(2, out textbox1); // this = Form

     

     

    みたいなかんじで取得時に型を指定するような Generics の使い方であれば、コンポーネントとして構築するのは難しくはないと思います。
    2007年9月15日 23:03
  • レスありがとうございます。

     

    理解しづらい説明で済みませんでした。

    説明下手でして、うまく表現できません orz

     

     

    > もうすこし文章量や具体例を増やされるとよいのではないかと思います。

    具体的な例を挙げますとVB6のコントロール配列と同等以上の物が欲しいです。

     

     

    >CodeDomSerliazer ですが、CodeDomSerializer はコンポーネントをコードに保存する際の動作をカスタマイズできるものですが、保存されるコンポーネントの内容となるコードを追加/変更ができますが、保存先であるストアとなる 変数 をカスタマイズする用途に使えたかは記憶にありません。

     

    その親元のシリアライズでもよいのでカスタマイズ出来れば実現できそうなんですが、取得の仕方がまだわかっておりません。

    他に情報もないため SharpDevelop のソースコードを見ながら簡易的なフォームデザイナを作ってみようかとも思っています。

     

     

    説明が下手で大変申し訳ないです。

    どうやったら説明がうまくなるのか悩みの種だったり・・・

     

    以上です。

    分かりづらい文ですが、どうかよろしくお願いします。

    2007年9月18日 0:53
  • 三輪の牛さん、レスありがとうございます。

     

    カスタムコントロールはPanelを継承させたものをイメージしておりまして、そのPanel上に置かれたコントロールを、コード上で配列変数として提供させたいのですが、分かりづらい説明で本当に申し訳ないです。。 orz

     

     

    > javascriptのプロトタイプチェーンは継承に相当するものだと思っていました。

    私は Mix-in に近いイメージを抱いています。

     

    2007年9月18日 1:05
  • こんなのじゃ駄目かなぁ……。

     

    Code Snippet

    // 返値IEnumerable<T>がいやなら適当にList<T>に変換するなり配列に変換するなり

    public IEnumerable<T> GetControls<T>() where T : Control {
        foreach (Control c in this.Controls) {
            T t = c as T;
            if (t != null) yield return t;
        }
    }

     

    2007年9月18日 1:41
  • レスありがとうございます。

     

    済みませんでした、言葉足らずですね・・・

     

    例えばフォームデザイナ上でボタンを貼り付けた時に以下のコードが生成されると思います。

    サンプル1

    #region Windows フォーム デザイナで生成されたコード

    /// 
    /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディタで変更しないでください。
    /// 
    private void InitializeComponent()
    {
        this.button1 = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // button1
        // 
        this.button1.Location = new System.Drawing.Point(15226);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(8765);
        this.button1.TabIndex = 0;
        this.button1.Text = "button1";
        this.button1.UseVisualStyleBackColor = true;
        // 
        // Form3
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(292273);
        this.Controls.Add(this.button1);
        this.Name = "Form3";
        this.Text = "Form3";
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.Button button1;

     

     

     

    理想としているコードは以下のようになることです。

    サンプル2

    #region Windows フォーム デザイナで生成されたコード

    /// <summary>
    /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディタで変更しないでください。
    /// </summary>
    private void InitializeComponent()
    {
        this.button1 = new System.Windows.Forms.Button[1];
        this.button1[0] = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // button1[0]
        // 
        this.button1[0].Location = new System.Drawing.Point(15226);
        this.button1[0].Name = "button1";
        this.button1[0].Size = new System.Drawing.Size(8765);
        this.button1[0].TabIndex = 0;
        this.button1[0].Text = "button1";
        this.button1[0].UseVisualStyleBackColor = true;
        // 
        // Form3
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(292273);
        this.Controls.Add(this.button1[0]);
        this.Name = "Form3";
        this.Text = "Form3";
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.Button[] button1;

     

     

     

    サンプル2のコードでは

    button1[0].Text = "サンプル2";

    のように記述出来ると思います。

     

    シリアル化以外で上記のような呼び出し方が可能なのでしょうか?

    色々調べてみても、最終的にシリアル化に行き着くのですが・・・・

     

    2007年9月18日 2:05
  • CodeDomSerializerにこだわるよりも、サンプル1の状態から2の状態に変換するVSマクロを作ってしまったほうが簡単なのでは?
    デザイナを閉じたらマクロ実行するだけで複雑な変換だろうと思いのままですが。



    2007年9月18日 4:00
  • 自分だけが利用するなら、コントロール配列にこだわらないのですが、上の人の指示のため出来る限りの手を打とうと思いまして・・・

     

    2007年9月18日 4:12
  •  レゲレゲ さんからの引用

    > もうすこし文章量や具体例を増やされるとよいのではないかと思います。

    具体的な例を挙げますとVB6のコントロール配列と同等以上の物が欲しいです。

    例としては具体的ですが、説明としては抽象的すぎる書き方の悪例にならっていそうです(苦笑
     
    「VB6のコントロール配列」について知らない人からすれば、どのようなものであるかサッパリ理解できませんし、逆に VB6 のコントロール配列を知っている場合であっても、その機能性のうち何が重要であったり必須であったりするかといった認識が異なる場合がありますので、このような書き方は絶対にしないほうがいいです。

     

     

     レゲレゲ さんからの引用

    その親元のシリアライズでもよいのでカスタマイズ出来れば実現できそうなんですが、取得の仕方がまだわかっておりません。

    他に情報もないため SharpDevelop のソースコードを見ながら簡易的なフォームデザイナを作ってみようかとも思っています。

    「親元のうんぬん」というのがよくわかりませんが、前述したように、CodeDomSerializer で修正できる部分は、
     
    Code Snippet
    this.button1.Property = InitializeValue
    this.button1.Method();

     

     

    という部分の、背景を黄色くした部分です。背景を紫色にした部分は CodeDomSerializer からは修正できません。Visual Studio 2003 から 2005 への変更で、紫色の部分をローカル変数にできるようになっていたりしますが、それらはシリアライザではなくデザイナ側から提供される機能です。
    また、上記のようなコード部分のうち、背景を黄色くした部分のコードは、button1 のクラス(たとえば System.Windows.Forms.Button クラス)のシリアライザが生成する部分ですので、Panel の派生クラスではなく、Panel に配置されうるすべてのコントロールの派生クラスが対象になってしまいますので、現実的な解決策ではないでしょう。
     
    前の投稿にも記載しましたが、ボタンを配列にするとしたら、ボタンはボタンで配置しておいて、追加で「配列のように見せるコンポーネント」を別途用意して、
     
    Code Snippet
    private 自作コンポーネントButtons buttons;
     
    private void InitializeComponent()
    {
        // GenerateMember プロパティを false にしてローカル変数として生成させる
        System.Windows.Forms.Button button1 = new System.Windows.Forms.Button();
     
        // button1
        button1.xxx = ...;
     
        // button2
        button1.xxx = ...;
     
        // buttons
        this.buttons.Initialize(new System.Windows.Forms.Button[]
        { button1, button2, button3, button4 });
    }
     
    // たとえば button3 の Text プロパティの変更
    this.buttons[2].Text = "Button3";

     

    みたいなかんじにするのが、実装的に簡単で、コーディング上も配列であるかのように扱えて便利だと思います。

    2007年9月19日 6:35
  • レスありがとうございます。

     

     

    >例としては具体的ですが、説明としては抽象的すぎる書き方の悪例にならっていそうです(苦笑

     
    >「VB6のコントロール配列」について知らない人からすれば、どのようなものであるかサッパリ理解できませんし、逆に VB6 のコントロール配列を知っている場合であっても、その機能性のうち何が重要であったり必須であったりするかといった認識が異なる場合がありますので、このような書き方は絶対にしないほうがいいです。

     

     

    済みません説明不足ですね orz

    何が重要かを考えますと、同じコントロールが存在する場合に、配列へとコードが書き換わることが望ましいです。

     

    通常コピーやコントロールを複数置いた場合、自動的に振られる名前の連番になると思います。

    Code Snippet

    Button button1 = new Button();

    Button button2 = new Button();

     

    望んでいるコードは以下のようになることです。

    Code Snippet

    Button[] button = new Button[] { new Button(), new Button() };

     

    各標準コントロールを継承するなりすれば可能ですが、継承せずにそのようなコードをデザイン上から操作したときに生成できないものかと試行錯誤しているのですが、シリアル化処理を取得して、生成されるコードを書き換えるくらいしか思いつきませんでした。

     

     

    > 「親元のうんぬん」というのがよくわかりませんが、前述したように、CodeDomSerializer で修正できる部分は、

    親元だけでは説明になってませんね・・・・

     

    親元は以下のメソッドのことを言いたかったのですが、どれが親元か分からなかったため、そのように書いてしまいました。

    Code Snippet
    CodeDomProvider#GenerateCodeFromMember(CodeTypeMember member, TextWriter writer, CodeGeneratorOptions options);

     

    引数に指定されている「CodeTypeMember」を取得&編集できれば、コントロール配列のコードを出力できるんじゃないかと推測してます。

     

     

    > 前の投稿にも記載しましたが、ボタンを配列にするとしたら、ボタンはボタンで配置しておいて、追加で「配列のように見せるコンポーネント」を別途用意して、

     

    コードまで書いてくださってありがとうございます!

    「継承しないでも配列として扱えるように」と言われていまして、その方法で進められません。。

    CodeDomなどで動的にソースを生成するなら別なんですが、元あるコントロールを継承しない方法で模索しています。

    2007年9月20日 4:07
  • とりあえずアドインでやることになりました。

     

    私が実現させたい手法はコード出力関係でのコントロール配列なので、色々試した結果実現出来た場合、それの実現方法を書いて解決済みにしたいと思います。

    それまでは未解決のまま置いておきます。

     

    多くのレスを頂き大変感謝いたします。

    ありがとうございました。

    2007年9月21日 3:55