web-dev-qa-db-ja.com

C#のパラメーターを持つ 'UserControl'コンストラクター

クレイジーだと思いますが、私はパラメーターを持たないコンストラクター(必要な場合)を好むタイプの男です。私の思考プロセス:オブジェクトを実際に構築するためにプロパティが必要な場合は、コンストラクターに配置する必要があります。次の2つの利点があります。

  1. オブジェクトが(エラー/例外なしで)構築されるとき、私のオブジェクトは良いことを知っています。
  2. 特定のプロパティを設定することを忘れないようにするのに役立ちます。

この考え方は、フォーム/ユーザーコントロールの開発に関して私を傷つけ始めています。これを想像してくださいUserControl

public partial class MyUserControl : UserControl
{
  public MyUserControl(int parm1, string parm2)
  {
    // We'll do something with the parms, I promise
    InitializeComponent();
  }
}

設計時に、このUserControlをフォームにドロップすると、Exceptionが得られます。

コンポーネント「MyUserControl」を作成できませんでした...
System.MissingMethodException-このオブジェクトにはパラメーターなしのコンストラクターは定義されていません。

私にとって、それを回避する唯一の方法は、デフォルトのコンストラクタを追加することであるように思えます(他の誰かが方法を知っていない限り)。

public partial class MyUserControl : UserControl
{
  public MyUserControl()
  {
    InitializeComponent();
  }

  public MyUserControl(int parm1, string parm2)
  {
    // We'll do something with the parms, I promise
    InitializeComponent();
  }
}

パラメーターなしのコンストラクターを含めないことの全体的なポイントは、それを使用しないことです。そして、私はDesignModeプロパティを使って次のようなこともできません:

public partial class MyUserControl : UserControl
{
  public MyUserControl()
  {
    if (this.DesignMode)
    {
      InitializeComponent();
      return;
    }

    throw new Exception("Use constructor with parameters");
  }
}

これも機能しません:

if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)

順調に進んで...

パラメーターなしのコンストラクターがあり、フォームにドロップできます。フォームのInitializeComponentは次のようになります。

private void InitializeComponent()
{
  this.myControl1 = new MyControl();

  // blah, blah
}

そして、私がそれをやったので(はい、Visual Studioが生成したコメントを無視して)、いじってみて、InitializeComponentにパラメーターを渡して、MyControlのコンストラクターに渡すことができるので、 。

これは私をこれに導きます:

public MyForm()
{
  InitializeComponent(); // Constructed once with no parameters

  // Constructed a second time, what I really want
  this.myControl1 = new MyControl(anInt, aString);  
}

コンストラクターにパラメーターを持つUserControlを使用するには、不要な2番目のコンストラクターを追加する必要がありますか?コントロールを2回インスタンス化しますか?

私は何か間違ったことをしているに違いないと感じています。考え?ご意見?保証(できれば)?

97
JustLooking

Windows Formsの動作方法に関する設計上の決定により、Windows Formsコンポーネントのパラメーター化された.ctorが多少なりとも排除されます。それらを使用することはできますが、実行すると、一般に承認されたメカニズムの外に出ます。むしろ、Windowsフォームはプロパティを介した値の初期化を好みます。広く使用されていない場合、これは有効な設計手法です。

ただし、これにはいくつかの利点があります。

  1. クライアントの使いやすさ。クライアントコードは大量のデータを追跡する必要はなく、すぐに何かを作成し、賢明な(面白くない場合)結果でそれを見ることができます。
  2. デザイナーの使いやすさ。デザイナーのコードは、一般に、より明確で簡単に解析できます。
  3. 単一のコンポーネント内で異常なデータ依存関係を阻止します。 (ただし、Microsoftでも SplitContainer でこれを吹き飛ばしました)

この手法でもデザイナーと適切に作業するためのフォームには多くのサポートがあります。 DefaultValueAttributeDesignerSerializationVisibilityAttribute 、および BrowsableAttribute のようなものは、提供する機会を与えます最小限の労力でリッチなクライアントエクスペリエンスを実現します。

(これは、Windowsフォームでのクライアントエクスペリエンスのために作成された唯一の妥協ではありません。抽象基本クラスコンポーネントも毛むくじゃらになる可能性があります。)

