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

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

В главе 4 мы обсуждали различия между элементом управления «полоса прокрутки» и полосами прокрутки, которые создаются автоматически и являются принадлежностью любого класса-наследника ScrollableControl (например, Form и Panel). При использовании автоматической прокрутки надо указать размер клиентской области, и полосы прокрутки автоматически появятся справа и внизу клиентской области. С автоматическими полосами прокрутки события не связаны — по крайней мере события, которые может получить приложение.

Класс ScrollBar является абстрактным классом-наследником Control:


Рис. 12.18.

У ScrollBar два наследника: горизонтальная полоса прокрутки (HScrollBar) и вертикальная полоса прокрутки (VScrollBar). Их можно размещать в любом месте клиентской области и делать любого размера. Хотя для вертикальных полос прокрутки устанавливается ширина по умолчанию, а для горизонтальных — высота по умолчанию, вы можете создавать как очень тонкие, так и очень толстые полосы прокрутки. Однако для полос прокрутки нельзя установить фоновый цвет или цвет шрифта.

Чтобы использовать общие термины для вертикальных и горизонтальных полос прокрутки, я буду говорить о толщине и длине. Толщина — это высота горизонтальной полосы и ширина вертикальной, а длина — ширина горизонтальной и высота вертикальной. По умолчанию толщина полос прокрутки стандартна и равна SystemInformation.VerticalScrollBarWidth и SystemInformation.HorizontalScrollBarHeight.

Вот пять основных свойств, добавленных к Control классом ScrollBar.


Свойства ScrollBar (выборочно)



Тип Свойство Доступ Описание

int Value Чтение/запись Изменяется от Minimum до (Maximum + 1 - LargeChange)
int Minimum Чтение/запись По умолчанию 0
int Maximum Чтение/запись По умолчанию 100
int SmallChange Чтение/запись По умолчанию 1
int LargeChange Чтение/запись По умолчанию 10


Свойство Value представляет текущую позицию ползунка на полосе. Оно изменяется от Minimum до... ну, почти до Maximum. При щелчках по стрелкам, расположенным на концах полосы свойство Value изменяется на SmallChange. При щелчке по полосе с любой стороны от ползунка свойство Value изменяется на LargeChange.

Почему же Value изменяется в диапазоне от Minimum до (Maximum + 1 - LargeChange)? Представьте документ, содержащий 500 строк текста. Свойство Minimum устанавливается 0 и Maximum в 499. Клиентская область может вместить 25 строчек текста. SmallChange присваивается 1 (одна строка текста), a LargeChange — 25.

Размер ползунка по отношению к длине полосы прокрутки зависит от отношения LargeChange к Maximum. Это доля видимой части документа.

Если Value равно 0, вы просматриваете начало документа, т.е. строки с 0 по 24, если номера строк в документе начинаются с 0. Когда Value равно 1, вы просматриваете строки с 1 по 25. И когда Value равно 475, вы просматриваете строки с 475 по 499. Это конец документа, и Value не должно более увеличиваться. Вот почему Value не становится больше, чем (Maximum + 1 - LargeChange).

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

В ScrollBar реализованы два события:


События ScrollBar



Событие Метод Делегат Аргументы

ValueChanged OnValueChanged EventHandler EventArgs
Scroll OnScroll ScrollEventHandler ScrollEventArgs


Событие ValueChanged происходит, когда на самом деле изменяется свойство Value. Если на вашу клавиатуру ляжет кошка, вам не придется тратить время на обработку огромного количества ненужных событий.

Событие ValueChanged происходит не только, когда пользователь что-то делает с полосой прокрутки, но и когда значение свойства Value изменяется программно. Событие Scroll не происходит, если свойство Value было изменено в программе.

Более того, событие Scroll может дать гораздо больше информации о том, что именно произошло с полосой прокрутки. Может, вам и не придется никогда использовать событие Scroll, но оно необходимо, если события ValueChanged недостаточно. Обработчик события Scroll принимает объект типа ScrollEventArgs, имеющий такие свойства:


Свойства ScrollEventArgs



Тип Свойство Доступ

int NewValue Чтение/запись
ScrollEventType Type Чтение/запись


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


Перечисление ScrollEventType



Член Значение Описание

SmallDecrement 0 Мышь: левая кнопка или верхняя стрелка.
Клавиатура: Клавиши «стрелка-влево» или «стрелка-вверх»
SmallIncrement 1 Мышь: правая кнопка или нижняя стрелка.
Клавиатура: Клавиши «стрелка-вправо» или «стрелка-вниз»
LargeDecrement 2 Мышь: щелчок в левой или верхней области.
Клавиатура: Клавиша Page Up
LargeIncrement 3 Мышь: щелчок в правой или нижней области.
Клавиатура: Клавиша Page Down
ThumbPosition 4 Мышь: отпускание кнопки на бегунке
ThumbTrack 5 Мышь: нажатие кнопки на бегунке или перемещение
First 6 Клавиатура: клавиша Home
Last 7 Клавиатура: клавиша End
EndScroll 8 Операция прокрутки завершена


