Дата публикации статьи: 05.10.2006 14:00

Удачные диаграммы: Вторая помощь в круговых диаграммах. Часть 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, куда мы вернемся для создания самой лучшей гистограммы.