パラメーターなしのコンストラクターを使用し、Windowsフォームの設計原則内で作業することをお勧めします。 UserControlが強制する必要のある実際の前提条件がある場合は、それらを別のクラスにカプセル化し、プロパティを介してそのクラスのインスタンスをコントロールに割り当てます。これにより、懸念の分離も少し改善されます。

62
Greg D

クラスを設計するための2つの競合するパラダイムがあります。

  1. パラメーターなしのコンストラクターを使用し、後でプロパティの束を設定します
  2. パラメーター化されたコンストラクターを使用して、コンストラクターでプロパティを設定します

Visual Studio Windows Forms Designerは、適切に機能するために、コントロールにパラメーターなしのコンストラクターを提供することを強制します。実際には、コントロールをインスタンス化するためにパラメーターなしのコンストラクターのみが必要であり、それらを設計する必要はありません(デザイナーは実際にコントロールの設計中にInitializeComponentメソッドを解析します)。つまり、デザイナーを使用してパラメーターなしのコンストラクターなしでフォームまたはユーザーコントロールを設計できますが、デザイナーがインスタンス化に失敗するため、そのコントロールを使用する別のコントロールを設計することはできません。

プログラムでコントロールをインスタンス化するつもりがない場合(つまり、UIを「手作業で」構築する場合)、パラメーター化されたコンストラクターは使用されないため、作成する必要はありません。プログラムでコントロールをインスタンス化する場合でも、必要に応じてデザイナーで使用できるように、パラメーターなしのコンストラクターを提供することができます。

どのパラダイムを使用する場合でも、特にDesignModeプロパティは読み込み時に機能するがコンストラクターでは機能しないため、OnLoad()メソッドに長い初期化コードを配置することも一般に良い考えです。

36
Kevin Kibler

私がお勧めします

public partial class MyUserControl : UserControl
{
    private int _parm1;
    private string _parm2;

    private MyUserControl()
    {
        InitializeComponent();
    }

    public MyUserControl(int parm1, string parm2) : this()
    {
        _parm1 = parm1;
        _parm2 = parm2;
    }
}

このように、ベースコンストラクターは常に最初に呼び出され、コンポーネントへの参照はすべて有効です。

その後、必要に応じてパブリックctorをオーバーロードし、コントロールが常に正しい値でインスタンス化されるようにします。

いずれにしても、パラメーターのないctorが呼び出されないようにします。

私はこれをテストしていませんので、それが落ちたら謝罪します!

9
Antony Koch

デザイナーにパラメーターなしのコンストラクターを提供し、プライベートにします-本当にこの方法で行う必要がある場合... :-)

編集:もちろん、これはUserControlsでは機能しません。私は明らかにはっきりと考えていませんでした。デザイナーはInitializeComponent()でコードを実行する必要があり、コンストラクターがプライベートの場合は機能しません。ごめんなさいただし、フォームに対してはdoes動作します。

4
Dan Byström

残念ながら、これは制御スペースだけでなく、頻繁に発生する設計上の問題です。

パラメーターなしのコンストラクターは理想的ではありませんが、パラメーターなしのコンストラクターが必要な場合がよくあります。たとえば、多くの値型、IMOは、パラメータなしのコンストラクタなしでより良いでしょうが、そのように機能するものを作成することは不可能です。

これらの状況では、できるだけ最良の方法でコントロール/コンポーネントを設計する必要があります。少なくとも(できれば)コンポーネントを適切な値で初期化できるため、妥当な(できれば最も一般的な)デフォルトパラメータを使用すると劇的に役立ちます。

また、コンポーネントの生成後にこれらのプロパティを変更できるようにコンポーネントを設計してください。 Windows Formsコンポーネントを使用する場合、通常は問題ありません。ロード時間が安全になるまで、ほとんど何でもできるからです。

繰り返しますが、これは理想的ではありませんが、私たちが一緒に生活し、回避しなければならないものです。

4
Reed Copsey

要するに、デザイナーはパラメーターのないコンストラクターが好きな人です。したがって、私の知る限り、パラメーターベースのコンストラクターを本当に使用したい場合は、おそらく何らかの方法で回避する必要があります。

4
Fredrik Mörk

これを行うだけです:

public partial class MyUserControl : UserControl
{
    public MyUserControl() : this(-1, string.Empty)
    {
    }

    public MyUserControl(int parm1, string parm2)
    {
        // We'll do something with the parms, I promise
        if (parm1 == -1) { ... }
        InitializeComponent();
    }
}

