Удачные диаграммы: Вторая помощь в круговых диаграммах. Часть 3
Автор: Ged Mead
Перевод: Виталий Готовцов
[Оригинал Статьи] [Обсудить в форуме]
Скачать исходный код к статье
Разметка формы
Поскольку эти статьи имеют целью показать вам, как использовать классы Graphics, я не буду углубляться в не-графический материал там, где это возможно. Так, чтобы сократить процесс создания формы Windows и его элементов управления я подключил проект, содержащий скелетную форму (т.е. содержащую элементы управления, но без графического кода. Вы найдете ее в папке, названной «Skeleton» («Скелет»), в приложенном zip-файле.
Форма и ее элементы управления выглядят так:
Конечно, если вы предпочитаете создать форму сами – действуйте. Я буду сообщать вам используемые названия и другие детали для элементов управления, по мере приближения к ним.
Давайте начнем писать код
Вставьте эти операторы в начало кода формы:
Option Strict On
Imports System.Drawing.Drawing2D
Imports System.Collections
Как в предыдущих статьях, для разделения данных и массива, хранящего их, используем Структуру (Structure). Это делается быстро и легко, и должно уже быть известно вам к настоящему времени.
Structure GraphData
Dim Name As String
Dim Amount As Decimal
Dim Clr As Color
Dim Pattern As HatchStyle
End Structure
' Массив для хранения данных, которые вводятся пользователем
Dim UserData As New ArrayList
Эта структура подобна той, которую мы создавали в Части 1 – первые три поля (Name, Amount и Clr) представляют те же элементы. Дополнительный элемент (Pattern) будет содержать выбранные пользователем HatchStyles (Стили штриховок). Framework предлагает диапазон из более чем 50 стилей – в основном они предлагают выбор цветных штриховок. Картинка ниже показывает несколько типовых образцов:
Временные переменные
Из того, что мы делали в частях 1 и 2 вы должны знать, что мы будем создавать экземпляры User Defined Value Type (Определенные Пользователем Типы Значений) (GraphData) и будем присваивать значения каждому из четырех полей. В этой версии мы позволим пользователю делать это во время выполнения.
Так же нам нужна пара переменных, которые временно будут содержать значение элементов Цвет (Color) и HatchStyle, пока пользователь будет выбирать остальные значения. Добавьте эти строки в область деклараций, где вы создавали массив:
Dim clrPicked As Drawing.Color = Color.Black
Dim hatchPicked As HatchStyle = HatchStyle.DarkHorizontal
Вы увидите, что в код включены значения по умолчанию (Black и DarkHorizontal (Черный и Темный горизонтальный)). Это сделано для того, чтобы избежать ошибки, если пользователь забудет выбрать какой-либо из этих элементов.
Элементы пользовательского ввода
TextBox
Если игнорировать ярлыки, первым элементом управления на форме будет TextBox. Если вы создаете свой проект, его нужно назвать txtName.
NumericUpDown
Следующий элемент – NumericUpDown, названный nudValue. Вы можете установить его Min, Max и значение, которое захотите сами.
Кнопка «Select Color»
Далее на форме находится кнопка. Я назвал ее btnColor, но имя не имеет значения.
ColorDialog
Событие нажатия на кнопку открывает ColorDialog, из которого пользователь может выбрать цвет для штриховки. Добавьте ColorDialog в вашу форму, если это необходимо (он уже включен в предложенный скелетный проект).
Следующий код содержит выбранный пользователем цвет:
Private Sub btnColor_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnColor.Click
' Получает выбранный пользователем цвет
Dim ColorPicker As New ColorDialog
Dim Choice As DialogResult = ColorPicker.ShowDialog
If Choice <> DialogResult.Cancel Then
clrPicked = ColorPicker.Color
End If
End Sub
ComboBox «Select Pattern»
Четвертый элемент пользовательского ввода – ComboBox, который следует назвать cboPattern.
Этот ComboBox позволяет пользователю выбрать любой HatchStyle для использования в круговой диаграмме. Поэтому мы должны заполнить комбо этим списком. К счастью, VB.NET дает нам крайне легкий способ сделать это.
Поместите этот код в событие Form_Load, чтобы получить названия стилей штриховки и наполнить ими ComboBox:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Заполняем combobox всеми доступными HatchStyles
Dim patts() As String
patts = System.Enum.GetNames(GetType(HatchStyle))
cboPattern.Items.AddRange(patts)
End Sub
Способность получить эти Enumeration (Перечисления) и манипулировать ими, как диапазоном, может быть очень полезной. Альтернативой является ручное добавление каждого значение в код каждой строки с помощью «Items.Add», что не очень привлекательно!
Теперь пользователь может выбрать HatchStyle из ComboBox и когда это происходит, мы записываем выбор пользователя в переменную, которую мы создали для этого раньше, и назвали Hatchpicked. Код в событии комбобокса SelectedIndexChanged выглядит так:
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles cboPattern.SelectedIndexChanged
hatchPicked = CType(System.Enum.Parse(GetType(HatchStyle), _
cboPattern.Text), HatchStyle)
End Sub
Фактически этот код проверяет выбор пользователя, который является строковым типом в комбобоксе, находит его соответствие в перечислении HatchStyle и сохраняет его.
Получение данных
Мы предлагаем пользователю ввести четыре элемента данных – Название, Стоимость, Цвет и Шаблон – для каждого сегмента круговой диаграммы. Если они не будут введены, то будет использовано «дефолтное» значение. Если пользователь доволен «текущими настройками» данных (то есть тем, что станет с одним сегментом диаграммы), щелчок на кнопке Confirm запустит выполнение следующего кода. Он возьмет четыре части данных и поместит их в массив, как один сложный элемент GraphData.
Private Sub btnConfirm_Click(ByVal sender As _
System.Object, ByVal e As _
System.EventArgs) Handles btnConfirm.Click
' Добавляет самый последний элемент данных в массив
If txtName.Text = "" Then txtName.Text = "No Name"
Dim gd As New GraphData
gd.Name = txtName.Text
gd.Amount = nudValue.Value
gd.Clr = clrPicked
gd.Pattern = hatchPicked
UserData.Add(gd)
End Sub
Я уверен, что вы определите строку, которая проверяет, что пользователь действительно ввел Название (Name) и, если не ввел, то предлагает текст для отображения.
Создание диаграммы
Кнопка для рисования или перерисовки диаграммы конечно эта:
Я назвал ее btnDraw, но это не имеет значения.
Если вы не используете скелетное решение, то не забудьте добавить элемент Panel в ваш проект и назовите его pnlChart. На изображении в начале этой статьи панель – это белое пространство в правой части формы.
Invalidate
Для того чтобы диаграмма была нарисована или перерисована в панели когда пользователь щелкнет на кнопке, нам нужно написать код, который выполнит это действие. Это очень легко и метод Invalidate вскоре может стать вашим лучшим другом.
- Invalidate заставляет элемент управления перерисовываться;
- Перерисовка вызывает метод Paint элемента управления.
- Поместите соответствующий код в метод Paint управляющего элемента и,
- Запросто мы создали диаграмму с доступными данными.
Метод Invalidate в событии щелчка кнопки не потребует много нашего времени:
Private Sub btnDisplay_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) _
Handles btnDraw.Click
pnlChart.Invalidate()
End Sub
Рисуем Panel
Так что осталось поместить «движок» в событие Paint объекта Panel, который проводит окончательное рисование: я только проведу вас в детали, код и логика которых отличаются от наших предыдущих проектов. Мы встречали объект Graphics, свойство SmoothingMode и Brush раньше:
Private Sub pnlChart_Paint(ByVal sender _
As Object, ByVal e As _
System.Windows.Forms.PaintEventArgs) _
Handles pnlChart.Paint
Dim g As Graphics = e.Graphics
g.SmoothingMode = SmoothingMode.HighQuality
Dim PattBrush As Brush
Мы не хотим совершить ошибку, пытаясь нарисовать диаграмму без данных. Поэтому следующая строка проверяет, что есть, по меньшей мере, один законченный элемент в массиве, прежде чем действию будет позволено продолжиться. Если данные существуют, мы можем продолжить и начать обрабатывать их: (Это тот же код, который мы использовали в первой части, так что я не буду снова объяснять все в деталях.)
If UserData.Count > 0 Then
' Create Rectangle to contain the Pie Chart
Dim rect As Rectangle = New Rectangle(20, 10, 200, 200)
' Calculate the grand total
Dim TotalCount As Single
For Each gd As GraphData In UserData
TotalCount += gd.Amount
Next
' Create variables to hold the changing values of Angles
Dim StartAngle As Single = 0
Dim SweepAngle As Single = 0
Следующий блок кода почти тот же, что был в Части 1. Ключевое отличие содержится в природе объекта Brush. Мы создадим новый HatchBrush и назначим ему выбранный шаблон и цвет. Я выбрал Белый как цвет фона во всех классах. Однако ощутите свободу выбора или – если вам не чужд авантюризм – добавьте в дальнейшем выбор для пользователя HatchStyle BackColor.
' Рисует диаграмму
For Each gd As GraphData In UserData
SweepAngle = 360 * gd.amount / TotalCount
PattBrush = New HatchBrush(gd.Pattern, gd.clr, Color.White)
g.FillPie(PattBrush, rect, StartAngle, SweepAngle)
' Опционально: рисует линии вокруг сегментов:-
g.DrawPie(New Pen(Color.Black), rect, StartAngle, SweepAngle)
StartAngle += SweepAngle
Next
Ключ
Создание ключа повторяет код Части 1, за исключением того, что я заменил круглые пули (bullets) квадратными. Этому нет особенной причины: просто я мог это сделать.
' Создает Brush, чтбы рисовать текст
Dim TextBrsh As Brush = New SolidBrush(Color.Black)
' Создает экземпляр объекта Font для отображения текста
Dim TextFont As New Font("Verdana", 9, FontStyle.Bold)
' Рисует пули (bullets) и информацию о компании
Dim pxFromTop As Integer = 235
' Рисует текст заголовка
g.DrawString("Chart Key", TextFont, TextBrsh, 35, pxFromTop)
For Each gd As GraphData In UserData
' Увеличивает расстояние от верха (Top)
pxFromTop += 20
' Рисует пулю (bullet)
PattBrush = New HatchBrush(gd.Pattern, gd.clr, Color.White)
g.FillRectangle(PattBrush, 20, pxFromTop, 15, 15)
' Рисует линию вокруг пули (bullet).
g.DrawRectangle(New Pen(Color.Black), 20, pxFromTop, 15, 15)
' Рисует кодированный цветной текст
g.DrawString(gd.name & " (" & gd.Amount & ")", TextFont, TextBrsh, 60, pxFromTop)
Next
Финальное домоводство
Наконец, избавляемся от доступных объектов, которые больше не используются:
TextBrsh.Dispose()
TextFont.Dispose()
PattBrush.Dispose()
Restart Option
Опция «Уппс!»
Мы почти все сделали. Пользователь может ввести столько данных, сколько их будет нужно и диаграмма может обновляться каждый раз, чтобы отобразить данные и выбранные шаблон/цвет. Но может наступить время, когда пользователь захочет начать все с начала.
Вместо того, чтобы заставить его закрыть приложение и запустить его сначала, давайте дадим ему кнопку, чтобы удалить данные и очистить панель для других данных.
Этот код для события Click кнопки Restart, является всем необходимым:
Private Sub btnRestart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRestart.Click
UserData.Clear()
pnlChart.Invalidate()
End Sub
Наш новый друг - метод Invalidate - снова делает за нас большую часть работы, снова удаляя все данные из массива.
Выводы
В этой статье оригинальный код создания круговой диаграммы приближается еще ближе к сценарию реального мира, того, где пользователь вводит данные во время выполнения.
Был введен метод Invalidate и мы увидели, как он вызывает перерисовку элемента, для которого он был вызван. Для заполнения сегментов диаграммы выбранными образцами из перечисления HatchStyle был использован HatchBrush. В итоге, эта статья охватывает следующее:
- Array
- Brush
- ColorDialog
- Dispose
- DrawPie
- DrawRectangle
- DrawString
- FillPie
- FillRectangle
- Объект Font
- HatchBrush
- HatchStyle
- Invalidate
- Pen
- StartAngle
- Structure
- SweepAngle
- System.Enum.GetNames
- System.Enum.Parse
Если вы прочитали все три статьи в этой серии, я надеюсь, что теперь вы чувствуете себя очень удобно с несколькими ключевыми основными методами, свойствами и техниками класса Graphics. И я также надеюсь, что вы будете с нетерпением ждать нового на территории Части 4, куда мы вернемся для создания самой лучшей гистограммы.