Своя программа для Windows. C# очень простыми словами.

 
 

Своя программа для Windows. C# очень простыми словами.

 
 

Раньше я рассказал самые основы программирования (на любом языке), рассказал как установить среду разработки Visual Studio и где найти самую полную документацию по языку C# . В той статье был рассмотрен пример консольного приложения, использующего самые базовые элементы языка. К этому моменту у Вас должна быть установлена Visual Studio, а также Вы должны знать что такое переменные, условия и циклы.

Если для управления программой используются кнопки, переключатели, поля ввода и вывода, а также многие другие элементы, которые привычны нам по используемым в ОС Windows программам, то говорят, что используется оконный интерфейс, а сами эти элементы называют элементами интерфейса. Делать такие программы мы сейчас и научимся.

 

Запускаем Visual Studio, она предложит открыть старый или сторонний проект. В правом нижнем углу этого окошка выбираем «Начать без кода». Далее заходим в меню Файл->Создать->Проект и создаём проект «Приложение Windows Forms (.NET Framework)» для языка C# .

 
Своя программа для Windows. C# очень простыми словами.
 

Учтите, что если хотите, чтобы Ваша программа работала на Windows XP — поменяйте платформу на NET Framework версии 3.5. Для Windows 10 — выбирайте самую свежую (она будет стоять по-умолчанию). Тут надо сказать пару слов про то, что такое NET Framework.

Что такое NET Framework

В программировании два понятия «.Net» (читается «Дот Нэт») и «Framework» (читается «Фрэймворк») встречаются довольно часто. Фрэймворк — это большая группа инструментов, которая позволяет экономить время на написание того, что уже кто-то написал раньше (более мелкая группа инструментов называется библиотека или либа). .NET — это предложенный компанией Microsoft способ выполнения программного кода, призванный максимизировать совместимость с другими их продуктами, в том числе с ОС Windows. Мы выбрали тип приложения «Приложение Windows Forms (.NET Framework)» потому что он наиболее эффективный по трудозатратам исходя из конкретной задачи. NET Framework позволяет использовать в готовом виде, а не писать самому классы Windows Forms, например такие как окно программы или кнопка. При этом не надо будет опасаться непредвиденных ситуаций или плохой совместимости с ОС Windows.

Перед тем как начнём немного о функциях, процедурах и методах

Помните, в школе были функции, например y=2x+1, в них при изменении х меняется значение у. Практически во всех языках программирования существуют функции. Если они только выполняют действия, но не возвращают результат, тогда их называют процедурами. В объектно-ориентированном программировании, функция общая для множества объектов (например дыхание для всех людей) называется методом. В языке C# не может что-то выполняться вне объектов, поэтому в нём эти три слова равнозначны, потому что все они на самом деле — методы.

И что такое классы в программировании

В примере с дыханием людей метод дыхания присущ не только людям, но и всем живым существам. Продолжая аналогию можно сказать, что живые существа — это класс со своим набором атрибутов (спит или бодрствует, стоит или движется, возраст) методов (размножение, дыхание, питание). У него есть дочерние классы (собачки, рыбы, люди) с общими методами родительского класса, но и собственными. А вот разные люди будут объектами класса «человек»: у каждого своё имя, у всех разный рост и возраст, но они всё равно все люди и ходят на двух ногах, а летать не умеют.

Например так в C# , и картинка (PictureBox) и кнопка (Button) унаследовали от родительского класса (элемента управления) такие свойства как ширина (Width) или видимость (Visible), методы выделить (Select() ) или масштабировать (Scale(SizeF) ). Для всех элементов управления при вызове метода OnClick() произойдёт событие Click, но использовать картинку в качестве кнопки хоть и можно, но сродни забиванию гвоздя плоскогубцами. Картинку можно сохранить в файл, а кнопка имеет отдельный стиль для нажатого состояния. Если нужен свой особенный инструмент, для конкретной узкой задачи, то его можно создать.

Создание пользовательского интерфейса

Допустим моя программа должна делать следующее:

1) Пользователь рисует что-то мышкой (символ) и затем выбирает букву на клавиатуре.

2) Программа создаёт связь нарисованной картинки (символа) и буквы.

3) При вводе строки программа должна генерировать картинку из символов, соответствующих буквам в введённой строке.

Получится программа, которая генерирует написанный от руки текст. Либо создаёт шифр, вроде «пляшущих человечков», который разгадывал Шерлок Холмс.

Итак, у нас открыто чистое окошко.

 
Своя программа для Windows. C# очень простыми словами.
 

В левом верхнем углу есть вертикальная вкладка «Toolbox» (на картинке посередине левого края). Кстати, у меня стоит английский язык интерфейса. Часто документацию можно найти только на английском языке, так проще создать поисковый запрос и вообще английский я стараюсь подтягивать при любой возможности, что и Вам советую.