К примеру, полоса прокрутки имеет фокус ввода, и вы нажимаете и отпускаете одну из клавиш управления курсором. Или щелкаете по стрелке на полосе прокрутки. Вначале вы получите событие Scroll, в котором поле Туре равно ScrollEventArgsType.SmallIncrement или ScrollEventArgsType.SmallDecrement. Затем вы получите событие ValueChanged и затем еще одно событие Scroll, в котором на этот раз поле Туре будет равно ScrollEventArgsType.EndScroll. Если sb — объект типа ScrollBar, a sea — объект типа ScrollEventArgs, последовательность событий будет такова:



Событие sb.Value sea.Type sea.NewValue

Scroll N SmallIncrement N + 1
ValueChanged N + 1    
Scroll N + 1 EndScroll N + 1


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



Событие sb.Value sea.Type sea.NewValue

Scroll N SmallIncrement N + 1
ValueChanged N + 1    
Scroll N + 1 SmallIncrement N + 2
ValueChanged N + 2    
Scroll N + 2 SmallIncrement N + 3
ValueChanged N + 3    
Scroll N + 3 SmallIncrement N + 4
ValueChanged N + 4    
Scroll N + 4 EndScroll N + 4


Вы не будете получать событие ValueChanged, если Value достигнет своего максимального или минимального значения. При нажатии клавиши End в общем случае вы получите такую последовательность событий:



Событие sb.Value sea.Type sea.NewValue

Scroll N Last sbMaximum
ValueChanged sbMaximum    
Scroll sbMaximum EndScroll sbMaximum


Однако если бегунок при нажатии клавиши End уже находился в конце полосы прокрутки, последовательность событий будет такой:



Событие sb.Value sea.Type sea.NewValue

Scroll sbMaximum Last sbMaximum
Scroll sbMaximum EndScroll sbMaximum


Если «схватить» бегунок мышью и перемещать его, последовательность событий будет такой:



Событие sb.Value sea.Type sea.NewValue

Scroll N ThumbTrack N
Scroll N ThumbTrack N + 1
ValueChanged N + 1    
Scroll N + 1 ThumbTrack N + 2
ValueChanged N + 2    
Scroll N + 2 ThumbTrack N + 3
ValueChanged N + 3    
Scroll N + 3 ThumbTrack N + 4
ValueChanged N + 4    
Scroll N + 4 ThumbTrack N + 4
Scroll N + 4 EndScroll N + 4


В зависимости от скорости, с которой вы перемещаете ползунок, вы можете получать события не со всеми значениями. И на самом деле необходимость установки обработчика события Scroll вместо события ValueChanged определяется тем, как программа должна реагировать на быстрое перемещение ползунка. Попробуйте «схватить» ползунок мышью и резко «подергать» его. Если ваша программа не может с этим справиться, возможно, стоит установить обработчик события Scroll вместо события ValueChanged. Например, можно игнорировать все значения Туре, кроме EndScroll.

Программа ColorScroll использует три полосы прокрутки: Red, Green и Blue, — позволяющие выбирать сочетание цветов. Результирующий цвет устанавливается в качестве фонового цвета для формы. Чтобы все надписи и полосы прокрутки были видимы, добавлен элемент управления Panel белого цвета, занимающий половину клиентской области формы. Все прочие элементы управления — 3 полосы прокрутки и 6 надписей — являются дочерними элементами Panel.

