6.2 Создание растрового редактора - часть 2. Оболочка программы.

Необходимые знания:
Вам могут понадобится следующие статьи:
Итак, в предыдущей части главы мы создали основу нашего приложения – оболочку, основы базовых классов и даже научили нашу программу рисовать. Отлично.
Целью этой части главы будет реализация нескольких элементов рисования и создание возможности выбора рисования необходимым цветом. Также мы внесем некоторые «косметические» изменения.

Важные темы, рассматриваемые в данной части главы:
  1. Доработка класса, управляющего кистями.
  2. Создание возможности выбора цвета и рисования заданным цветом.
  3. Обновление дизайна и функциональности оболочки растрового редактора.
Начнем с доработки класса anBrush и реализации функций работы с кистями.

Мы полностью переработаем конструктор этого класса. Начнем мы с того, что конструкторов теперь будет два:

Первый конструктор (public anBrush(int Value, bool Special)) будет отвечать за создание кистей двух классов.
Если параметр Special указан как false, то будет создаваться стандартная кисть, размером Value x Value. Все ее пиксели будут устанавливаться черными.

В противном случае будут использоваться специальные кисти, маски которых мы будем задавать вручную и хранить в программе. В зависимости от параметра Value будет выполняться ветвление кода с помощью конструкции switchcase. Кистью по умолчанию мы назначим кисть, созданную нами вручную в предыдущей части главы.

Второй конструктор (public anBrush(string FromFile)) в качестве параметра будет принимать имя bmp-файла, из которого будут загружены растровые данные. Этот файл можно создать в любом графическом редакторе, единственным требованием будет, чтобы пиксели, которые должны в последствии стать невидимыми, были заданы красным цветом - RGB (255,0,0).

Обновленный код этого класса:

/*http://esate.ru, Anvi*/

public class anBrush
{
  public Bitmap myBrush;

  // стандартная (квадратная) кисть, с указанием масштаба 
  // и флагом закраски углов 

  public anBrush(int Value, bool Special)
  { 
    if(!Special)
    { 
      myBrush = new Bitmap(Value, Value);

      for (int ax = 0; ax < Value; ax++)
      for (int bx = 0; bx < Value; bx++)
        myBrush.SetPixel(0, 0, Color.Black);

    }
    else
    { 
      // здесь мы будем размещать предустановленные кисти 
      // созданная нами ранее кисть в виде перекрестия двух линий будет кистью по умолчанию 
      // на тот случай, если задан не описанный номер кисти 
      switch (Value)
      { 
        default:
        { 
          myBrush = new Bitmap(5, 5);

          for (int ax = 0; ax < 5; ax++)
          for (int bx = 0; bx < 5; bx++)
            myBrush.SetPixel(ax, bx, Color.Red);

          myBrush.SetPixel(0, 2, Color.Black);
          myBrush.SetPixel(1, 2, Color.Black);

          myBrush.SetPixel(2, 0, Color.Black);
          myBrush.SetPixel(2, 1, Color.Black);
          myBrush.SetPixel(2, 2, Color.Black);
          myBrush.SetPixel(2, 3, Color.Black);
          myBrush.SetPixel(2, 4, Color.Black);

          myBrush.SetPixel(3, 2, Color.Black);
          myBrush.SetPixel(4, 2, Color.Black);

          break;
        }

      }

    }

  }

  // второй конструктор будет позволять загружать кисть из стороннего файла 
  public anBrush(string FromFile)
  { 
    string path = Directory.GetCurrentDirectory();
    path += "\\" + FromFile;
    myBrush = new Bitmap(path);
  }

} 


Как видите, здесь все предельно просто.

Теперь обратимся к классу anEngine.
Сначала добавим переменную, которая будет хранить последний установленный цвет. Это необходимо, потому что при установке цвета он назначается текущему активному слою. Назначать ее сразу всем слоям (это создание дополнительного цикла перебора всех слоев) довольно неудобно.

Мы просто будем хранить последний установленный цвет, и, когда в следующей главе добавим функции смены слоев, новому установленному или созданному слою будет назначаться данный цвет в качестве активного.

/*http://esate.ru, Anvi*/

// последний установленный цвет 
private Color LastColorInUse; 


Теперь в конструкторе класса будет создаваться начальная кисть, заданная следующим образом:

/*http://esate.ru, Anvi*/

// создание кисти по умолчанию в конструкторе класса anEngine 
standartBrush = new anBrush(3,false); 


Также мы добавим 3 функции, которые будут вызываться нажатиями кнопок на панели инструментов в левой части окна создаваемой программы для установки кистей:

/*http://esate.ru, Anvi*/

// функция установки стандартной кисти, передается только размер 
public void SetStandartBrush(int SizeB)
{ 
  standartBrush = new anBrush(SizeB, false);
}

// функция установки специальной кисти 
public void SetSpecialBrush(int Nom)
{
  standartBrush = new anBrush(Nom, true);
}

// установка кисти из файла 
public void SetBrushFromFile(string FileName)
{ 
  standartBrush = new anBrush(FileName);
}