その後、「実際の」コンストラクターはそれに応じて動作できます。

3
Bob Nadler

質問が出されてからかなりの時間が経ちましたが、私のアプローチは誰かに役立つかもしれません。

個人的には、特定のプロパティを設定することを忘れないように、パラメータ化されたコンストラクタを使用することも好みます。

したがって、実際のコンストラクタを使用する代わりに、私は単純にpublic void PostConstructorを定義します。したがって、UserControlの実際のコンストラクターには、常にInitializeComponent()のみが含まれます。このように、お気に入りのプログラミングパラダイムをVisualStudioに合わせて調整する必要はなく、Designerを適切に実行する必要があります。このプログラミングスキーマが機能するためには、一番下からそれに従う必要があります。

実際には、このPostConstructionalizmは次のようになります。UserControl呼び出し階層の一番下にあるControlから始めましょう。

public partial class ChildControl : UserControl
{
  public ChildControl()
  {
    InitializeComponent();
  }

  public void PostConstructor(YourParameters[])
  {
      //setting parameters/fillingdata into form
  }
}

したがって、ChildControlを含むUserControlは次のようになります。

public partial class FatherControl : UserControl
{
  public FatherControl()
  {
    InitializeComponent();
  }

  public void PostConstructor(YourParameters[])
  {
      ChildControl.PostConstructor(YourParameters[])
      //setting parameters/fillingdata into form
  }
}

最後に、ユーザーコントロールのいずれかを呼び出すフォームは、PostConstructorをInitializeComponentの後に配置します。

public partial class UI : Form
{
  public UI(yourParameters[])
  {
    InitializeComponent();
    FatherControl.PostConstructor(yourParameters[]);
  }
}
1
Hermilton

私はそれを回避する方法を持っています。

  1. パラメーターなしのコンストラクターでフォームにコントロールAを作成します。
  2. フォームcontstructorのパラメーター化されたコンストラクターでコントロールBを作成します。
  3. AからBに位置とサイズをコピーします。
  4. Aを非表示にします。
  5. BをAの親に追加します。

これが役立つことを願っています。私はちょうど同じ質問に出会い、この方法を試し、テストしました。

デモンストレーション用のコード:

public Form1()
{
    InitializeComponent();
    var holder = PositionHolderAlgorithmComboBox;
    holder.Visible = false;
    fixedKAlgorithmComboBox = new MiCluster.UI.Controls.AlgorithmComboBox(c => c.CanFixK);
    fixedKAlgorithmComboBox.Name = "fixedKAlgorithmComboBox";
    fixedKAlgorithmComboBox.Location = holder.Location;
    fixedKAlgorithmComboBox.Size = new System.Drawing.Size(holder.Width, holder.Height);
    holder.Parent.Controls.Add(fixedKAlgorithmComboBox);
}

ホルダーはコントロールA、fixedKAlgorithmComboBoxはコントロールBです。

さらに優れた完全なソリューションは、リフレクトを使用して、プロパティを1つずつAからBにコピーすることです。当分の間、私は忙しく、これをしていません。将来的には、コードを使用して戻ってくるかもしれません。しかし、それほど難しくはないので、自分でできると思います。

0
Eric Chow

メインのWindowsフォームで作成されたオブジェクトをカスタムUserControlフォームに渡そうとすると、同様の問題が発生しました。私のために働いたのは、デフォルト値を持つプロパティをserControl.Designer.csに追加し、メインフォームでInitializeComponent()呼び出し後に更新することでした。デフォルト値を使用すると、WinFormsデザイナーは「オブジェクトのインスタンスに設定されていないオブジェクト参照」エラーをスローできなくなります。

例:

// MainForm.cs
public partial class MainForm : Form
   public MainForm() 
   {
     /* code for parsing configuration parameters which producs in <myObj> myConfig */
     InitializeComponent();
     myUserControl1.config = myConfig; // set the config property to myConfig object
   }

//myUserControl.Designer.cs
partial class myUserControl
{
    /// <summary> 
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    // define the public property to hold the config and give it a default value
    private myObj _config = new myObj(param1, param2, ...);      
    public myObj config
    {
        get
        {
            return _config ;
        }
        set
        {
            _config = value;
        }
    }

    #region Component Designer generated code
    ...
}

お役に立てれば!

0
mikmaksi