Покликав по вкладкам можно ознакомиться с основными элементами управления. Назначение большинства понятно интуитивно по опыту работы с другими программами в ОС Windows. Перетащим необходимые для решения задачи элементы. Для надписей используйте Label, для ввода текста TextBox, для рисования Panel, для вывода рисунка PictureBox, а кнопка — это Button. Затем подвигайте элементы, растяните их по размеру создав интерфейс программы. Тексты (не путать с Name — по нему Вы будете обращаться к конкретному объекту) и внешний вид выделенных элементов управления можно поменять в блоке Properties (по-умолчанию он в правом нижнем углу программы).

Поставленную задачу можно решить множеством способов. Например пункты 1 и 2 можно реализовать сделав поле ввода рисунка, поле ввода символа и кнопку для объединения их. А можно сделать поле ввода рисунка и кнопку, по которой будет предложено выбрать букву на клавиатуре (я так и поступлю). Как лучше сделать зависит только от программиста (Вас), если нет конкретного ТЗ (технического задания) от заказчика.

Пара слов о принципах программирования, которых следует придерживаться

В общем случае лучше следовать принципу бритвы Оккама или принципу KISS, что в общем об одном и том же. Не усложняя чрезмерно делать так, чтобы разные части программы не противоречили друг другу. 5 принципов SOLID и принцип YAGNI, также выступают за это.

Оставлять комментарии следует только там, где без них действительно будет сложно разобраться. Причём если без комментариев действительно тяжело разобраться, то комментарий надо оставить даже если не планируется, что кто-то ещё хоть когда-то заглянет в этот фрагмент когда.

1. Рисуем символ

Для рисования нам понадобятся общие переменные, я рекомендую объявить их в классе Form:

Point CurrentPoint; // текущая позиция мышки

Point PrevPoint; // предыдущая позиция мышки

bool isPressed; // нажата ли мышка

Graphics g; // сам рисунок

Bitmap b = new Bitmap(200, 200); //текущее состояние рисунка

В конструктор Form1() следует написать, то что должно произойти при создании формы. В данном случае:

g = Graphics.FromImage(b); // свяжем рисунок с изображением

Если не понятно куда печатать по моему описанию, то внизу есть полный листинг основного модуля программы.

Отступление про данные и память и особенности графики в C#

В компьютерах (или любых других цифровых устройствах, например смартфонах) есть данные, например какое-то фото. Эти данные находятся в памяти, например на жёстком диске. Можно зайти в директорию (папку) с этим фото и открыть это фото. Если скопировать это фото и изменить, то получится два различных фото.

Но можно создать ссылку (ярлык) на это фото. Если отредактировать фото по ссылке или скопировать ссылку и отредактировать фото, то изначальный вариант фото исчезнет и будет только новый.

В простой программе все переменные и объекты (в том числе картинки) находятся в оперативной памяти и к ним можно обращаться как напрямую, так и по ссылке.

В случае объекта типа Graphics (который используем в текущем примере) уместна аналогия, что он является столом слепого художника, а объект типа Bitmap — лист на котором он будет рисовать. Слепому художнику надо явно сказать где рисовать и какого размера лист. При этом магия заключается в том что все листы художника связаны друг с другом и изменив что угодно в любом из них рисунок меняется везде одновременно. Происходит это потому что все они связаны общим потоком памяти.

Если бы нашей программе нужен был только один рисунок, то проще всего было использовать вместо листа саму панель g = panel1.CreateGraphics(); но мы будем в нужный момент копировать текущий «волшебный» лист в обычные, которые останутся неизменными и разными.

Продолжаем рисовать символ

Реакцию на событие клика мышкой можно создать двойным кликом на элементе управления в конструкторе формы. Чтобы создать реакции на остальные события следует нажать в разделе Properties кнопку Events (со значком молнии). Там в левом столбце надо найти требуемое событие и связать их с методом из правого столбца, который выполнится в качестве реакции на это событие. Если требуемого метода ещё нет, то просто два раза кликнете на пустом поле — метод создастся автоматически.

 
Создадим реакции на события для панели, требуемые для рисования
 
Создадим реакции на события для панели, требуемые для рисования

В panel1_MouseDown добавим код:

isPressed = true;

CurrentPoint = e.Location;

В panel1_MouseUp добавим код:

isPressed = false;

В panel1_MouseMove добавим код:

if (isPressed) {

PrevPoint = CurrentPoint;

CurrentPoint = e.Location;

Pen p = new Pen(Color.Black);

g.DrawLine(p, PrevPoint, CurrentPoint);

panel1.BackgroundImage = b;

panel1.Refresh();}

Теперь можно нажать зелёную кнопку Start (F5) и убедиться, что нет ошибок и получается рисовать.