Также мы добавим функции для установки активного цвета:

/*http://esate.ru, Anvi*/

// функция установки активного цвета 
public void SetColor(Color NewColor)
{
  ((anLayer)Layers[ActiveLayerNom]).SetColor(NewColor);
  LastColorInUse = NewColor;
}


Как видите, мы вызываем функцию SetColor для активного в данный момент (ActiveLayerNom) слоя. Класс anLayer ранее не содержал такой функции, так что нашим следующим действием будет добавление в него данной функции.

Перейдем к классу anLayer и добавим реализацию данной функции:

/*http://esate.ru, Anvi*/

// установка текущего цвета для рисования в слое 
public void SetColor(Color NewColor)
{
  ActiveColor = NewColor;
}


Вот и всё. Обновление кода классов, которое мы планировали в данной главе, готово.

Теперь перейдем к редактированию оболочки.

Первым делом обновим кнопки для установки кистей. Используйте данные изображения для размещения на кнопках (мы научились это делать в главе 2.3).

Кнопка 1. Кнопка 1

Кнопка 2. Кнопка 2

Кнопка 3. Кнопка 3

Теперь панель будет выглядеть следующим образом (рис. 1):
Уроки OpenGL + C#: Меню в окне программы Рисунок 1. Меню в окне программы.
Код для обработки события нажатием на эти кнопки будет выглядеть следующим образом:

/*http://esate.ru, Anvi*/

private void toolStripButton1_Click(object sender, EventArgs e)
{
  // устанавливаем стандартную кисть 4х4 
  ProgrammDrawingEngine.SetStandartBrush(4);
}

private void toolStripButton2_Click(object sender, EventArgs e)
{
  // устанавливаем специальную кисть 
  ProgrammDrawingEngine.SetSpecialBrush(0);
}

private void toolStripButton3_Click(object sender, EventArgs e)
{ 
  // установить кисть из файла 
  ProgrammDrawingEngine.SetBrushFromFile( "brush-1.bmp");
}


Для того чтобы кисть из файла успешно установилась, необходимо добавить данное изображение в папку bin -> debug текущего проекта.

Добавление функций установки текущего цвета

Теперь добавим на нашу форму дополнительные элементы:
  1. Элемент colorDialog, из панели инструментов (рис. 2). После добавления переименуйте данный элемент в «changeColor» (свойство name).
  2. Два элемента panel. Переименуйте их в color1 и color2. Расположение этих элементов показано на рисунке 3.
  3. Элемент LinkLabel. Параметр text в его свойствах установите равным «поменять». Положение элемента также представлено на рисунке 3.
Уроки OpenGL + C#: Добавление элемента colorDialog Рисунок 2. Добавление элемента colorDialog.
Уроки OpenGL + C#: Элементы panel для отображения цвета Рисунок 3. Элементы panel для отображения цвета.
Свойство BackColor для элемента color1 установите равным black. Для элемента color2 – любым. Также установите границы обоих элементов: свойство BorderStyle должно быть FixedSingle. Данная комбинация элементов будет отвечать за установку текущего цвета.

Добавьте обработчик нажатия клавиши мыши на элементе color1 (MouseClick) и на элементе linklabel. При щелчке на элементе color1 мы будет открыть диалоговое окно выбора цвета, и если цвет успешно выбран, устанавливать его для переменной BackColor элемента color1. Так он будет виден пользователям. После установки он также будет передан в класс anEngine для дальнейшей установки активным цветом текущего слоя.

В случае щелчка мыши на элементе LinkLabe (текст «поменять») будет производиться обмен цветов у элементов color1 и color2. Далее цвет, установленный для color1, будет передаваться в класс anEngine.

Код обработки данных событий:

/*http://esate.ru, Anvi*/

// обмен значений цветов 
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{

  // временное хранение цвета элемента color1 
  Color tmp = color1.BackColor;

  // замена: 
  color1.BackColor = color2.BackColor;
  color2.BackColor = tmp;

  // передача нового цвета в ядро растрового редактора 
  ProgrammDrawingEngine.SetColor(color1.BackColor);

}

// функция установки нового цвета с помощью диалогового окна выбора цвета 
private void color1_MouseClick(object sender, MouseEventArgs e)
{ 
  // если цвет успешно выбран 
  if (changeColor.ShowDialog() == DialogResult.OK)
  { 
    // установить данный цвет 
    color1.BackColor = changeColor.Color;
    // и передать его в класс anEngine для установки активным цветом текущего слоя 
    ProgrammDrawingEngine.SetColor(color1.BackColor);
  }
}


Пример работы программы можно видеть на рисунке 4.
Уроки OpenGL + C#: Пример работы растрового редактора с использованием OpenGL и C# Рисунок 4. Пример работы растрового редактора с использованием OpenGL и C#.

Примечания

В Visual Studio 2010 вы можете столкнуться с проблемой неопределенных ArrayList.
Для решения проблемы добавьте следующую строку (спасибо пользователю @LyaminSS):

/*http://esate.ru, Anvi*/


using System.Collections;


Нет доступа к просмотру комментариев.

^