netlib.narod.ru< Назад | Оглавление | Далее >

События и методы «On...»

Как вы уже видели, при создании экземпляра Control или любого класса, происходящего от Control, например Form, можно установить обработчик события Paint. Для этого сначала нужно определить статический метод с такими же типами параметров и возвращаемого значения, что и у делегата PaintEventHandler:

  static void MyPaintHandler(object objSender, PaintEventArgs pea)
  {
      // Код, выполняющий прорисовку
  }

Затем нужно установить обработчик этого события для конкретного объекта, например для form, при помощи кода:

  form.Paint += new PaintEventHandler(MyPaintHandler);

Однако в классе, происходящем от Control, не нужно (хотя и можно) устанавливать обработчик события Paint. Можно просто переопределить метод OnPaint:

  protected override void OnPaint(PaintEventArgs pea)
  {
      // Код, выполняющий прорисовку
  }

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


События Control (выборочно)



Событие Метод Делегат Параметр

Paint OnPaint PaintEventHandler PaintEventArgs


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

Вы можете предположить (я тоже так думал), что метод OnPaint по существу является предустановленным обработчиком события Paint. Но это не так. Метод OnPaint класса Control отвечает за вызов всех установленных обработчиков события Paint.

Давайте затронем эту концепцию. Показанный ранее класс HelloWorld является наследником Form, а созданный нами класс InheritHelloWorld будет наследником класса HelloWorld.

InheritHelloWorld.cs

  //----------------------------------------------------
  // InheritHelloWorld.cs (C) 2001 by Charles Petzold
  //----------------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class InteritHelloWorld: HelloWorld
  {
      public new static void Main()
      {
          Application.Run(new InheritHelloWorld());
      }
      public InheritHelloWorld()
      {
          Text = "Inherit " + Text;
      }
      protected override void OnPaint(PaintEventArgs pea)
      {
          Graphics grfx = pea.Graphics;

          grfx.DrawString("Hello from InheritHelloWorld!",
                          Font, Brushes.Black, 0, 100);
      }
  }

Сначала решим «хозяйственные» вопросы. При создании проекта InheritHelloWorld в Visual Studio .NET я создал новый файл С# InheritHetloWorld.cs как обычно, но, кроме того, потребовалось включить в проект HelloWorld.cs. Для этого я выбрал пункт меню Add Existing Item и затем — Link File в раскрывающемся меню рядом с кнопкой Open. Поэтому не потребовалось создавать копию файла HelloWorld.cs.

Заметьте: метод Main содержит ключевое слово new, означающее, что он замещает методы Main, определенные в классах-предках, например в HelloWorld. Кроме того, вы должны сообщить Visual Studio .NET, какой из методов Main является точкой входа в программу. Это делается в диалоговом окне Properties. В General | Common Properties, укажите Startup Object InheritHelloWorld.

При запуске компилятора С# из командной строки укажите в командной строке оба исходных файла, а также ключ компилятора:

  /Main: InheritHelloWorld

чтобы определить, метод Main какого класса является точкой входа в программу.

Как я уже говорил, при создании нового объекта класса-наследника при помощи конструктора по умолчанию выполняются все конструкторы по умолчанию всех классов-предков, начиная с Object. На предпоследнем этапе этого процесса вызывается конструктор HelloWorld, присваивающий свойству Text формы значение «Hello World». И, наконец, последним выполняется конструктор InheritHelloWorld, присваивающий значение свойству Text:

  Text = "Inherit " + Text;

To, что в заголовке этой программы написано «Inherit Hello World», подтверждает, что конструкторы выполняются именно в такой последовательности.

Метод OnPaint класса InheritHelloWorld переопределяет метод OnPaint класса HelloWorld. При запуске InheritHelloWorld показывается текст «Hello from InheritHelloWorld!». Я специально расположил этот текст в точке с координатами (0, 100), чтобы было видно, что метод HelloWorld.OnPaint при этом не выполняется. Метод OnPaint класса HelloWorld переопределен.

Теперь взглянем на программу, делающую немного другую вещь. В ней не определяется класс, унаследованный от HelloWorld, а создается экземпляр класса HelloWorld.

InstantiateHelloWorld.cs

  //--------------------------------------------------------
  // InstantiateHelloWorld.cs (C) 2001 by Charles Petzold
  //--------------------------------------------------------
  using System;
  using System.Drawing;
  using System.Windows.Forms;

  class InstantiateHelloWorld
  {
      public static void Main()
      {
          Form form = new HelloWorld();
          form.Text = "Instantiate " + form.Text;
          form.Paint += new PaintEventHandler(MyPaintHandler);

          Application.Run(form);
      }
      static void MyPaintHandler(object objSender, PaintEventArgs pea)
      {
          Form form = (Form)objSender;
          Graphics grfx = pea.Graphics;

          grfx.DrawString("Hello from InstantiateHelloWorld!",
                          form.Font, Brushes.Black, 0, 100);
      }
  }

Во-первых, обратите внимание, что класс InstantiateHelloWorld не наследуется от HelloWorld, Form, или любого другого класса (конечно же, кроме Object):

  class InstantiateHelloWorld

Вместо этого создается новый экземпляр класса HelloWorld, который помещается в переменную form, как в предыдущих программах этой главы, создававших экземпляры класса Form:

  Form form = new HelloWorld();

Программа может помещать объект HelloWorld в переменную типа Form, потому что HelloWorld является наследником Form. При создании объекта HelloWorld вызывается конструктор HelloWorld, присваивающий свойству Text формы значение «Hello World». Следующий оператор добавляет в начало свойства Text слово «Instantiate». Затем программа устанавливает для формы обработчик события Paint.

Но в клиентской области InstantiateHelloWorld появится не «Hello from InstantiateHelloWorld!», а текст «Hello, Windows Forms!», отображаемый методом OnPaint класса HelloWorld. Что произошло?

Метод OnPaint класса Control отвечает за вызов всех установленных обработчиков события Paint. Так как класс HelloWorld перекрывает OnPaint, эта работа не выполняется. Поэтому в документации .NET рекомендуется: если вы переопределяете защищенный метод с префиксом On, следует вызвать метод On базового класса так:

  base.OnPaint(pea)

Попробуйте поместить этот оператор в начале метода OnPaint программы HelloWorld и заново собрать InstantiateHelloWorld. Программа будет работать так, как вы, вероятно, и хотели: показывать свою текстовую строку («Hello from InstantiateHelloWorld!») и, кроме того, показывать строку «Hello, Windows Forms!».

В усовершенствованной версии события будут происходить в таком порядке.

Документация по Windows Forms рекомендует вызывать метод On базового класса, если переопределяется метод On. Однако обычно это требуется, только когда вы определяете класс, экземпляры которого собираетесь создавать, и этот класс устанавливает обработчики событий для переопределяемых вами методов On. Так бывает, но не очень часто. Но иногда все-таки приходится вызывать методы базового класса в методах, переопределяющих методы On. Как вы узнаете в главе 3, одним из таких случаев является использование OnResize.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz