Как правильно инициализировать приложение Windows Forms - PullRequest
1 голос
/ 10 февраля 2011

Я написал этот пример программы - упрощение очень сложного приложения.Тот же двоичный файл [.exe] выдает исключение нулевого указателя на некоторых машинах при запуске.Итак, я хочу знать, как правильно создать форму Windows Forms.

В нашем приложении метод Form1_SizeChanged является результатом метода this.ResumeLayout(false), который является последним оператором в InitializeComponents().Я не знаю, чтобы это смоделировать, поэтому я сам изменил размер для этой тестовой программы.

public partial class Form1 : Form
{
    public class Logger {
        public Logger() { }
        public void log(string str) {
            Console.WriteLine("logging - " + str);
        }
    }

    Logger logger = null;

    public Form1()
    {
        InitializeComponent();
        this.Size = new Size(200, 300);
        MyInitialize();
    }

    private void MyInitialize() {

        // Just that it takes some time.
        Console.WriteLine("MyInitialize -- Enter");
        for (int count = 0; count <5 ; count++){
            Console.WriteLine("Sleeping -- " + count);
            Thread.Sleep(1000);
        }
        logger = new Logger();
    }

    private void sleepingPill(int millisec) {
        Thread.Sleep(millisec);
    }

    private void Form1_SizeChanged(object sender, EventArgs e)
    {
        logger.log("Form1_SizeChanged -- Enter");
    }
}

В соответствии с моим пониманием, Form1_SizeChanged не следует вызывать, если Form1 не будет правильно построен.Может кто-нибудь пролить свет на то, как работает архитектура событий Windows Forms в этом сценарии?


Original Stack Trace: from our complex application
System.NullReferenceException was unhandled
  Message=Object reference not set to an instance of an object.
  Source=ABCD
  StackTrace:
       at ABCD.Form1.AppendToLog(String s)
       at ABCD.Form1.Form1_SizeChanged(Object sender, EventArgs e)
       at System.Windows.Forms.Control.OnSizeChanged(EventArgs e)
       at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight)
       at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height)
       at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
       at System.Windows.Forms.Form.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
       at System.Windows.Forms.Control.ScaleControl(SizeF factor, BoundsSpecified specified)
       at System.Windows.Forms.ScrollableControl.ScaleControl(SizeF factor, BoundsSpecified specified)
       at System.Windows.Forms.Form.ScaleControl(SizeF factor, BoundsSpecified specified)
       at System.Windows.Forms.Control.ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
       at System.Windows.Forms.ContainerControl.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
       at System.Windows.Forms.ContainerControl.PerformAutoScale(Boolean includedBounds, Boolean excludedBounds)
       at System.Windows.Forms.ContainerControl.PerformNeededAutoScaleOnLayout()
       at System.Windows.Forms.ContainerControl.OnLayoutResuming(Boolean performLayout)
       at System.Windows.Forms.Control.ResumeLayout(Boolean performLayout)
       at ABCD.Form1.InitializeComponent()
       at ABCD.Form1..ctor()
       at ABCD.Program.Main()
  InnerException:

Уведомление из трассировки стека: Form1_sizeChanged вызывается из InitializeComponents () .., который, я думаю, не должен происходить.Form1_sizeChanged является методом экземпляра класса Form1 и не должен вызываться перед созданием Form1.Если среда .NET хочет обработать это событие, она должна подождать, пока форма Form1 будет построена правильно, не так ли?

Ответы [ 4 ]

2 голосов
/ 10 февраля 2011

Во второй строке конструктора вы устанавливаете размер.Это вызовет функцию Form1_SizeChanged () перед созданием регистратора.Переместите настройку размера после вызова MyInitialize.

1 голос
/ 10 февраля 2011

Вероятно, Form1_SizeChanged вызывается до MyInitialize и, следовательно, до инициализации регистратора.Измените этот метод на

   if (logger != null)
       logger.log("Form1_SizeChanged -- Enter");
1 голос
/ 10 февраля 2011

Я предполагаю, что вы получаете пустую ошибку в событии SizeChanged /

Ваша подпрограмма InitializeComponent () устанавливает событие Form1_SizeChanged для сопоставления с событием SizeChanged формы. Ваш код this.Size запустит указанное событие. Поскольку logger.log не инициализируется до вашей процедуры MyInitialize, вы можете увидеть спорадические результаты.

Обычно Windows ставит в очередь событие Form1_SizeChanged, и ваше приложение будет реагировать на него асинхронно, поэтому иногда оно может отвечать на него после инициализации Logger (), а иногда - до инициализации Logger.

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

0 голосов
/ 10 апреля 2012

Вы сами ответили на вопрос в своем вопросе.Все это связано с вызовом ResumeLayout ().

Автоматически сгенерированный код «InitializeComponent ()» отключает макет, собирает компоненты и затем снова включает макет.Это, я полагаю, оптимизация, потому что пересчет компоновки при каждом добавлении подкомпонента может быть медленным.Когда вернется макет, вероятно, будут вызваны события изменения размера многих компонентов, включая ваш.

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

Исправление заключается в инициализации всего, что должно иметь полностью сконструированный объект в событии Load.Событие Load вызывается только один раз и вызывается после полного создания экземпляра.Для вас это означает удаление события Form1_SizeChanged из того, чем управляет среда разработки, и добавление события Form1_Load, которое включает

private void Form1_Load(object sender, EventArgs e)
{
    this.Load += Form1_SizeChanged;
}

Да, это боль.Да, вы правы, а формы неправильны.По крайней мере, обходной путь не слишком обременителен.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...