2. Связь рисунка с символом с клавиатуры

Объявим переменную Char c = ‘ ‘; (там же где и предыдущие, в классе Form) в ней будем хранить введённый с клавиатуры символ. По кнопке будем связывать текущий символ с картинкой. Для самой связи объявим коллекцию типа словарь Dictionary<Char Bitmap> d = new Dictionary<Char Bitmap>()Проверки ввода одних и тех же кнопок нет (это вызовет ошибку, так как в словаре может быть только одно ключевое значение), так что в этом месте лучше сделать обработку ошибок.

Создадим для кнопки обработчик события KeyPress, в него добавим код: c = e.KeyChar; Для того чтобы обработчик сработал необходимо, чтобы кнопка была выделена. Поэтому добавим команду выделить её при создании формы Form1: button1.Select();

Кнопка будет выделена с момента создания формы (так как мы указали это в её конструкторе) до тех пор пока не закончим формирование словаря, когда потребуется выделить поле ввода текста.

После двойного клика на кнопке курсор окажется в свежесозданном методе button1_Click. Напишем в него добавление связи в словарь и очистку картинки:

Bitmap _b = new Bitmap(b); // это копия текущего рисунка

d.Add(c, _b); // добавим в конец словаря пару символов

panel1.BackgroundImage = null;

g.Clear(Color.White);

3. Вывод готового рисунка

Закрепим изученное. Для вывода создадим графику, которую будем менять в случае, если в строке встретился символ из словаря, созданного в предыдущем пункте. Для этого в обработчик KeyDown элемента TextBox добавим следующий код:

Bitmap _bp = new Bitmap(pictureBox1.Width, pictureBox1.Height);

Graphics _g = Graphics.FromImage(_bp);

_g.Clear(Color.White);

if (e.KeyCode == Keys.Enter) {

int _i = 0;

foreach (Char _c in textBox1.Text) {

if (d.TryGetValue(_c, out Bitmap _b)) { //только если встречается в словаре

Point _p0 = new Point(_i * 200, 0); //смещаем место куда будем рисовать дальше

_g.DrawImage(_b, _p0);

pictureBox1.Image = _bp; }

_i++; }}

Осталось только нажать зелёную кнопку Start (F5) и убедиться, что нет ошибок и всё работает как задумано.

 
Готовая программа (присвоены рисунки только буквам "a" и "c")
 
Готовая программа (присвоены рисунки только буквам «a» и «c»)

Полный код основного модуля:

 

using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.Runtime.InteropServices;
  using System.Windows.Forms;
   
  namespace WindowsFormsApp1
  {
  public partial class Form1 : Form
  {
  //блок общих переменных
  Point CurrentPoint;
  Point PrevPoint;
  bool isPressed;
  Bitmap b = new Bitmap(200, 200);
  Graphics g;
  Char c = ‘ ‘;
  Dictionary<Char, Bitmap> d = new Dictionary<Char, Bitmap>();
   
  public Form1()
  {
  InitializeComponent();
  g = Graphics.FromImage(b);
  button1.Select(); //иначе не будет работать button1_KeyPress
  }
   
  private void panel1_MouseDown(object sender, MouseEventArgs e)
  {
  isPressed = true;
  CurrentPoint = e.Location;
  }
   
  private void panel1_MouseMove(object sender, MouseEventArgs e)
  {
  if (isPressed)
  {
  PrevPoint = CurrentPoint;
  CurrentPoint = e.Location;
  Pen p = new Pen(Color.Black);
  g.DrawLine(p, PrevPoint, CurrentPoint);
  panel1.BackgroundImage = b;
  panel1.Refresh();
  }
  }
   
  private void panel1_MouseUp(object sender, MouseEventArgs e)
  {
  isPressed = false;
  }
   
  private void button1_Click(object sender, EventArgs e)
  {
  Bitmap _b = new Bitmap(b);
  d.Add(c, _b);
  panel1.BackgroundImage = null;
  g.Clear(Color.White);
  }
   
  private void button1_KeyPress(object sender, KeyPressEventArgs e)
  {
  c = e.KeyChar;
  label3.Text = c.ToString();
  }
   
  private void textBox1_KeyDown(object sender, KeyEventArgs e)
  {
  Bitmap _bp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
  Graphics _g = Graphics.FromImage(_bp);
  _g.Clear(Color.White);
  if (e.KeyCode == Keys.Enter)
  {
  int _i = 0;
  foreach (Char _c in textBox1.Text)
  {
  if (d.TryGetValue(_c, out Bitmap _b))
  {
  Point _p0 = new Point(_i * 200, 0);
  _g.DrawImage(_b, _p0);
  pictureBox1.Image = _bp;
  }
  _i++;
  }
  }
  }
  }
  }
 

Обновлено: 09.10.2020 — 09:13