none
RectangleShapeを配置したフォームの継承 RRS feed

  • 質問

  • いつもお世話になっております。
    C#4.0でWindowsFormアプリケーションを開発しています。

    フォームの境界線をオリジナルで実装してほしいと
    いう要件があり、RectangleShapeの利用を検討しております。

    具体的な要件としては、次の通りです。
    ・フォームのタイトルバーを非表示にしたい。
    ・境界線は太い線で表示してほしい。
    ・境界線の色を指定したい。

    この要件を満たすためには、FormのFormBorderStyleをNoneにして
    RectangleShapeを配置するのが良いだろうと検討しました。
    本対応をするフォームは複数あり、今後も増える予定がありましたので、
    本対応をしたフォームをスーパークラスとして用意して、
    そのフォームを継承して、それぞれを実装しようと考えました。

    しかし、継承すると継承先のフォームのデザイナで
    ファイルを開くことができません。
    以下の警告が表示されてしまいます。

    -----------------------------------------------------------------------------------
    サービス System.Windows.Forms.Design.IEventHandlerService は既にサービス コンテナーに存在します。
    パラメーター名: serviceType
    -----------------------------------------------------------------------------------

    警告なのでプログラムは実行でき、
    当該フォームも、実行プログラムからは起動できました。

    何か手順に問題がありますでしょうか?
    RectangleShapeを配置したフォームは継承してはいけないというのが
    一般的な規約だったりしますでしょうか?

    要件が満たされれば、別な対応策でも良いと考えておりますので、
    何か良い方法がございましたら、ご教示頂けますと幸いでございます。

    以上、よろしくお願い致します。
    2012年10月29日 7:36

回答

  • こんなふうにすれば回避できました

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Controls.Remove(shapeContainer1);
        }
    
        protected override void  OnLoad(EventArgs e)
        {
            this.Controls.Add(shapeContainer1);
            base.OnLoad(e);
        }
    
        //Shapeを貼ったときに自動で作成されるShapeContainer
        private Microsoft.VisualBasic.PowerPacks.ShapeContainer shapeContainer1;
    
        //このフォームを継承したフォームにShapeを貼りたい時は、
        //最初だけは一時的にBaseFormにあるShapeContainerを消す必要があるみたい
        //継承したフォームにShapeを貼らないならこのプロパティは不要
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual bool IsShowBaseShape
        {
            get
            {
                return _IsShowBaseShape;
            }
            set
            {
                _IsShowBaseShape = value;
                if (value)
                {
                    this.Controls.Add(shapeContainer1);
                    this.Controls.SetChildIndex(shapeContainer1, 0);
                }
                else
                {
                    this.Controls.Remove(shapeContainer1);
                }
            }
        }
        private bool _IsShowBaseShape = true;
    }

    継承されたフォームがデザイナ用に生成されるときに、ShapeContainerが存在するとエラーを吐くようです。
    なので、生成された直後にShapeContainerが存在していないように、一時的にはずしています。

    また、継承したフォームにShapeを貼ろうとしても、すでにShapeContainerが存在していると*.Designer.csに不完全なコードが生成されてしまいます。
    この場合も一時的にShapeContainerをはずしてからShapeを貼ると、正常にコード(新しいShapeContainer)が生成されます。

    ShapeContainerをpublicやprotectedにするとこの方法でも回避はできないので、privateにしておく必要があります。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク コンドル 2012年11月5日 1:35
    2012年11月2日 11:58

