Правильная архитектура для расширения базовых классов WinForm UserControl?

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

class BaseControl : UserControl 
{
   // common
}  

class RedControl : BaseControl
{
   // specialized
}

class BlueControl : BaseControl
{
   // specialized
}

и т.д ...

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

Когда я попробовал следующий код, я не увидел ни одной кнопки во время выполнения...

public partial class RedControl : BaseControl
{
  public RedControl()
  {
    InitializeComponent();

    addButtonToBase();  // no button shows up 
    this.PerformLayout();
  }
  void addButtonToBase()
  {
    Button button  = new Button();
    button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)));
    button.Location = new System.Drawing.Point(3, 3);
    button.Size = new System.Drawing.Size(23, 23);
    button.Text = "My Button";

    baseSplitContainer.Panel1.Controls.Add(button); // protected child control in base
  }
  // ...
}

Я могу сделать кнопку отображаемой как дочернюю для baseSplitContainer, если я сделаю addButtonToBase() виртуальной и вручную добавлю ее в сгенерированный код в InitalizeComponent() BaseControl. Макет BaseControl все еще продолжался, и вам разрешено вызывать виртуальные функции в конструкторах в С#.Net....

Так что даже если это работает, это не очень хорошее решение. Во-первых, когда я редактирую BaseControl в конструкторе VS, вызов addBaseControl() в IntializeComponent удаляется. Во-вторых, вызов виртуальных функций в конструкторах кажется опасным.

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

Кстати, да, я знаю, что WPF хорош в этом. Не могу использовать его из-за ограничений в других системах.


person meissnersd    schedule 24.08.2011    source источник


Ответы (2)


Оказывается, правильный способ изменить макет базового элемента управления — переопределить вызов макета из Control.OnLayout().

так что-то вроде

public RedControl()
{
    //....
    protected override void OnLayout(LayoutEventArgs e)
    {
        addButtonToBase(); // modify base layout
        base.OnLayout(e);
    }
}
person meissnersd    schedule 30.08.2011

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

public RedControl()
: base()
  { ... }

OR

public RedControl()
  {
    base.InitializeComponent();
    InitializeComponent();

    addButtonToBase();  // no button shows up 
    this.PerformLayout();
  }
person Scarlaxx    schedule 24.08.2011
comment
Когда создается производный элемент управления, всегда вызывается конструктор базового класса. Нет необходимости вызывать base.InitializeComponent(). Также это не решает проблему макета. - person meissnersd; 24.08.2011