Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 15:

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

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

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

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


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

  1. UserPreferenceChanged - отслеживает большинство изменений, которые пользователь может сделать в Панели управления
  2. 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);
    }
  }
Листинг 15.22. Обновления вывода в форме при изменении настроек системы
  • Добавьте в начало файла SysInfoUpdate.cs ссылку на пространство имен Microsoft.Win32
using Microsoft.Win32;
using System;
using System.Windows.Forms;
using System.Drawing;
Листинг 15.23. Добавление в файл ссылки на пространство имен

Проверить приложение можно в окне панели задач на свойстве "Автоматически убирать с экрана", разнеся это окно и окно формы. При щелчке на кнопке "Применить" будет меняться значение последнего свойства 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);
      }
    }
  }
}
Листинг 15.24. Код прорисовки только видимых строк (SysInfoEfficient)
  • Запустите приложение и компилятор выдаст ошибки, что используемые в форме переменные не имеют требуемого уровня доступа.

В классе SysInfoEfficient мы используем код, наследуемый из класса SysInfoUpdate, но непозаботились о явном объявлении уровня доступа для переменных-членах базового класса. А они в C# по умолчанию считаются private и поэтому доступны только внутри класса, в котором объявлены. Исправим это

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

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


Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