すべての返信

  • そのスーパークラスはどのようなコードなのでしょうか?
    2012年10月29日 8:24
  • 佐祐理様

    デザイナ側のプログラムしか変更されていないので、
    そちらを載せますね。
    70行くらいになっているので、申し訳ないのですが・・・

    行っていることとしては、
    RectangleShapeをFormに合わせて配置していることと、
    FormのFormBorderStyleを変更していることくらいです。

    namespace WindowsFormsApplication73
    {
        partial class Form1
        {
            /// <summary>
            /// 必要なデザイナー変数です。
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary>
            /// 使用中のリソースをすべてクリーンアップします。
            /// </summary>
            /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Windows フォーム デザイナーで生成されたコード
    
            /// <summary>
            /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
            /// コード エディターで変更しないでください。
            /// </summary>
            private void InitializeComponent()
            {
                this.shapeContainer1 = new Microsoft.VisualBasic.PowerPacks.ShapeContainer();
                this.rectangleShape1 = new Microsoft.VisualBasic.PowerPacks.RectangleShape();
                this.SuspendLayout();
                // 
                // shapeContainer1
                // 
                this.shapeContainer1.Location = new System.Drawing.Point(0, 0);
                this.shapeContainer1.Margin = new System.Windows.Forms.Padding(0);
                this.shapeContainer1.Name = "shapeContainer1";
                this.shapeContainer1.Shapes.AddRange(new Microsoft.VisualBasic.PowerPacks.Shape[] {
                this.rectangleShape1});
                this.shapeContainer1.Size = new System.Drawing.Size(284, 262);
                this.shapeContainer1.TabIndex = 0;
                this.shapeContainer1.TabStop = false;
                // 
                // rectangleShape1
                // 
                this.rectangleShape1.BorderWidth = 5;
                this.rectangleShape1.Location = new System.Drawing.Point(0, 0);
                this.rectangleShape1.Name = "rectangleShape1";
                this.rectangleShape1.Size = new System.Drawing.Size(283, 261);
                // 
                // Form1
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(284, 262);
                this.Controls.Add(this.shapeContainer1);
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                this.Name = "Form1";
                this.Text = "Form1";
                this.ResumeLayout(false);
    
            }
    
            #endregion
    
            private Microsoft.VisualBasic.PowerPacks.ShapeContainer shapeContainer1;
            private Microsoft.VisualBasic.PowerPacks.RectangleShape rectangleShape1;
        }
    }
    

    これをForm2で継承しています。
    プログラムは以下の通りです。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication73
    {
        public partial class Form2 : Form1
        {
            public Form2()
            {
                InitializeComponent();
            }
        }
    }
    

    既定で作成されているプログラムからは、
    「Form」のところをForm1で継承されるよう「Form1」と変更しただけです。

    何かお分かりになりますでしょうか。
    ぜひお願い致します。

    2012年10月29日 9:15
  • ありがとうございました。
    解決に至りませんので、クローズ致します。
    ご検討頂いた方々、ありがとうございました。
    別な方法がないか検討してみます。

    • 回答としてマーク コンドル 2012年11月2日 0:38
    • 回答としてマークされていない コンドル 2012年11月5日 1:35
    2012年11月2日 0:38
  • VB Power Pack は使用していないのですが、エラーメッセージからすると、RectangleShape をデザイナで配置すると、ShapeContainer というのが自動で配置されるのでしょうか?

    それで、ShapeContainer の可視性がデフォルトの private のままになっていて、 protected に変更しないとダメって話だったりしませんかね。(こういう類の自動的に1つだけ追加されるコンテナ系コンポーネントは、派生後も可視でないと、コンポーネントを追加しようとして二重登録されてしまうため)

    2012年11月2日 4:05
  • こんなふうにすれば回避できました

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Controls.Remove(shapeContainer1);
        }
    
        protected override void  OnLoad(EventArgs e)
        {
            this.Controls.Add(shapeContainer1);
            base.OnLoad(e);
        }
    
        //Shapeを貼ったときに自動で作成されるShapeContainer
        private Microsoft.VisualBasic.PowerPacks.ShapeContainer shapeContainer1;
    
        //このフォームを継承したフォームにShapeを貼りたい時は、
        //最初だけは一時的にBaseFormにあるShapeContainerを消す必要があるみたい
        //継承したフォームにShapeを貼らないならこのプロパティは不要
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual bool IsShowBaseShape
        {
            get
            {
                return _IsShowBaseShape;
            }
            set
            {
                _IsShowBaseShape = value;
                if (value)
                {
                    this.Controls.Add(shapeContainer1);
                    this.Controls.SetChildIndex(shapeContainer1, 0);
                }
                else
                {
                    this.Controls.Remove(shapeContainer1);
                }
            }
        }
        private bool _IsShowBaseShape = true;
    }

    継承されたフォームがデザイナ用に生成されるときに、ShapeContainerが存在するとエラーを吐くようです。
    なので、生成された直後にShapeContainerが存在していないように、一時的にはずしています。

    また、継承したフォームにShapeを貼ろうとしても、すでにShapeContainerが存在していると*.Designer.csに不完全なコードが生成されてしまいます。
    この場合も一時的にShapeContainerをはずしてからShapeを貼ると、正常にコード(新しいShapeContainer)が生成されます。

    ShapeContainerをpublicやprotectedにするとこの方法でも回避はできないので、privateにしておく必要があります。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク コンドル 2012年11月5日 1:35
    2012年11月2日 11:58
  • K. Takaokaさん
    gekkaさん

    ありがとうございます。
    とても参考になりました。

    > VB Power Pack は使用していないのですが、エラーメッセージからすると、RectangleShape をデザイナで配置すると、ShapeContainer というのが自動で配置されるのでしょうか?
    はい、自動で配置されます。

    > それで、ShapeContainer の可視性がデフォルトの private のままになっていて、 protected に変更しないとダメって話だったりしませんかね。
    確認してみましたが、解消されませんでした。
    確かに!と思ったのですが・・・

    表示されていた警告はgekkaさんからご教示頂いた方法で、
    解消されました。
    shapeContainerの扱いが特殊で、それを考慮する必要があるのですね。

    とても勉強になりました。
    ありがとうございました。
    2012年11月5日 1:35