netlib.narod.ru | < Назад | Оглавление | Далее > |
Указание растрового изображения — не единственный способ отображать нестандартные кнопки. Можно полностью переопределить ее прорисовку, установив обработчик события Paint кнопки. Иногда такой подход называют прорисовкой владельцем (owner draw): ваша программа является владельцем кнопки, и именно она, а не сама кнопка, отвечает за рисование.
Прорисовка владельцем не так проста, как применение растровых изображений, — ее лучше использовать, если изображением является простой векторный рисунок, просто потому, что векторная графика лучше масштабируется.
Прорисовку владельцем облегчает то, что совсем не нужно самостоятельно прорисовывать каждую деталь. Пространство имен System.Windows.Forms содержит класс ControlPaint, включающий набор статических методов для прорисовки фрагментов общеупотребительных элементов управления. Так, для прорисовки кнопок могут пригодиться статические перегружаемые методы:
Статические методы ControlPaint (выборочно)
void DrawButton(Graphics grfx, int x, int y, int cx, int cy, ButtonState bs) |
void DrawButton(Graphics grfx, Rectangle rect, ButtonState bs) |
void DrawFocusRectangle(Graphics grfx, Rectangle rect) |
void DrawFocusRectangle(Graphics grfx, Rectangle rect, Color clr, Color clr) |
Метод DrawButton на самом деле просто рисует границу вокруг кнопки. Перечисление ButtonState представляет собой набор битовых флагов, управляющих состоянием кнопки:
Перечисление ButtonState
Член | Значение |
Normal | 0x0000 |
Inactive | 0x0100 |
Pushed | 0x0200 |
Checked | 0x0400 |
Flat | 0x4000 |
All | 0x4700 |
Я использую оба этих метода в программе OwnerDrawButtons:
OwnerDrawButtons.cs
//------------------------------------------------- // OwnerDrawButtons.cs (C) 2001 by Charles Petzold //------------------------------------------------- using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; class OwnerDrawButtons: Form { readonly int cxImage, cyImage; readonly int cxBtn, cyBtn, dxBtn; readonly Button btnLarger, btnSmaller; public static void Main() { Application.Run(new OwnerDrawButtons()); } public OwnerDrawButtons() { Text = "Owner-Draw Buttons"; ResizeRedraw = true; cxImage = 4 * Font.Height; cyImage = 4 * Font.Height; cxBtn = cxImage + 8; cyBtn = cyImage + 8; dxBtn = Font.Height; btnLarger = new Button(); btnLarger.Parent = this; btnLarger.Size = new Size(cxBtn, cyBtn); btnLarger.Click += new EventHandler(ButtonLargerOnClick); btnLarger.Paint += new PaintEventHandler(ButtonOnPaint); btnSmaller = new Button(); btnSmaller.Parent = this; btnSmaller.Size = new Size(cxBtn, cyBtn); btnSmaller.Click += new EventHandler(ButtonSmallerOnClick); btnSmaller.Paint += new PaintEventHandler(ButtonOnPaint); OnResize(EventArgs.Empty); } protected override void OnResize(EventArgs ea) { base.OnResize(ea); btnLarger.Location = new Point(ClientSize.Width / 2 - cxBtn - dxBtn / 2, (ClientSize.Height - cyBtn) / 2); btnSmaller.Location = new Point(ClientSize.Width / 2 + dxBtn / 2, (ClientSize.Height - cyBtn) / 2); } void ButtonLargerOnClick(object obj, EventArgs ea) { Left -= (int)(0.05 * Width); Top -= (int)(0.05 * Height); Width += (int)(0.10 * Width); Height += (int)(0.10 * Height); } void ButtonSmallerOnClick(object obj, EventArgs ea) { Left += (int)(Width / 22f); Top += (int)(Height / 22f); Width -= (int)(Width / 11f); Height -= (int)(Height / 11f); } void ButtonOnPaint(object obj, PaintEventArgs pea) { Button btn = (Button) obj; Graphics grfx = pea.Graphics; ControlPaint.DrawButton(grfx, 0, 0, cxBtn, cyBtn, (btn == (Button) GetChildAtPoint( PointToClient( MousePosition))) && btn.Capture ? ButtonState.Pushed : ButtonState.Normal); GraphicsState grfxstate = grfx.Save(); grfx.TranslateTransform((cxBtn - cxImage) / 2, (cyBtn - cyImage) / 2); if (btn == btnLarger) DrawLargerButton(grfx, cxImage, cyImage); else DrawSmallerButton(grfx, cxImage, cyImage); grfx.Restore(grfxstate); if (btn.Focused) ControlPaint.DrawFocusRectangle(grfx, new Rectangle((cxBtn - cxImage) / 2 + cxImage / 16, (cyBtn - cyImage) / 2 + cyImage / 16, 7 * cxImage / 8, 7 * cyImage / 8)); } void DrawLargerButton(Graphics grfx, int cx, int cy) { Brush brush = new SolidBrush(btnLarger.ForeColor); Pen pen = new Pen(btnLarger.ForeColor); grfx.TranslateTransform(cx / 2, cy / 2); for (int i = 0; i < 4; i++) { grfx.DrawLine(pen, 0, 0, cx / 4, 0); grfx.FillPolygon(brush, new Point[] { new Point(cx / 4, -cy / 8), new Point(cx / 2, 0), new Point(cx / 4, cy / 8)}); grfx.RotateTransform(90); } } void DrawSmallerButton(Graphics grfx, int cx, int cy) { Brush brush = new SolidBrush(btnSmaller.ForeColor); Pen pen = new Pen(btnSmaller.ForeColor); grfx.TranslateTransform(cx / 2, cy / 2); for (int i = 0; i < 4; i++) { grfx.DrawLine(pen, 3 * cx / 8, 0, cx / 2, 0); grfx.FillPolygon(brush, new Point[] { new Point(3 * cx / 8, -cy / 8), new Point( cx / 8, 0), new Point(3 * cx / 8, cy / 8)}); grfx.RotateTransform(90); } } }
Для обеих кнопок я устанавливаю обработчик события Paint с именем ButtonOnPaint. В начале обработчика вызывается DrawButton. Длинное выражение в середине вызова метода определяет, каким должен быть флаг, передаваемый в качестве последнего аргумента: ButtonState.Normal или ButtonState.Pushed. Посмотрев на обычную кнопку, вы обнаружите, что она переключается в нажатое состояние, когда вы щелкаете по ней. Кнопка остается в этом состоянии, пока кнопка мыши не будет отпущена или указатель мыши не покинет кнопку. Если снова поместить указатель на кнопку, все еще не отпуская кнопку мыши, то кнопка вернется в нажатое состояние. Реализованная в этой программе логика заключается в проверке свойства Capture кнопки (равно ли оно true), а также находится ли при этом указатель над кнопкой. Это работает!
Таким способом метод разбирается с прорисовкой границы. Затем вызываются методы DrawLargerButton и DrawSmallerButton, чтобы прорисовать внутренние области. Однако перед ними вызывается метод TranslateTransform, чтобы сдвинуть точку привязки графики внутрь границы. Я обрамляю перенос точки привязки вызовами методов Save и Restore объекта Graphics, чтобы они не влияли на вызов DrawFocusRectangle.
Методы DrawLargerButton и DrawSmallerButton прорисовывают внутренние области. Заметьте: сначала они устанавливают точку привязки в центр изображения, а затем рисуют одно и то же изображение 4 раза, каждый раз поворачивая его на 90°. (Вообще-то для рисования растровых изображений из BitmapButtons я использовал эти же два метода.)
Обработка ButtonOnPaint завершается вызовом DrawFocusRectangle, если кнопка имеет фокус ввода. Этот метод рисует обычную прямоугольную пунктирную рамку.
netlib.narod.ru | < Назад | Оглавление | Далее > |