Практикум прикладного программирования на C# в среде VS.NET 2005

Вывод текста в клиентскую область формы

Показывать лекцию целиком

Файлы к лабораторной работе Вы можете скачать здесь.

Даже при простом выводе текста на экран часто нужно обеспечить взаимодействие с пользователем. В данной работе мы рассмотрим вопросы вывода текста в колонках и добавление полос прокрутки. По ходу дела затроним другие полезные возможности библиотеки .NET Framework.

Создание заготовки приложения для выполнения упражнений C#

Прежде всего мы создадим главную форму, позволяющую управлять вызовом дочерних форм для каждого упражнения.

  • Перейдите в каталог для размещения работы и создайте новое решение с именем Petzold4
  • Оболочка создаст новый подкаталог Petzold4 в выбранном каталоге размещения работы.

  • Щелкните правой кнопкой мыши на узле решения в панели Solution Explorer и добавьте к решению новый пустой проект с именем Test
  • В панели Solution Explorer щелкните правой кнопкой мыши на узле References (ссылки) проекта и выполните команду Add Reference контекстного меню
  • Добавьте ссылки на нужные нам библиотеки DLL как показано на снимке
  • Щелкните по узлу Test панели Solution Explorer и добавьте в проект новый класс с именем Start.cs
  • Мастер оболочки создаст заготовку, которую мы и будем использовать для испытания примеров, рассматриваемых далее.

    using System;
      
    namespace Test
    {
      /// 
      /// Summary description for Start.
      /// 
      public class Start
      {
        public Start()
        {
          //
          // TODO: Add constructor logic here
          //
        }
      }
    }
  • Добавьте в начало файла Start.cs нужные пространства имен, соответствующие добавленным ссылкам на библиотеки DLL. Сделайте наш класс наследником библиотечного класса Form. Удалите сгенерированные оболочкой комментарии и должен получиться такой код
  • using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class Start : Form
      {
        public Start()
        {
          // В этом конструкторе будем размещать
          // настройки формы
        }
      }
    }

    Отметьте, что как только мы добавили к нашему классу базовый класс Form, в панели Solution Explorer пиктограмма класса заменилась на значек формы.

  • Добавьте к проекту новый пустой файл с именем AppClass.cs, в котором мы разместим точку входа приложения
  • Удалите из кода файла AppClass.cs лишние комментарии и конструктор. Окончательно содержащий точку входа класс должен иметь такой код
  • namespace Test
    {
      public class AppClass
      {
        // Точка входа приложения
        static void Main()
        {
          System.Windows.Forms.Application.Run(new Start());
        }
      }
    }

    Здесь мы из точки входа, расположенной в классе AppClass, создаем экземпляр главной формы вызовом конструктора класса Start.

  • Выполните команду меню оболочки Project/Test Properties и в поле Output Type вкладки Common Properties/General установите значение Windows Application, чтобы отключить окно командной строки приложения
  • В этом же окне свойств проекта установите параметр Startup Object в значение Test.AppClass

  • Задайте значение строки заголовка главной формы, разместив в конструкторе класса Start следующий код
  • using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class Start : Form
      {
        public Start()
        {
          // В этом конструкторе будем размещать
          // настройки формы
          this.Text = "Главная форма Лекция №15";
        }
      }
    }
  • Постройте проект и убедитесь, что пустая форма создается
  • Теперь мы поместим на главную форму элемент управления ListBox и заполним в нем строки заголовков упражнений, которые нам предстоит выполнить в этой лабораторной работе. Затем мы создадим обработчики событий выбора любого из упражнений, внутри которых будем запускать дочерние формы, соответствующие каждому отдельному упражнению. Упражнения будут демонстрировать возможности классов библиотеки .NET Framework и языка C#.

  • Откройте форму Start.cs в режиме View Designer и поместите на ней из панели Toolbox элемент управления ListBox. Выделите элемент управления ListBox и через панель Properties задайте значения его свойств в соответствии с таблицей
  • Значения свойств элемента управления ListBox
    Свойство Значение
    Name listBox
    Dock Fill
    Items 1) Избранные сведения о системе (SysInfoFirstTry)
      2) Разбиение текста на колонки (SysInfoColumn)
      3) Использование своего класса (SysInfoList)
      4) Добавление полос прокрутки (SysInfoPanel)
      5) Прокрутка без элементов управления (SysInfoScroll)
      6) События изменения настроек системы (SysInfoUpdate)
      7) Оптимизация работы функции прорисовки (SysInfoEfficient)
  • Растяните форму Start.cs так, чтобы все строки списка были полностью в ней видны
  • Запустите приложение.
  • Мы не увидели тех изменений, которые внесли в форму и ожидали увидеть. Как и прежде, она оказалось пустой. Исправим это.

  • Щелкните на форме правой кнопкой мыши и выполните команду View Code
  • Мы видим, что оболочка сгенерировала соответствующий код, когда мы создавали пользовательский интерфейс. Весь сгенерированный код разместился в функции InitializeComponent(), но вызова этой функции в коде нет.

  • Поместите в конструктор класса Start код вызова функции InitializeComponent()
  • public Start()
        {
          // В этом конструкторе будем размещать
          // настройки формы
          this.Text = "Главная форма Лекция №15";
          InitializeComponent();
        }
  • Постройте приложение - должен получиться такой результат
  • Теперь нам нужно создать обработчик, который бы при выборе любой строки списка запускал соответствующую форму.

  • Перейдите в режим View Designer формы Start и выделите компонент listBox. Установите в панели Properties режим Events и для события SelectedIndexChanged двойным щелчком на его поле значений создайте обработчик, который заполните так
  • private void listBox_SelectedIndexChanged(object sender, System.EventArgs e)
        {
          switch(listBox.SelectedIndex + 1)
          {
            case 1:
              SysInfoFirstTry  frm1 = new SysInfoFirstTry();
              frm1.Show();
              break;
            case 2:
              SysInfoColumn frm2 = new SysInfoColumn();
              frm2.Show();
              break;
            case 3:
              SysInfoList frm3 = new SysInfoList();
              frm3.Show();
              break;
            case 4:
              SysInfoPanel frm4 = new SysInfoPanel();
              frm4.Show();
              break;
            case 5:
              SysInfoScroll frm5 = new SysInfoScroll();
              frm5.Show();
              break;
            case 6:
              SysInfoUpdate frm6 = new SysInfoUpdate();
              frm6.Show();
              break;
            case 7:
              SysInfoEfficient frm7 = new SysInfoEfficient();
              frm7.Show();
              break;
          }
        }

    Применение секционных скобок для удобства редактирования

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

  • Перейдите в режим кода файла Start.cs и охватите функцию InitializeComponent() секционными скобками
  • #region Это секция кода создания списка
        private void InitializeComponent()
        {
        ...............................................
        }
         #endregion
  • То же самое проделаем с кодом, где будут размещаться объявления переменных-членов класса
  • #region Это секция переменных-членов класса
        private System.Windows.Forms.ListBox listBox;
         #endregion
  • Сверните созданные секции кода и насладитесь полученным "коммунальным" удобством. Убедитесь, что при наведении указателя мыши на свернутую секцию оболочка показывает содержащийся в ней код
  • Создание заготовок для дочерних форм

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

  • Добавьте в проект новый класс с именем Template командой Project/Add Class
  • Отредактируйте код файла Template.cs так
  • using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class Template : Form
      {
        public Template()
        {
        }
      }
    }
  • Сохраните изменения файла Template.cs на диске, выполнив команду File/Save All
  • В панели Solution Explorer выделите файл Template.cs, затем через меню Edit оболочки выполните команду Copy, а затем 7 раз подряд команду Paste. Таким образом мы получим 7 копий файла Template.cs
  • Выделите файл Template.cs и выполните команду Edit/Delete, чтобы удалить файл физически.
  • В панели Solution Explorer переименуйте полученные копии файлов в соответствии со следующим списком
  • SysInfoFirstTry.cs
  • SysInfoColumn.cs
  • SysInfoList.cs
  • SysInfoPanel.cs
  • SysInfoScroll.cs
  • SysInfoUpdate.cs
  • SysInfoEfficient.cs
  • Откройте каждый из только что переименованных файлов в редакторе кода и присвойте классам и их конструкторам имена соответствующих файлов, например
  • using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class SysInfoFirstTry : Form
      {
        public SysInfoFirstTry()
        {
        }
      }
    }
  • Постройте приложение и проверьте, что при щелчке на каждой строке списка создается соответствующая ей новая форма (пока еще пустая).
  • После таких грандиозных подготовительных операций перейдем к выполнению самих упражнений. В каждом упражнении мы будем ставить задачу и рассматривать ее решение с использованием библиотеки FCL (Framework Class Library) и средств C#.

    Упражнение 1.Получение информации о системе

    Основную информацию о системе, на которой выполняется программа, можно получить с помощью класса System.Windows.Forms.SystemInformation, вернее с помощью его 60 общедоступных статических свойств

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

    Разделение текстовых строк

    Расстояние между базовыми линиями двух строк по вертикали можно определить несколькими способами

  • float System.Drawing.Font.GetHeight(float dpi) - Возвращает высоту шрифта в пикселах для вывода на устройство с указанной вертикальной разрешающей способностью. Это может быть экран, принтер или другое устройство вывода.
  • float System.Drawing.Font.GetHeight() - Возвращает межстрочный интервал, в пикселах, установленного шрифта. Межстрочный интервал - вертикальное расстояние между базовыми линиями двух последовательных строк текста. Таким образом, межстрочный интервал включает в себя пустое пространство между строками вместе с высотой самого символа.
  • float System.Drawing.Font.GetHeight(System.Drawing.Graphics graphics) - Возвращает межстрочный интервал шрифта, указанный в текущем модуле графического объекта.
  • int System.Drawing.Font.Height - Содержит межстрочный интервал текущего шрифта.
  • int System.Windows.Forms.Control.FontHeight - Свойство устанавливает или возвращает размер шрифта.
  • В следующем примере мы воспользуемся свойством Font.Height формы для определения требуемого междустрочного расстояния для установленного в форме шрифта. Давайте выведем первые десять свойств класса System.Windows.Forms.SystemInformation.

  • Откройте файл SysInfoFirstTry в режиме кода и отредактируйте его так
  • using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class SysInfoFirstTry : Form
      {
        public SysInfoFirstTry()
        {
          // В этом конструкторе будем размещать
          // настройки формы
          this.Text = "Избранные сведения о системе";
          this.BackColor = SystemColors.Window;
          this.ForeColor = SystemColors.WindowText;
        }
      
        int i;
        // Заголовок этой функции набрать вручную!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
          System.Int32 y = 0; // Псевдонимом является int
      
          gr.DrawString((++i).ToString() + ") "
            + "ArrangeDirection: "
            + SystemInformation.ArrangeDirection.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "ArrangeStartingPosition: "
            + SystemInformation.ArrangeStartingPosition.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "BootMode: "
            + SystemInformation.BootMode.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "Border3DSize: "
            + SystemInformation.Border3DSize.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "BorderSize: "
            + SystemInformation.BorderSize.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "CaptionButtonSize: "
            + SystemInformation.CaptionButtonSize.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "CaptionHeight: "
            + SystemInformation.CaptionHeight.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "ComputerName: "
            + SystemInformation.ComputerName,
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "CursorSize: "
            + SystemInformation.CursorSize.ToString(),
            Font, brush, 0, y);
      
          y += Font.Height;
          gr.DrawString((++i).ToString() + ") "
            + "DbcsEnabled: "
            + SystemInformation.DbcsEnabled.ToString(),
            Font, brush, 0, y);
      
          base.OnPaint (e);
        }
      }
    }

    Экранный результат будет таким

    Попробуйте посворачивать и поразворачивать дочернюю форму SysInfoFirstTry. Можно заметить, что каждый раз нумерация пунктов свойств увеличивается. Исправим это

  • Добавьте в начало функции OnPaint() следующий код
  • protected override void OnPaint(PaintEventArgs e)
        {
          i = 0;
          ............................................
        }

    Из 60 свойств системы в библиотечном классе SystemInformation мы вывели только первые 10, чтобы повторяющийся код был не таким большим. Но можно воспользоваться циклом для вывода всех свойств класса и мы это сделаем чуть позже, дойдя до соответствующего места.

    Упражнение 2.Разбивка текста на колонки

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

    using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class SysInfoColumn : Form
      {
        public SysInfoColumn()
        {
          // Начальные настройки формы
          // Здесь ключевое слово this необязательно
          this.Text = "Разбиение текста на колонки";
          this.BackColor = SystemColors.Window;
          this.ForeColor = SystemColors.WindowText;
        }
      
        // Заголовок этой функции набрать вручную!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
          int linespacing = Font.Height;
          Single y = 0; // Позиция строк по вертикали
          SizeF sizeF = gr.MeasureString( // Измеряем текстовый блок 
            "ArrangeStartingPosition ", // Образец самого длинного текста
            Font);
          Single xCol = sizeF.Width;// Позиция второй колонки
      
          gr.DrawString("ArrangeDirection: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.ArrangeDirection.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("ArrangeStartingPosition: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.ArrangeStartingPosition.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("BootMode: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.BootMode.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("Border3DSize: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.Border3DSize.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("BorderSize: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.BorderSize.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("CaptionButtonSize: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.CaptionButtonSize.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("CaptionHeight: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.CaptionHeight.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("ComputerName: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.ComputerName,
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("CursorSize: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.CursorSize.ToString(),
            Font, brush, xCol, y);
      
          y += linespacing;
          gr.DrawString("DbcsEnabled: ",
            Font, brush, 0, y);
          gr.DrawString(SystemInformation.DbcsEnabled.ToString(),
            Font, brush, xCol, y);
      
          base.OnPaint (e);
        }
      }
    }

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

    Упражнение 3.Создание своего класса со свойствами "только для чтения"

    Класс SystemInformation очень полезный класс, к которому возможно разработчику придется часто обращаться, чтобы заставить программу автоматически приспосабливаться к конкретной платформе. На примере этого класса рассмотрим важное средство языка C# - создавать свойства классов.

    Если вспомнить принцип инкапсуляции объектно-ориентированного программирования, то он требует при проектировании классов сделать недоступными извне как можно больше переменных-членов, чтобы закрыть от клиента класса случайное изменение переменных. Поэтому чаще всего переменные-члены имеют модификатор доступа private.

    Но мы уже говорили, и это справедливо, что библиотека классов BCL ( BCL==FCL )является классическим примером объектно-ориентированного подхода к разработке классов. В то же время мы говорили о переменных-членах многих классов BCL, которые можно читать и даже писать, потому что они доступны клиенту (нашему коду) на уровне public. Есть ли здесь противоречие? Нет, потому что это не переменные-члены класса, а свойства-члены класса.

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

    Свойство C# состоит из двух блоков: get и set. Внутри этих блоков активно используется универсальный тип value, который определяется типом свойства. Свойство представляет собой именованный блок кода, имя которой является именем свойства, сам блок размещен внутри класса и состоит из разделов get и set. Вот простой пример синтаксиса

    public class MyClass
    {
    ...........................................
      private int xxx; // закрытая переменная - член
      
      // Свойство Xxx для xxx
      public int Xxx
      {
        get{return xxx;}
        set
        {
          // Здесь можно реализовать логику проверки
          // сохраняемого данного и любой другой контроль
          xxx = value; // value - ключевое слово, менять нельзя!
        }
      } // Конец блока кода объявления свойства
    ...........................................
    }

    Несколько замечаний.

  • Обращаться к объекту value можно только в теле блока set внутри определения свойства.
  • Если в своем классе разработчик создал хоть одно свойство, то ни один метод этого класса не должен называться с префикса get_ или set_.
  • Свойства, предназначенные для клиента, всегда должны быть public.
  • Для статических данных-членов класса нужно создавать статические свойства.
  • Если при объявлении свойства блок set опустить, то получится свойство "только для чтения".
  • Если в заголовке свойства указать ключевое слово readonly, то свойство будет доступно "только для чтения" несмотря на присутствие блока set.
  • Имена переменной и упаковывающего ее свойства внутри одного класса должны различаться как и любые отдельные именованные конструкции класса.
  • После такого отступления давайте упакуем свойства библиотечного класса SystemInformation в свойства нашего отдельного класса. Естественно, что они должны иметь статус "только для чтения".

  • Выполните команду меню оболочки Project/Add Class и присвойте имя класса SysInfoStrings
  • Ниже приведен соответствующий код этого класса (этот листинг я выделил зеленым фоном как вспомогательный класс)

    using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      // Упаковка 60 свойств класса SystemInformation BCL
      public class SysInfoStrings
      {
        // Свойство массива наименований
        public static String[] Labels
        {
          get
          {
            return new String[]
            {
              "ArrangeDirection", 
              "ArrangeStartingPosition", 
              "BootMode", 
              "Border3DSize",
              "BorderSize", 
              "CaptionButtonSize", 
              "CaptionHeight", 
              "ComputerName", 
              "CursorSize", 
              "DbcsEnabled", 
              "DebugOS", 
              "DoubleClickSize", 
              "DoubleClickTime", 
              "DragFullWindows", 
              "DragSize", 
              "FixedFrameBorderSize", 
              "FrameBorderSize", 
              "HighContrast", 
              "HorizontalScrollBarArrowWidth", 
              "HorizontalScrollBarHeight", 
              "HorizontalScrollBarThumbWidth", 
              "IconSize", 
              "IconSpacingSize", 
              "KanjiWindowHeight", 
              "MaxWindowTrackSize", 
              "MenuButtonSize", 
              "MenuCheckSize", 
              "MenuFont", 
              "MenuHeight", 
              "MidEastEnabled", 
              "MinimizedWindowSize", 
              "MinimizedWindowSpacingSize", 
              "MinimumWindowSize", 
              "MinWindowTrackSize", 
              "MonitorCount", 
              "MonitorsSameDisplayFormat", 
              "MouseButtons", 
              "MouseButtonsSwapped", 
              "MousePresent", 
              "MouseWheelPresent", 
              "MouseWheelScrollLines", 
              "NativeMouseWheelSupport", 
              "Network", 
              "PenWindows", 
              "PrimaryMonitorMaximizedWindowSize", 
              "PrimaryMonitorSize", 
              "RightAlignedMenus", 
              "Secure", 
              "ShowSounds", 
              "SmallIconSize", 
              "ToolWindowCaptionButtonSize", 
              "ToolWindowCaptionHeight", 
              "UserDomainName", 
              "UserInteractive", 
              "UserName", 
              "VerticalScrollBarArrowHeight", 
              "VerticalScrollBarThumbHeight", 
              "VerticalScrollBarWidth", 
              "VirtualScreen", 
              "WorkingArea" 
            }; // End of Array property Labels
          } // End of get
        } // end of property Labels
      
        // Свойство массива значений
        public static String[] Values
        {
          get
          {
            return new String[]
            {
              SystemInformation.ArrangeDirection.ToString(), 
              SystemInformation.ArrangeStartingPosition.ToString(), 
              SystemInformation.BootMode.ToString(), 
              SystemInformation.Border3DSize.ToString(),
              SystemInformation.BorderSize.ToString(), 
              SystemInformation.CaptionButtonSize.ToString(), 
              SystemInformation.CaptionHeight.ToString(), 
              SystemInformation.ComputerName.ToString(), 
              SystemInformation.CursorSize.ToString(), 
              SystemInformation.DbcsEnabled.ToString(), 
              SystemInformation.DebugOS.ToString(), 
              SystemInformation.DoubleClickSize.ToString(), 
              SystemInformation.DoubleClickTime.ToString(), 
              SystemInformation.DragFullWindows.ToString(), 
              SystemInformation.DragSize.ToString(), 
              SystemInformation.FixedFrameBorderSize.ToString(), 
              SystemInformation.FrameBorderSize.ToString(), 
              SystemInformation.HighContrast.ToString(), 
              SystemInformation.HorizontalScrollBarArrowWidth.ToString(), 
              SystemInformation.HorizontalScrollBarHeight.ToString(), 
              SystemInformation.HorizontalScrollBarThumbWidth.ToString(), 
              SystemInformation.IconSize.ToString(), 
              SystemInformation.IconSpacingSize.ToString(), 
              SystemInformation.KanjiWindowHeight.ToString(), 
              SystemInformation.MaxWindowTrackSize.ToString(), 
              SystemInformation.MenuButtonSize.ToString(), 
              SystemInformation.MenuCheckSize.ToString(), 
              SystemInformation.MenuFont.ToString(), 
              SystemInformation.MenuHeight.ToString(), 
              SystemInformation.MidEastEnabled.ToString(), 
              SystemInformation.MinimizedWindowSize.ToString(), 
              SystemInformation.MinimizedWindowSpacingSize.ToString(), 
              SystemInformation.MinimumWindowSize.ToString(), 
              SystemInformation.MinWindowTrackSize.ToString(), 
              SystemInformation.MonitorCount.ToString(), 
              SystemInformation.MonitorsSameDisplayFormat.ToString(), 
              SystemInformation.MouseButtons.ToString(), 
              SystemInformation.MouseButtonsSwapped.ToString(), 
              SystemInformation.MousePresent.ToString(), 
              SystemInformation.MouseWheelPresent.ToString(), 
              SystemInformation.MouseWheelScrollLines.ToString(), 
              SystemInformation.NativeMouseWheelSupport.ToString(), 
              SystemInformation.Network.ToString(), 
              SystemInformation.PenWindows.ToString(), 
              SystemInformation.PrimaryMonitorMaximizedWindowSize.ToString(), 
              SystemInformation.PrimaryMonitorSize.ToString(), 
              SystemInformation.RightAlignedMenus.ToString(), 
              SystemInformation.Secure.ToString(), 
              SystemInformation.ShowSounds.ToString(), 
              SystemInformation.SmallIconSize.ToString(), 
              SystemInformation.ToolWindowCaptionButtonSize.ToString(), 
              SystemInformation.ToolWindowCaptionHeight.ToString(), 
              SystemInformation.UserDomainName.ToString(), 
              SystemInformation.UserInteractive.ToString(), 
              SystemInformation.UserName.ToString(), 
              SystemInformation.VerticalScrollBarArrowHeight.ToString(), 
              SystemInformation.VerticalScrollBarThumbHeight.ToString(), 
              SystemInformation.VerticalScrollBarWidth.ToString(), 
              SystemInformation.VirtualScreen.ToString(), 
              SystemInformation.WorkingArea.ToString() 
            }; // End of Array property Values
          } // End of get
        } // end of property Values
      
        // Свойство. Содержит длину массива строк
        public static int Count
        {
          get
          {
            return Labels.Length;
          }
        }
      
        // Вспомогательный метод поиска длины
        // самой длинной строки
        private static Single MaxWidth(String[] strArray,
          Graphics graphics, Font font)
        {
          Single fMax = 0;
          foreach(String str in strArray)
            fMax = Math.Max(fMax, graphics.MeasureString(str, font).Width);
          return fMax;
        }
      
        // Метод, доступный для клиентов через имя класса
        public static Single MaxLabelWidth(Graphics graphics, Font font)
        {
          return MaxWidth(Labels, graphics, font);
        }
      
        // Метод, доступный для клиентов через имя класса
        public static Single MaxValueWidth(Graphics graphics, Font font)
        {
          return MaxWidth(Values, graphics, font);
        }
      } // End of class SysInfoStrings
    } // End of namespace Test

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

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

    using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class SysInfoList : Form
      {
        // Переменные-члены класса (поля класса)
        Single xCol;
        int linespacing;
      
        public SysInfoList()
        {
          // Начальные настройки формы
          this.Text = "Использование своего класса";
          this.BackColor = SystemColors.Window;
          this.ForeColor = SystemColors.WindowText;
          this.Width += 100;
          // Временно создать графический объект,
          // чтобы измерить длину пробела
          Graphics gr = this.CreateGraphics();
          // Замерим длину пробела
          SizeF sizeF = gr.MeasureString(" ", Font);
          // Определим позицию второй колонки
          xCol = sizeF.Width 
            + SysInfoStrings.MaxLabelWidth(gr, Font);
          // Освободить занимаемые временным объектов ресурсы
          gr.Dispose();
      
          linespacing = Font.Height;
        }
      
        // Заголовок этой функции набрать вручную!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
          // Длина массива свойств
          int count = SysInfoStrings.Count;
          // Локальные ссылки на массивы через статические свойства
          String[] strArrayLabels = SysInfoStrings.Labels;
          String[] strArrayValues = SysInfoStrings.Values;
      
          // Выводим все свойства на экран, 
          // хоть часть и не поместится
          for(int i = 0; i < count; i++)
          {
            gr.DrawString(strArrayLabels[i],
              Font, brush, 0, i * linespacing);
            gr.DrawString(strArrayValues[i],
              Font, brush, xCol, i * linespacing);
          }
      
          base.OnPaint (e);
        }
      }
    }

    Класс Control содержит наследуемый классом Form метод CreateGraphics(), который мы использовали для создания временного объекта, чтобы воспользоваться его методом MeasureString() для измерения длины пробела и текстового блока максимальной длины. Тем самым мы вычислили величину отступа, которую сохранили в поле класса xCol. Мы должны также вовремя позаботиться об освобождении памяти, занимаемой эти временным графическим объектом. Объект Graphics можно использовать для вывода графики в клиентской области окна программы, что мы и делаем в обработчике события OnPaint().

    Запустите приложение и должен получиться такой результат

    К сожалению все 60 свойств на форме не поместились, но дальше, когда мы "приделаем" к форме полосы прокрутки, - все получится.

    Полосы прокрутки

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

    Полосы прокрутки можно добавить к форме двумя способами:

  • Создать объекты типа VScrollBar и HScrollBar и разместить их в любой части клиентской области
  • Включить автопрокрутку для формы, задав ее свойству AutoScroll значение True. Это свойство действует на элементы управления, размещенные на форме, но не помещающиеся в ее видимой области.
  • Упражнение 4.Организация прокрутки с помощью элемента Panel

    Для создания полос прокрутки используем элемент Panel, который не имеет визуального представления, а служит для группирования визуальных элементов в качестве родителя. Мы добавим в наше приложение код создания элемента Panel по размерам клиентской области формы и включим в форме свойство автопрокрутки. Нужно создать обработчик события Paint для перерисовки элемента панель. Мы будем использовать в коде созданный нами ранее класс SysInfoStrings.

  • Добавьте в класс SysInfoPanel переменные-члены и код конструктора класса
  • using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class SysInfoPanel : Form
      {
        // Переменные-члены класса
        Single xCol;
        int linespacing;
      
        public SysInfoPanel()
        {
          // Начальные настройки формы
          this.Text = "Добавление полос прокрутки";
          this.BackColor = SystemColors.Window;
          this.ForeColor = SystemColors.WindowText;
          this.AutoScroll = true;// Включить автопрокрутку в форме
          // Надбавка к области для скролирования формы
          this.AutoScrollMargin = new Size(10, 10);
      
          // Временно создать, чтобы измерить
          Graphics gr = this.CreateGraphics();
          // Замерим длину пробела
          SizeF sizeF = gr.MeasureString(" ", Font);
          // Определим позицию второй колонки
          xCol = sizeF.Width 
            + SysInfoStrings.MaxLabelWidth(gr, Font);
          linespacing = Font.Height;
      
          // Создать объект типа Panel
          Panel panel = new Panel();
          // Указать панели в качестве родителя текущую форму
          // Каждый элемент управления должен иметь родителя!!!
          panel.Parent = this;
          // Создать делегата обработчика события Paint панели
          // Эту строку кода написать вручную!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
          panel.Paint +=new PaintEventHandler(panel_Paint);
          // Задать левый верхний угол панели нулевым
          // (совместить с левым верхним углом клиентской области формы)
          panel.Location = Point.Empty;
          // Настроить правый нижний угол панели на размер документа
          panel.Size = new Size(
            (int)Math.Ceiling(xCol + SysInfoStrings.MaxValueWidth(gr, Font)),
            (int)Math.Ceiling(linespacing * SysInfoStrings.Count));
          /*
          // Или то же самое по другому
          SizeF size_F = new SizeF(
            xCol + SysInfoStrings.MaxValueWidth(gr, Font),
            linespacing * SysInfoStrings.Count);
          panel.Size = Size.Ceiling(size_F);
          */
      
          // Освободить занимаемые временным графическим объектов ресурсы
          gr.Dispose();
        }
      }
    }
  • Пропишите выделенный красным код регистрации обработчика вручную, чтобы правильно создать заготовку обработчика события Paint панели
  • Заполните заготовку обработчика события Paint панели следующим кодом
  • private void panel_Paint(object sender, PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
      
          // Перекрасим фон панели, чтобы видеть ее размеры
          // в клиентской области формы
          gr.Clear(Color.Yellow);
      
          SolidBrush brush = new SolidBrush(this.ForeColor);
          // Длина массива свойств
          int count = SysInfoStrings.Count;
          // Локальные ссылки на массивы через статические свойства
          String[] strArrayLabels = SysInfoStrings.Labels;
          String[] strArrayValues = SysInfoStrings.Values;
      
          // Выводим все строки на элемент Panel. Если часть из них не  
          // поместится, то у элемента Panel появятся полосы прокрутки 
          for(int i = 0; i < count; i++)
          {
            gr.DrawString(strArrayLabels[i],
              Font, brush, 0, i * linespacing);
            gr.DrawString(strArrayValues[i],
              Font, brush, xCol, i * linespacing);
          }
        }

    Мы создали объект панель и настроили ее размеры с помощью временного графического объекта, который использовался для измерения измерения позиции второй колонки. Мы назначили панели родителя - форму, чтобы панель принимала сообщения от Windows, которые передаются системой самой форме. Размеры панели соответствуют размерам отображаемого на ней документа и, если панель не помещается в своем родителе, то форма генерирует полосы прокрутки в соответствии со значением ее свойства AutoScroll. Мы создали делегата и обработчик для события Paint панели, чтобы панель могла восстанавливать себя всякий раз после своего разрушения на экране.

    Метод Math.Ceiling() использовался для для дополнения вещественных значений до целого числа. Как альтернативу этому можно было создать структуру типа SizeF и использовать статический метод Size.Ceiling() для округления до целого

    // Или то же самое по другому
          SizeF size_F = new SizeF(
            xCol + SysInfoStrings.MaxValueWidth(gr, Font),
            linespacing * SysInfoStrings.Count);
          panel.Size = Size.Ceiling(size_F);

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

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

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

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

    Использование классов System.Windows.Forms.HScrollBar и System.Windows.Forms.VScrollBar - более универсальный подход к прокрутке. Оба этих класса наследуют от класса ScrollBar, который имеет ряд свойств для настройки. Свойства Minimum и Maximum, в частности, позволяют задать числовые значения, соответствующие крайним положениям ползунка, внутри которых и может работать полоса прокрутки.

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

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

    Хотя последняя наша программа не обрабатывает событие Scroll, она обрабатывает событие Paint для панели. Когда программа выводит информацию на панель, прорисовывается только видимая область панели. Но при каждой прокрутке на экране появляются новые области панели и срабатывает событие Paint панели, чтобы их прорисовать. То же происходит при появления панели на переднем плане после ее закрытия другими окнами.

    Механизм работы автопрокрутки

    Среди многих классов, наследуемых классом Form, есть класс ScrollableControl и именно в нем реализована автопрокрутка.

    Некоторые из свойств класса ScrollableControl с уровнем доступа "чтение/запись" приведены в таблице.

    Некоторые свойства класса ScrollableControl
    Свойство Тип Описание
    AutoScroll Boolean Включает автопрокрутку
    HScroll Boolean Включает/выключает видимость горизонтальной полосы прокрутки
    VScroll Boolean Включает/выключает видимость вертикальной полосы прокрутки
    AutoScrollMargin Size Устанавливает отступ справа и снизу от элемента управления
    AutoScrollMinSize Size Задает минимальную область прокрутки
    AutoScrollPosition Point Указывает положение полосы прокрутки

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

    Упражнение 5.Прокрутка без использования элементов управления

    Ключ к созданию автопрокрутки без использования дочерних элементов управления - в установке наследуемому формой свойству AutoScrollMinSize ненулевого значения (0,0 устанавливается по умолчанию). Обычно область прокрутки основывается на положении и размере элементов управления в клиентской области формы. Однако AutoScrollMinSize задает минимальную область прокрутки независимо от наличия на форме элементов управления. И, конечно, нужно задать наследуемому формой свойству AutoScroll значение true.

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

  • Откройте созданную нами ранее заготовку файла SysInfoScroll.cs
  • Заполните файл SysInfoScroll.cs следующими кодом
  • public class SysInfoScroll : Form
      {
        // Переменные-члены класса
        Single xCol;
        int linespacing;
      
        public SysInfoScroll()
        {
          // Начальные настройки формы
          this.Text = "Прокрутка без элементов управления";
          // this.BackColor = SystemColors.Window;
          BackColor = Color.MediumSpringGreen;
          this.ForeColor = SystemColors.WindowText;
      
          // Временно создать, чтобы измерить
          Graphics gr = this.CreateGraphics();
          // Замерим длину пробела
          SizeF sizeF = gr.MeasureString(" ", Font);
          // Определим позицию второй колонки
          xCol = sizeF.Width 
            + SysInfoStrings.MaxLabelWidth(gr, Font);
          linespacing = Font.Height;
      
          // Установить свойства автопрокрутки
          this.AutoScroll = true;// Включить автопрокрутку в форме
          // Надбавка к области для скролирования формы
          this.AutoScrollMargin = new Size(10, 10);
          // Установить размеры свойства равными документу
          SizeF size_F = new SizeF(
            xCol + SysInfoStrings.MaxValueWidth(gr, Font),
            linespacing * SysInfoStrings.Count);
          // Округлить до целого
          this.AutoScrollMinSize = Size.Ceiling(size_F);
      
          // Освободить занимаемые временным графическим объектов ресурсы
          gr.Dispose();
        }
      
        // Следующую строку написать вручную!!!
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
          // Длина массива свойств
          int count = SysInfoStrings.Count;
          // Локальные ссылки на массивы через статические свойства
          String[] strArrayLabels = SysInfoStrings.Labels;
          String[] strArrayValues = SysInfoStrings.Values;
      
          // Выводим все строки на клиентскую область формы. Если  
          // часть из них не поместится, то появятся полосы прокрутки 
          Point point = this.AutoScrollPosition; // Копируем в структуру
          for(int i = 0; i < count; i++)
          {
            gr.DrawString(strArrayLabels[i],
              Font, brush,
              point.X, point.Y + i * linespacing);
            gr.DrawString(strArrayValues[i],
              Font, brush, 
              point.X + xCol, point.Y + i * linespacing);
          }
      
          base.OnPaint (e);
        }
      }

    В данной программе мы вместо создания делегата и обработчика события Paint перегружаем метод OnPaint() формы, поэтому

  • Создайте вручную в конце класса SysInfoScroll код перегрузки метода OnPaint() формы и заполните его тело так
  • public class SysInfoScroll : Form
      {
      ..............................................................
    
        // Следующую строку написать вручную!!!
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
          // Длина массива свойств
          int count = SysInfoStrings.Count;
          // Локальные ссылки на массивы через статические свойства
          String[] strArrayLabels = SysInfoStrings.Labels;
          String[] strArrayValues = SysInfoStrings.Values;
      
          // Выводим все строки на клиентскую область формы. Если  
          // часть из них не поместится, то появятся полосы прокрутки 
          Point point = this.AutoScrollPosition; // Копируем в структуру
          for(int i = 0; i < count; i++)
          {
            gr.DrawString(strArrayLabels[i],
              Font, brush,
              point.X, point.Y + i * linespacing);
            gr.DrawString(strArrayValues[i],
              Font, brush, 
              point.X + xCol, point.Y + i * linespacing);
          }
      
          base.OnPaint (e);
        }
      }

    Генерируемая приведенным кодом форма будет выглядеть так

    При появлении полос прокрутки клиентская область программы сразу уменьшается ровно на столько, сколько станет занимать появившееся полоса прокрутки. Функция DrawString() объекта Graphics ничего не знает о полосах прокрутки и готова выводить информацию в клиентскую область с самого начала документа, поэтому позицию вывода документа в видимую часть клиентской области нужно корректировать. Это делается на основе свойства AutoScrollPosition.

    Упражнение 6. Отслеживание изменений в выводимой информации

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

    Но можно проинформировать приложение о сделанных изменениях при помощи событий класса Microsoft.Win32.SystemEvents.

    Класс содержит 12 событий, из которых мы воспользуемся только двумя:

  • UserPreferenceChanged - отслеживает большинство изменений, которые пользователь может сделать в Панели управления
  • DisplaySettingsChanged - инициируется при изменении параметров экрана
  • Откройте в режиме кода созданную нами ранее заготовку файла SysInfoUpdate.cs
  • Заполните класс SysInfoUpdate следующим кодом
  • public class SysInfoUpdate : Form
      {
        // Переменные-члены класса, видимые во всех методах-членах класса
        Single xCol;
        int linespacing, count;
        String[] strArrayLabels, strArrayValues;
      
        public SysInfoUpdate()
        {
          // Начальные настройки формы
          this.Text = "События изменения настроек системы";
          this.BackColor = SystemColors.Window;
          this.ForeColor = SystemColors.WindowText;
          this.AutoScroll = true;// Включить автопрокрутку в форме
      
          // Объявления (делегаты) событий
          SystemEvents.UserPreferenceChanged
            +=new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
          SystemEvents.DisplaySettingsChanged
            +=new EventHandler(SystemEvents_DisplaySettingsChanged);
      
          UpdateAllInfo(); // Наша функция, определена ниже!
        }
      
        private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
        {
          UpdateAllInfo();
          this.Invalidate();
        }
      
        private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
        {
          UpdateAllInfo();
          this.Invalidate();
        }
      
        private void UpdateAllInfo()
        {
          // Устанавливаем значения переменным уровня класса
          count = SysInfoStrings.Count;
          // Ссылки на массивы через статические свойства
          strArrayLabels = SysInfoStrings.Labels;
          strArrayValues = SysInfoStrings.Values;
      
          // Временно создать, чтобы измерить
          Graphics gr = this.CreateGraphics();
          // Замерим длину пробела
          SizeF sizeF = gr.MeasureString(" ", Font);
          // Определим позицию второй колонки
          xCol = sizeF.Width 
            + SysInfoStrings.MaxLabelWidth(gr, Font);
          linespacing = Font.Height;
      
          // Установить свойства автопрокрутки
          SizeF size_F = new SizeF(
            xCol + SysInfoStrings.MaxValueWidth(gr, Font),
            linespacing * count);
          // Округлить до целого
          this.AutoScrollMinSize = Size.Ceiling(size_F);
      
          // Освободить занимаемые временным графическим объектов ресурсы
          gr.Dispose();
        }
      
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
      
          Point point = this.AutoScrollPosition; // Копируем в структуру
          for(int i = 0; i < count; i++)
          {
            gr.DrawString(strArrayLabels[i],
              Font, brush,
              point.X, point.Y + i * linespacing);
            gr.DrawString(strArrayValues[i],
              Font, brush, 
              point.X + xCol, point.Y + i * linespacing);
          }
      
          base.OnPaint (e);
        }
      }
  • Добавьте в начало файла SysInfoUpdate.cs ссылку на пространство имен Microsoft.Win32
  • using Microsoft.Win32;
    using System;
    using System.Windows.Forms;
    using System.Drawing;

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

    Упражнение 7. Оптимизация работы функции прорисовки недействительной области

    Обработчик OnPaint() формы срабатывает всякий раз, когда клиентская область программы становится недействительной. Недействительной она становится, если окно заслоняется другим окном или при скролировании. В обработчике используется цикл, который перебирает все строки массива и заставляет их рисоваться в объекте Graphics даже и в том случае, когда они не видны на экране. Это лишняя трата ресурсов. Давайте заставим обработчик перерисовывать только видимые в данный момент на экране строки, а закулисную информацию прорисовывать впустую не нужно.

    Воспользуемся свойством ClipRectangle класса PaintEventArgs, которое определяет наименьший прямоугольник в координатах клиентской области, в который умещается недействительная зона.

    Ниже приведен код программы SysInfoEfficient, которая наследует форму SysInfoUpdate и переопределяет метод OnPaint() его более эффективной версией. В перегруженном методе вычисляются номера строк, заносимые в переменные iFirst и iLast, которые затем используются в цикле для отображения минимального количества строк, требуемых для прорисовки клиентской области.

    Перед циклом стоит выражение, которое с помощью метода Math.Min() предотвращает выход значения iLast за пределы общего количества отображаемых строк. Это может случится тогда, когда окно программы выше, чем требуется для отображения всех строк.

  • Откройте созданную нами ранее заготовку класса SysInfoEfficient.cs и наполните ее следующим кодом
  • using Microsoft.Win32;
    using System;
    using System.Windows.Forms;
    using System.Drawing;
      
    namespace Test
    {
      public class SysInfoEfficient : SysInfoUpdate // Наследует предыдущий!!!
      {
        public SysInfoEfficient()
        {
          // Начальные настройки формы
          this.Text = "7) Оптимизация работы функции прорисовки";
          this.Width += 50;
        }
      
        protected override void OnPaint(PaintEventArgs e)
        {
          Graphics gr = e.Graphics;
          SolidBrush brush = new SolidBrush(this.ForeColor);
      
          Point point = this.AutoScrollPosition;
      
          int iFirst = (e.ClipRectangle.Top - point.Y) / linespacing;
          int iLast = (e.ClipRectangle.Bottom - point.Y) / linespacing;
          iLast = Math.Min(count - 1, iLast);
      
          for(int i = iFirst; i <= iLast; i++)
          {
            gr.DrawString(strArrayLabels[i],
              Font, brush,
              point.X, point.Y + i * linespacing);
            gr.DrawString(strArrayValues[i],
              Font, brush, 
              point.X + xCol, point.Y + i * linespacing);
          }
        }
      }
    }
  • Запустите приложение и компилятор выдаст ошибки, что используемые в форме переменные не имеют требуемого уровня доступа.
  • В классе SysInfoEfficient мы используем код, наследуемый из класса SysInfoUpdate, но непозаботились о явном объявлении уровня доступа для переменных-членах базового класса. А они в C# по умолчанию считаются private и поэтому доступны только внутри класса, в котором объявлены. Исправим это

  • Откройте файл SysInfoUpdate.cs в режиме кода и добавьте для переменных-членов модификаторы, обеспечивающие доступ наследникам
  • public class SysInfoUpdate : Form
      {
        // Переменные-члены класса, видимые во всех методах-членах класса
        protected Single xCol;
        protected int linespacing, count;
        protected String[] strArrayLabels, strArrayValues;
        ................................................
      }
  • ,

    Запустите приложение и проверьте работоспособность формы SysInfoEfficient и функциональность во всех построенных нами режимах

  • Вернуться к учебному плану