Для начала вспоминаем что любая программа WinForms стартует с класса Program
вызывая статический метод Main
. По умолчанию в нем находится две строчки
C# | ||
|
Первая строка отвечает за предустановки внешнего вида, а вторая вызывает и «захватывает» окно (т.е. метод Run
завершиться только когда закроется переданный инстанс окна MainForm
). Так как всё это код самого обычного метода, мы можем его расширить любым образом:
— добавить вызов окон (как в диалоговом режиме, так и просто). Вызов в диалоговом режиме будет приостанавливать выполнение кода до момента, пока не будет закрыто окно.
— добавить любые проверки перед вызовом Application.Run
— добавить проверки и НЕ вызывать Application.Run
, тем самым автоматически завершить программу.
Итого самый простой алгоритм действия для формы авторизации следующий:
— вызываем диалог формы и дожидаемся её завершения
— проверяем форму (на самом деле вариантов передачи результата авторизации больше, по сути нам нужно любым способом вернуть результат авторизации в вызывающий код) на то прошла ли авторизация успешно
— если результат ОК — вызываем основную форму
— так как нам не нужно чтобы форма авторизации вечно висела в памяти, её вызов нужно «упаковать» в отдельный метод. Таким образом наша форма станет «локальной» и будет жить только в рамках этого метода.
C# | ||
|
Теперь рассмотрим чуть подробней саму форму авторизации. Для начала её дизайн:
Пока не обращаем внимание на флаг «запомнить». Из основного, что я бы отметил по дизайну:
— обработку нужно вешать не только на кнопку «войти», но и на enter для поля пароля (как минимум).
— поле пароля по хорошему стоит закрыть символами (свойство PasswordChar
)
— опционально можно поотключать кнопки свернуть/развернуть чтобы больше походило на модальное окно
— именуем элементы не дефолтно textBox1, а более осознанно
Теперь про обработку. Можно разместить всю логику прям в форме, и просто выплевывать результат. Чуть более правильный подход — создать ТРИ отдельных класса:
— первый будет инкапсулировать логику проверки логина/пароля и сохранения его в настройках юзера
— второй — контекст, который будет «глобальным» для всего приложения, и хранить под каким пользователем залогинились. «Глобальность» будем реализовывать через патерн Singelton, а точнее — тупо public static
— класс отвечающий за непосредственную проверку логина+пароля на авторизацию. Обычно это обращение по API к серверу (ну или прям БД), либо иному внешнему ресурсу. Я отдельно акцентирую на этом внимание, т.к. это долгая операция и потенциально это может привести к «зависанию» программы.
C# | ||
|
Чуть подробней по DataService
: так как нам нужно смоделировать долгое обращение (обычно запросы несущественные, но тем не менее никто не застрахован), я просто вставляю задержки на 2сек для наглядности. Также у нас не всегда есть асинхронный метод для запроса, поэтому в нашем моке объявленно два варианта (не асинхроный вариант просто пакуем в задачу, пример закоментирован в методе Auth
)
использование внутри формы
C# | ||
|
На самом деле это не самый кошерный подход, более правильно — просто закрывать форму, а уже на стороне вызывающего кода проверять UserContext.IsAuthed
. Это дает большую независимость от того, что UI представлен в виде конкретной формы.
Теперь добавим возможность сохранить пароль, чтобы не вводить его в следующий раз. Для этого будем использовать механизм Settings, который позволяет сохранять данные в учетной записи пользователя Windows. Правый клик по проекту в SolutionExplorer — самая нижняя вкладка «Settings» — тыкаем «создать или открыть настройки». (Второй вариант: Add — Items — справа выбираем раздел General — ищем Settings File). В настройках добавляем две записи Login и Password. Тип выбираем string, скоп — User. Значения оставляем пустыми. Желательно оставить Internal.
Найболее юзерфрендли подход состоит в том чтобы у нас при старте программы также показывалась форма авторизации, но поля заполнялись автоматически и сама «нажималась» кнопка войти. Таким образом у нас не теряется отклик «выполняется вход» и юзер видит что программа запустилась, но дожидается ответа сервера. Добавляем в конструктор формы авторизации флаг автоматического выполнения при отображении. Также добавляем флаг «запоминать».
C# | ||
|
В сервисе авторизации добавляем метод (string Login, string Password)? LoadSecure()
для вычитки логина+пароля из настроек юзера. Также добавляем сохранение при авторизации (при условии что у нас выставлен флаг «запомнить»).
C# | ||
|
В стартовом методе добавляем вначале вычитку сохраненной учетки. Если успешно — передаем флаг автоматического входа и значения.
C# | ||
|
Не забываем подправить вызов проверки в самой форме авторизации (нужно добавить передачу флага remember из чекбокса). Теперь последняя фича — передача параметров. Тут тоже ничего сложного: все входящие параметры, это просто массив строк, которые передаются параметром args
в метод Main
. Мы просто проверяем есть ли на входе параметры, если «да» — передаем именно их окну, а не те что сохранены. Именно такая логика нужна, т.к. пользователь явно ожидает залогиниться под указанной учеткой. Он может банально не обратить внимание что программа зашла не под нужной (а если после авторизации происходят ещё какие-то действия — это ещё может и что-то поломать или выполнить ненужные действия). Выглядит эта проверка так
C# | ||
|
Теперь немного разберемся с тем, что форма блокируется вот прям вся. Это плохой подход, так как ожидание может быть существенным и желательно показывать процесс работы (например анимационую иконку). При таком подходе пользователь четко понимает что прога не повисла, а ждет чего-то. Общий принцип можно использовать следующий:
— размещаем элементы на отдельной панели в окне (либо выносим в юзер контрол)
— блокируем только эту панель
— на само окно добавляем иконку на время выполнения запроса.
— после выполнения операции убираем иконку и разблокируем окно.
С учетом того что внешний код может завалится по различным причинам, которые не должны валит всё приложение (например временно нет интернета) — стоит обернуть операцию в try-catch-finally блок. Это позволит повторить операцию ещё раз, при этом уведомив о возникших неполадках.
Всё это можно унифицировать следующим образом:
— создаем интерфейс, указывающий что нужно блокировать и где размещать элемент отображения «ожидания». Это позволит использовать код на различных формах, а не только авторизации.
— создаем класс-помощник, который выполняет безопасный вызов операции
— саму иконку реализуем через UserControl. Это позволит безболезненно менять её внешний вид.
C# | ||
|
Реализация всего это в форме авторизации
C# | ||
|
Финальный момент. После того как мы успешно выполнили авторизацию, в 99 случаев из 102 на главном окне нам тоже нужно выполнить подгрузку тех или иных данных. Т.е. опять задержки и прочее, что может вызывать дискомфорт у пользователя при работе. Как не стоит делать: лепить блокирующую загрузку в конструктор или метод Load. Это приведет к тому что после закрытия окна у нас главная форму не будет показываться (пользователь не поймет что программа продолжает работу, а не вылитела). В целом у нас уже ранее набран механизм для обеспечения юзерфрендли работы, и главное окно можно реализовать так
C# | ||
|
Финальный код примера в архиве Nya.AuthExample.zip
КАК НЕ НЕДО ДЕЛАТЬ ОКНО АВТОРИЗАЦИИ
анти-пример 1. В сети оооочень часто встречается подход следующего вида:
— в качестве основной формы в Application.Run
передаем форму авторизации
— после входа, мы делаем Hide()
диалоговой формы, и new MainForm().Show()
Что влечет за собой такой говнокод:
— во-первых, у нас в памяти теперь висит диалог авторизации. «Ну это же немного» будете рассказывать в очередном сраче, что винда жрет слишком много ОЗУ.
— во-вторых и основное: выход из приложения теперь подразумевает грохнуть скрытую форму, до которой ещё нужно достучаться. Придется лепить всякие Application.Exit()
, либо тянуть хвостом что «вот эту форму нужно сделать Close».
— в-третьих, у вас появляется супер тупая зависимость «второстепенная форма порождает главную». Когда вам нужно будет повторно вызвать форму авторизации (скажем истекло время сессии) — нужно «поднять» заныканую форму вначале.
анти-пример 2. Засунуть создание диалоговой формы авторизации в конструктор или метод Load главной. Пример менее говняный, нежели предыдущий, тем не менее у нас появляется зависимость «главное окно знает о существовании второстепенного». Жить можно, но рекомендую не порождать супер-окон (как и божественных классов).
Многие приложения требуют авторизации для полноценного использования. Сегодня мы напишем простую форму авторизации Windows-приложений на языке C#.
Шаг 1. Создание базы
Первым делом создадим новую базу данных test
для хранения тестовой информации. Добавьте таблицу user
со следующими полями:
id
(INT) c атрибутомAUTO_INCREMENT
;name
(VARCHAR(100));title
(VARCHAR(100));address
(VARCHAR(100)).
user
в базе данных test
Шаг 2. Создание проекта
Создайте проект для нового приложения. В Visual Studio для этого нужно зайти в меню File
> New
> Project
.
После этого появится окно New Project:
В поле Name
нужно вписать название вашего проекта, в поле Location
– указать нужную директорию, в Solution name
– ввести название решения. Заполнили данные – нажимаем OK
.
Шаг 3. Создание интерфейса
Создайте представление будущей формы авторизации, как показано на рисунке ниже. Добавьте два поля (username
и password
) и кнопку для входа.
Шаг 4. Настройка соединения с базой
Создайте класс connection
для настройки соединения с базой. Пример реализации представлен в листинге ниже:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
using System.Windows.Forms;
using System.Data;namespace Connection_DB
{
class connection
{
MySql.Data.MySqlClient.MySqlConnection conn;
string myConnectionString;
static string host = "localhost";
static string database = "test";
static string userDB = "ecco";
static string password = "password";
public static string strProvider = "server=" + host + ";Database=" + database + ";User ID=" + userDB + ";Password=" + password;
public bool Open()
{
try
{
strProvider = "server=" + host + ";Database=" + database + ";User ID=" + userDB + ";Password=" + password;
conn = new MySqlConnection(strProvider);
conn.Open();
return true;
}
catch (Exception er)
{
MessageBox.Show("Connection Error ! " + er.Message, "Information");
}
return false;
} public void Close()
{
conn.Close();
conn.Dispose();
} public DataSet ExecuteDataSet(string sql)
{
try
{
DataSet ds = new DataSet();
MySqlDataAdapter da = new MySqlDataAdapter(sql, conn);
da.Fill(ds, "result");
return ds;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return null;
} public MySqlDataReader ExecuteReader(string sql)
{
try
{
MySqlDataReader reader;
MySqlCommand cmd = new MySqlCommand(sql, conn);
reader = cmd.ExecuteReader();
return reader;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return null;
} public int ExecuteNonQuery(string sql)
{
try
{
int affected;
MySqlTransaction mytransaction = conn.BeginTransaction();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
affected = cmd.ExecuteNonQuery();
mytransaction.Commit();
return affected;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return -1;
}
}
}
Наконец, вернитесь к форме и добавьте следующий код:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MySql.Data.MySqlClient;namespace Connection_DB
{
public partial class Form1 : Form
{
connection con = new connection();
string id, username, password, firstname, lastname, address;
public Form1()
{
InitializeComponent();
} private void btnLogin_Click(object sender, EventArgs e)
{
try
{ if (txtUsername.Text != "" && txtPassword.Text != "")
{
con.Open();
string query = "select id,username,password,firstname,lastname,address from user WHERE username ='" + txtUsername.Text + "' AND password ='" + txtPassword.Text + "'";
MySqlDataReader row;
row = con.ExecuteReader(query);
if (row.HasRows)
{
while (row.Read())
{
id = row["id"].ToString();
username = row["username"].ToString();
password = row["password"].ToString();
firstname = row["firstname"].ToString();
lastname = row["lastname"].ToString();
address = row["address"].ToString();
} MessageBox.Show("Data found your name is " + firstname + " " + lastname + " " + " and your address at " + address);
}
else
{
MessageBox.Show("Data not found", "Information");
}
}
else
{
MessageBox.Show("Username or Password is empty", "Information");
}
}
catch
{
MessageBox.Show("Connection Error", "Information");
}
}
}
}
Результат
Нажмите F5
, чтобы запустить программу. Если соединение с базой данных успешно установится, вы увидите только что созданную форму.
Исходный код этой простой формы авторизации вы найдете на Github.
***
У нас есть много полезных материалов по C# для начинающих:
- Лучшие актуальные шпаргалки по C# на все случаи жизни
- Видеокурс по C# с нуля: от основ до полноценного приложения
- Самый понятный видеокурс по C# для новичков
Источники
- https://www.c-sharpcorner.com/article/simple-login-form-c-sharp-with-database-mysql/