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

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

В основном у меня есть несколько пользовательских элементов управления с одним базовым классом, который захватывает экземпляр моего основного окна формы, поэтому пользовательский элемент управления имеет доступ к свойствам основной формы и может вызывать методы в основной форме. Вот фрагмент (this.frmParent является публичным участником):

    private void ucBase_Load( object sender, EventArgs e )
    {
        // Establish the link to the main form.
        this.frmParent = FindForm() as frmMain;
    }

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

public partial class ucLiberty : ucBase

Затем в основной форме я вызову пользовательский элемент управления следующим образом:

                ucLiberty Liberty = new ucLiberty();
                IQDevicePath = Liberty.GetIQDrivePath();

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

Я заметил, что событие загрузки в пользовательском элементе управления не запускается. Я нашел метод под названием CreateControl (), который должен принудительно создать элемент управления, а затем мое событие load начало срабатывать, однако, когда я отследил выполнение в отладчике, я добрался до базового класса, где он пытался заполнить frmParent, FindForm () не всегда возвращает ненулевое значение.

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

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

this.frmParent = new frmMain();

Однако мне все еще нужен вызов CreateControl () для запуска события load, и мне не нравится идея требовать, чтобы будущие сопровождающие имели явные знания о различных поведенческих императивах. Другими словами, я бы хотел, чтобы все мои пользовательские элементы управления работали одинаково, чтобы упростить обслуживание.

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

Есть ли у кого-нибудь идеи, как решить эти проблемы? Спасибо.


person Mike Malter    schedule 05.09.2011    source источник


Ответы (2)


Вы совершаете довольно серьезный грех ООП, заставляя пользовательский элемент управления знать о форме, в которой он размещен. предполагается быть независимым классом, который не заботится о своем контейнере, вы используете события, чтобы позволить контейнеру знать обо всем, что произошло в вашем классе, что может заинтересовать контейнер. принцип, который буквально следует любым стандартным элементам управления в Winforms. Например, TextBox не волнует, в какой форме он помещен.

Это теория, практика не всегда так чиста. Проблема, с которой вы столкнулись, заключается в том, что метод OnLoad (также известный как событие Load) срабатывает по другой причине. Он запускается, когда создается собственный дескриптор Windows. Что обычно происходит, когда окно формы создается при вызове метода Show (). Это после метода формы IntializeComponent ().

Если у вас есть какой-либо код в конструкторе вашего пользовательского элемента управления, который требует, чтобы свойство Handle имело значение, Winforms обязуется и создает дескриптор Windows для вашего элемента управления и запускает событие Load. Слишком рано, до того, как у метода формы InitializeComponent () появилась возможность вызвать свой метод Controls.Add (). Свойство Parent еще не ссылается на форму. Kaboom на FindForm ().

С помощью отладчика легко диагностировать. Установите точку останова для метода OnLoad пользовательского элемента управления. Трассировка стека приведет вас прямо к оператору, который инициировал создание дескриптора.

person Hans Passant    schedule 05.09.2011
comment
У меня нет ничего в конструкторе пользовательского элемента управления, кроме того, что было помещено туда VS, то есть InitializeComponent (). В пользовательском элементе управления нет элементов управления, только код. событие загрузки не срабатывает при создании экземпляра элемента управления, оно не вызывается при вызове его первого метода, но вызывается при вызове второго метода и FindForm () возвращает значение null. - person Mike Malter; 06.09.2011

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

public class frmMain : Form
{
     private static frmMain s_Singleton;

     public static frmMain Singleton
     {
          get
          {
              if (s_Singleton == null) s_Singleton = new frmMain();
              return s_Singleton;
          }

     }
}

Поэтому вместо того, чтобы вызывать конструктор напрямую, вызовите frmMain.Singleton для ссылки (даже в (особенно в!) Вашем Program.cs, где форма, скорее всего, изначально построена). Кроме того, у вас есть глобально доступная ссылка на вашу основную форму, которую можно получить, вызвав frmMain.Singleton в пользовательских элементах управления.

Что касается причины, по которой ваш ucBase_Load не загружается, мое необразованное предположение состоит в том, что вы также обрабатываете событие в конкретном пользовательском элементе управления и что оно каким-то образом останавливает запуск базового обработчика. В этом случае добавьте base.OnLoad() в обработчик событий вашего конкретного пользовательского элемента управления.

А что касается того, почему FindForm не работает, это может быть вызвано тем, что метод вызывается до завершения конструктора пользовательского элемента управления. Это кажется маловероятным, но сложно сказать наверняка, не видя вашего кода. Причина, по которой это может быть проблема, заключается в том, что родительский элемент элемента управления и т. Д. Настроен в конструкторе. Но поскольку вы обрабатываете это в событии Load, это кажется маловероятным. Вы можете проверить эту теорию, переместив логику в событие OnParentChanged.

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

person havardhu    schedule 05.09.2011