Конструктор 'UserControl' с параметрами в C#


Назовите меня сумасшедшим, но я тип парня, который любит конструкторы с параметрами (если это необходимо), в отличие от конструктора без параметров, за которым следует установка свойств. Мой мыслительный процесс: если свойства необходимы для фактического построения объекта, они должны войти в конструктор. Я получаю два преимущества:

  1. я знаю, что когда объект построен (без ошибок/исключений), мой объект хорош.
  2. это помогает не забыть установить определенное свойство.

это мышление начинает причинять мне боль в отношении разработки form / usercontrol. Представьте себе это 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'...
Система.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 параметры в конструктор, я должен добавить второй конструктор, который мне не нужен? И создать экземпляр элемента управления дважды?

я чувствую, что должен делать что-то неправильно. Мысли? Мнения? Уверенность (надеюсь)?

10 94

10 ответов:

проектные решения, принятые в отношении того, как работает Windows Forms, более или менее исключают параметризацию .конструкторы для компонентов. Вы можете использовать их, но когда вы это делаете, вы выходите за рамки общепринятых механизмов. Скорее, Windows Forms предпочитает инициализацию значений через свойства. Это допустимый метод проектирования, если он не используется широко.

это имеет некоторые преимущества, хотя.

  1. простота использования для клиентов. Клиентскому коду это не нужно отследите кучу данных, он может сразу создать что-то и просто увидеть его с разумными (если неинтересными) результатами.
  2. простота использования для дизайнера. Дизайнерский код понятнее и проще для разбора в целом.
  3. не поощряет необычные зависимости данных в пределах одного компонента. (Хотя даже microsoft взорвал этот с SplitContainer)

есть много поддержки в формах для правильной работы с дизайнером в этом техника тоже. Такие вещи, как DefaultValueAttribute,DesignerSerializationVisibilityAttribute и BrowsableAttribute даст вам возможность обеспечить богатый опыт клиента с минимальными усилиями.

(это не единственный компромисс, который был сделан для работы с клиентами в windows forms. Абстрактные компоненты базового класса также могут стать волосатыми.)

Я бы предложил придерживаться конструктора без параметров и работать в рамках принципов проектирования windows forms. Если есть реальные предпосылки, что ваш UserControl необходимо принудительно, инкапсулировать их в другой класс, а затем назначить экземпляр этого класса для вашего элемента управления через свойство. Это также даст немного лучшее разделение беспокойства.

  1. используйте конструкторы без параметров и установите кучу свойств после этого
  2. используйте параметризованные конструкторы для установки свойств в конструкторе

The Visual Studio Windows Forms Designer заставляет вас предоставить параметрический констуктор на элементах управления для правильной работы. На самом деле, для этого требуется только конструктор без параметров, чтобы создавать экземпляры элементов управления, но не создавать их (конструктор фактически будет анализировать метод InitializeComponent при разработке элемента управления). Это означает, что конструктор можно использовать для разработки формы или пользовательского элемента управления без конструктора без параметров, но нельзя создать другой элемент управления для использования этого элемента управления, поскольку конструктор не сможет создать его экземпляр.

Если вы не собираетесь программно создавать свои элементы управления (т. е. создавать свой пользовательский интерфейс "вручную"), то не беспокойтесь создание параметризованных конструкторов, так как они не будут использоваться. Даже если вы собираетесь программно создавать экземпляры своих элементов управления, вы можете предоставить конструктор без параметров, чтобы их можно было использовать в конструкторе, если это необходимо.

независимо от того, какую парадигму вы используете, это также, как правило, хорошая идея, чтобы положить длинный код инициализации в OnLoad() метод, тем более что DesignMode свойство будет работать во время загрузки, но не работать в конструкторе.

Я бы порекомендовал

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, если это необходимо, гарантируя, что элемент управления всегда создается с правильными значениями.

в любом случае, вы убедитесь, что конструктор без параметров не назвал.

Я не проверял это, так что если он падает, я прошу прощения!

к сожалению, это проблема проектирования, которая будет происходить часто, а не только в пространстве управления.

часто бывают ситуации, когда вам нужно иметь конструктор без параметров, даже если конструктор без параметров не идеален. Например, многие типы значений IMO были бы лучше без конструкторов без параметров, но невозможно создать тот, который работает таким образом.

в этих ситуациях вам нужно просто спроектировать элемент управления / компонент в лучший из возможных способов. Использование разумных (и предпочтительно наиболее распространенных) параметров по умолчанию может значительно помочь, так как вы можете по крайней мере (надеюсь) инициализировать компонент с хорошим значением.

кроме того, попробуйте спроектировать компонент таким образом, чтобы можно было изменить эти свойства после создания компонента. С компонентами Windows Forms это обычно нормально, так как вы можете в значительной степени делать что-либо до времени загрузки безопасно.

опять же, я согласен - это не идеал, но это просто то, с чем мы должны жить и работать.

короче говоря, дизайнер-это тот парень, который любит конструкторы без параметров. Итак, насколько мне известно, если вы действительно хотите использовать конструкторы на основе параметров, вы, вероятно, застряли с работой вокруг него так или иначе.

предоставлять конструктор без параметров конструктор и сделать его частным - если вы действительно должны сделать это таким образом... : -)

EDIT: ну, конечно, это не будет работать для UserControls. Я, очевидно, плохо соображал. Дизайнер должен выполнить код в InitializeComponent () и это не может работать, если конструктор является частным. Сожалеть об этом. Это тут работы для форм, однако.

просто сделать это:

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();
    }
}

тогда "реальный" конструктор может действовать соответственно.

прошло довольно много времени с тех пор, как был задан вопрос, но, возможно, мой подход кому-то полезен.

Я лично также предпочитаю использовать параметризованные конструкторы, чтобы не забыть задать определенное свойство.

поэтому вместо использования фактического конструктора я просто определяю public void PostConstructor где все вещи помещаются вы обычно помещаете в конструктор. Таким образом, фактический конструктор UserControl всегда содержит только InitializeComponent (). Таким образом, вам не нужно настраивать свою любимую парадигму программирования на VisualStudios, чтобы правильно запустить конструктор. Чтобы эта схема программирования работала, она должна следовать с самого низа.

на практике это PostConstructionalizm будет выглядеть так: Давайте начнем с элемента управления в нижней части иерархии вызовов UserControl.

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

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

таким образом, UserControl, содержащий ChildControl будет выглядеть примерно так:

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. создайте элемент управления A в форме с конструктором без параметров.
  2. создайте элемент управления B с параметризованным конструктором в форме contstructor.
  3. копировать положение и размер от A до B.
  4. сделать невидимым.
  5. добавить B к материнской.

надеюсь, что это поможет. Я просто столкнулся с тем же вопросом и попробовал и протестировал этот метод.

код для продемонстрировать:

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);
}

держатель контролировать, fixedKAlgorithmComboBox Б. контроль

еще лучшим и полным решением было бы использовать reflect для копирования свойств по одному из A В B. В настоящее время я занят, и я этого не делаю. Может быть, в будущем я вернусь с кодом. Но это не так сложно и я верю, что вы можете сделать это самостоятельно.

У меня была аналогичная проблема при попытке передать объект, созданный в основной форме Windows, в пользовательскую форму UserControl. То, что работало для меня, добавляло свойство со значением по умолчанию в UserControl.Дизайнер.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
    ...
}

надеюсь, что это помогает!