ColorScroll.cs

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

  class ColorScroll: Form
  {
      Panel        panel;
      Label[]      alabelName  = new Label[3];
      Label[]      alabelValue = new Label[3];
      VScrollBar[] avscroll    = new VScrollBar[3];

      public static void Main()
      {
          Application.Run(new ColorScroll());
      }
      public ColorScroll()
      {
          Text = "Color Scroll";

          Color[] acolor = { Color.Red, Color.Green, Color.Blue };

          // Создаем панель

          panel = new Panel();
          panel.Parent = this;
          panel.Location = new Point(0, 0);
          panel.BackColor = Color.White;

          // Цикл для трех цветов

          for (int i = 0; i < 3; i++)
          {
              alabelName[i] = new Label();
              alabelName[i].Parent = panel;
              alabelName[i].ForeColor = acolor[i];
              alabelName[i].Text = "&" + acolor[i].ToKnownColor();
              alabelName[i].TextAlign = ContentAlignment.MiddleCenter;

              avscroll[i] = new VScrollBar();
              avscroll[i].Parent = panel;
              avscroll[i].SmallChange = 1;
              avscroll[i].LargeChange = 16;
              avscroll[i].Minimum  = 0;
              avscroll[i].Maximum = 255 + avscroll[i].LargeChange - 1;
              avscroll[i].ValueChanged += 
                             new EventHandler(ScrollOnValueChanged);
              avscroll[i].TabStop = true;

              alabelValue[i] = new Label();
              alabelValue[i].Parent = panel;
              alabelValue[i].TextAlign = ContentAlignment.MiddleCenter;
          }
          Color color = BackColor;
          avscroll[0].Value = color.R;  // Генерируем событие ValueChanged
          avscroll[1].Value = color.G;
          avscroll[2].Value = color.B;

          OnResize(EventArgs.Empty);
      }
      protected override void OnResize(EventArgs ea)
      {
          base.OnResize(ea);

          int cx = ClientSize.Width;
          int cy = ClientSize.Height;
          int cyFont = Font.Height;

          panel.Size = new Size(cx / 2, cy);

          for (int i = 0; i < 3; i++)
          {
              alabelName[i].Location = new Point(i * cx / 6, cyFont / 2);
              alabelName[i].Size = new Size(cx / 6, cyFont);

              avscroll[i].Location = new Point((4 * i + 1) * cx / 24,
                                               2 * cyFont);
              avscroll[i].Size = new Size(cx / 12, cy - 4 * cyFont);

              alabelValue[i].Location = new Point(i * cx / 6,
                                                  cy - 3 * cyFont / 2);
              alabelValue[i].Size = new Size(cx / 6, cyFont);
          }
      }
      void ScrollOnValueChanged(Object obj, EventArgs ea)
      {
          for (int i = 0; i < 3; i++)
              if((VScrollBar) obj == avscroll[i])
                  alabelValue[i].Text = avscroll[i].Value.ToString();

          BackColor = Color.FromArgb(avscroll[0].Value,
                                     avscroll[1].Value,
                                     avscroll[2].Value);
      }
  }

Конструктор создает все элементы управления и сохраняет их в полях. Полосы прокрутки должны предоставлять значения от 0 до 255. Заметьте: я устанавливаю LargeChange в 16 и затем присваиваю свойству Maximum значение 255 плюс LargeChange минус 1, что равно 270. При этом конструктор не определяет размеры и местоположение элементов управления. Это ответственное задание возложено на плечи метода OnResize. Местоположение и размеры элементов управления рассчитываются в зависимости от размера клиентской области и высоты шрифта. Вертикальные полосы прокрутки изменяют ширину при изменении размера формы. (Для достижения этого эффекта я пытался использовать «заякоривание», но у меня ничего не вышло.) Вот окно этой программы при обычных размерах:


Рис. 12.19.

Создаются два набора надписей: три элемента управления Label хранятся в массиве alabelName, а их свойствам Text заданы значения Red, Green и Blue, при этом их свойству ForeColor присвоен соответствующий цвет. Для этих целей я использую массив acolor. Если вызвать метод ToString объекта Color, результатом будет что-то вроде Color [Red]. Но если объект Color является членом перечисления KnownColor, объект Color можно преобразовать в значение KnownColor методом ToKnownColor. Значение перечисления преобразуется в строки вида Red.

Обычно свойство TabStop, унаследованное от Control, для полос прокрутки устанавливается в false. В ColorScroll ему присвоено значение true. Кроме того, в начале текста надписей Red, Green и Blue стоит амперсанд. Так как фокус ввода на метки не переходит, нажатие подчеркнутой клавиши переводит фокус ввода на следующий элемент управления, способный получить фокус ввода. Таким образом, фокус на полосы прокрутки можно перевести как клавишей Tab, так и клавишами R, G или В.

При перемещении ползунков происходит событие ValueChanged и вызывается метод ScrollOnValueChanged. В этом методе параметр obj приводится к объекту VScrollBar, а затем осуществляется поиск в массиве avscroll. Индекс совпадающего элемента применяется для задания текста соответствующего элемента управления Label, отображающего значение (alabelValue). Метод завершается пересчитыванием цвета фона формы в зависимости от значений трех полос прокрутки.

Будьте осторожны при задании в программах свойства Value полосы прокрутки! Вначале конструктор ColorScroll завершался тремя инструкциями, инициализирующими три полосы прокрутки значениями, составляющих цвета фона формы:

  avscroll[0].Value = BackColor.R;
  avscroll[1].Value = BackColor.G;
  avscroll[2].Value = BackColor.B;

Однако первое же выражение вызывало событие ValueChanged, что приводило к вызову ScrollOnValueChanged, а в этом методе устанавливался цвет фона формы на основе значений Value трех полос прокрутки. Но так как полосы Green и Blue к этому моменту еще не инициализированы, то зеленая и голубая составляющая цвета фона — BackColor.G и BackColor.B — устанавливались в 0. Проблему решило сохранение цвета в отдельной переменной Color и использование ее для установки свойства Value:

  Color color = BackColor;
  avscroll[0].Value = color.R;
  avscroll[1].Value = color.G;
  avscroll[2].Value = color.B;

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

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