ПИШЕМ БЛОКНОТ НА C# - ЧАСТЬ 2 (Работа с файлами)

В прошлой части мы разработали внешнюю оболочку нашего блокнота. В этой части мы напишем код для работы с файлами: научим блокнот создавать, открывать, сохранять, а также печатать текстовые документы.



Для начала в коде формы MainForm.cs добавим переменную-индикатор, которая будет сообщать о том, что в документе произошли какие-либо изменения. Она потребуется для того, чтобы программа понимала, когда можно просто очистить элемент notebox, а когда нужно сперва предложить сохранить изменения в документе. А также создадим переменную, в которую будем записывать путь открытого документа. Это потребуется для функции сохранения открытого документа. Поэтому в коде формы добавим строки:

bool tbChange = false;
string docPath = "";

Примечание. Важно добавить эти строки вне функций, чтобы у всех элементов формы был к ним доступ. Я добавил их сразу после функции инициализации компонентов формы.


Теперь нажмем дважды на textbox и перейдем в обработчик событий изменения текста в элементе notebox. Запишем туда следующую строку:

tbChange = true;

То есть, теперь если текст документа будет как-то изменяться, наша переменная-индикатор изменит свое значение на True.

Теперь добавим на форму следующие элементы:

  • printDocument1 (printDocument)
  • printDialog1 (printDialog) - в свойствах, в поле "Document" выбираем наш printDocument
  • pageSetupDialog1 (pageSetupDialog) - в свойствах, в поле "Document" выбираем наш printDocument
Они понадобятся нам для обеспечения возможности печати документа, а также настройки параметров печатаемых страниц.

Настройки приложения


Чтобы улучшить удобство использования программы, научим её запоминать изменения в интерфейсе, внесенные пользователем. Для этого открываем свойства проекта и переходим на вкладку параметров (Проект-Свойства-Параметры).


В первый столбец заносим имена параметров, во втором выбираем их тип, в четвертом указываем значение по-умолчанию:
  • programmName - string - Копирайтер+
  • newDocName - string - Новый документ
  • statusStripVisible - bool - True
  • textTransfer - bool - False
  • textFont - System.Drawing.Font - Segoe UI; 9,75pt
  • formWidth - int - 640
  • formHeight - int - 400
Для того чтобы значения применялись и сохранялись, задействуем 2 события формы MainForm.cs: "Load" (Событие при загрузке формы) и "FormClosing" (Событие после нажатия на кнопку закрытия формы, но до самого закрытия).

Код для события Load:

this.Width = Properties.Settings.Default.formWidth;
this.Height = Properties.Settings.Default.formHeight;
notebox.Font = Properties.Settings.Default.textFont;
if (Properties.Settings.Default.statusStripVisible == true)
{ mViewStatusStrip.CheckState = CheckState.Checked; }
else
{ mViewStatusStrip.CheckState = CheckState.Unchecked; }
if (Properties.Settings.Default.textTransfer == true)
{ mFormatTransfer.CheckState = CheckState.Checked; }
else
{ mFormatTransfer.CheckState = CheckState.Unchecked; }

Код для события Closing:

Properties.Settings.Default.formWidth = this.Width;
Properties.Settings.Default.formHeight = this.Height;
Properties.Settings.Default.textTransfer = notebox.WordWrap;
Properties.Settings.Default.textFont = notebox.Font;
Properties.Settings.Default.statusStripVisible = statusStrip.Visible;
Properties.Settings.Default.Save();

Параметры programmName и newDocName здесь не используются. Они понадобятся далее.

FileWorks.cs


Создаем класс для операций с файлами (FileWork.cs). В него мы поместим все методы для работы с файлами, которые будут применяться в коде не 1 раз: создать, открыть, сохранить, сохранить как.

Прописываем необходимые инструкции:

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Windows.Forms;
using System.Text;

Приступаем к созданию методов:

public static void CreateFile(ref TextBox notebox, ref bool tbChange, ref string docPath) // Метод "Создать новый документ"
{
    string newDocName = Properties.Settings.Default.newDocName;
    string programmName = Properties.Settings.Default.programmName;
    notebox.Clear();
    docPath = "";
    tbChange = false;
    MainForm.ActiveForm.Text = newDocName + " — " + programmName;
}

public static void OpenFile(ref TextBox notebox, ref bool tbChange, ref string docPath) // Метод "Открыть документ"
{
    string programmName = Properties.Settings.Default.programmName;
    OpenFileDialog openDocument = new OpenFileDialog();
    openDocument.Title = "Открыть текстовый документ";
    openDocument.Filter = "Текстовые файлы (*.txt) |*.txt| Все файлы (*.*)|*.*";
    if (openDocument.ShowDialog() == DialogResult.OK)
    {
        FileStream file = new FileStream(openDocument.FileName, FileMode.Open, FileAccess.Read);
        StreamReader reader = new StreamReader(file, Encoding.Default);
        notebox.Text = reader.ReadToEnd();
        reader.Close();
        docPath = openDocument.FileName;
        tbChange = false;
        MainForm.ActiveForm.Text = openDocument.SafeFileName + " — " + programmName;
    }
}
        
public static void SaveFile(ref TextBox notebox, ref bool tbChange, ref string docPath) // Метод "Сохранить документ"
{
    FileStream file = new FileStream(docPath, FileMode.Create, FileAccess.Write);
    StreamWriter writer = new StreamWriter(file, Encoding.Default);
    writer.Write(notebox.Text);
    writer.Close();
    tbChange = false;
}

public static void SaveAsFile(ref TextBox notebox, ref bool tbChange, ref string docPath) // Метод "Сохранить документ как..."
{
    string programmName = Properties.Settings.Default.programmName;
    SaveFileDialog saveAsDocument = new SaveFileDialog();
    saveAsDocument.Title = "Сохранить документ как...";
    saveAsDocument.FileName = "Текстовый документ";
    saveAsDocument.Filter = "Текстовые файлы (*.txt) |*.txt| Все файлы (*.*)|*.*";
            
    if (saveAsDocument.ShowDialog() == DialogResult.OK) //Если пользователь подтвердил сохранение
    {
        //Создаем файл по пути, выбранному в окне сохранения
        FileStream file = new FileStream(saveAsDocument.FileName, FileMode.Create, FileAccess.Write);
        StreamWriter writer = new StreamWriter(file, Encoding.Default);
        writer.Write(notebox.Text); //записываем содержимое в файл
        writer.Close(); //закрываем поток
        tbChange = false;
        docPath = saveAsDocument.FileName;
        MainForm.ActiveForm.Text = Path.GetFileName(saveAsDocument.FileName) + " — " + programmName;
    }
    else
    {
        tbChange = true;
        return;
    }
}

Теперь можно писать код для самих элементов меню "Файл".

Создать документ


if (tbChange == true)
{
    DialogResult message = MessageBox.Show("Сохранить текущий документ перед созданием нового?", "Создание документа", MessageBoxButtons.YesNoCancel);
    if (message == DialogResult.Yes)
    {
        if (docPath != "")
        {
            FileWork.SaveFile(ref notebox, ref tbChange, ref docPath);
            FileWork.CreateFile(ref notebox, ref tbChange, ref docPath);
        }
        else if (docPath == "")
        {
            FileWork.SaveAsFile(ref notebox, ref tbChange, ref docPath);
            FileWork.CreateFile(ref notebox, ref tbChange, ref docPath);
        }
    }
    else if (message == DialogResult.No)
    {
        FileWork.CreateFile(ref notebox, ref tbChange, ref docPath);
    }
}
else
{
    FileWork.CreateFile(ref notebox, ref tbChange, ref docPath);
}

Открыть документ


if (tbChange == true)
{
    DialogResult message = MessageBox.Show("Сохранить текущий документ перед открытием нового?", "Открытие документа", MessageBoxButtons.YesNoCancel);
    if (message == DialogResult.Yes)
    {
        if (docPath != "")
        {
            FileWork.SaveFile(ref notebox, ref tbChange, ref docPath);
            FileWork.OpenFile(ref notebox, ref tbChange, ref docPath);
        }
        else if (docPath == "")
        {
            FileWork.SaveAsFile(ref notebox, ref tbChange, ref docPath);
            FileWork.OpenFile(ref notebox, ref tbChange, ref docPath);
        }
    }
    else if (message == DialogResult.No)
    {
        FileWork.OpenFile(ref notebox, ref tbChange, ref docPath);
    }
    else
    {
        return;
    }
}
else
{
    FileWork.OpenFile(ref notebox, ref tbChange, ref docPath);
}

Сохранить документ


if (docPath != "")
{
    FileWork.SaveFile(ref notebox, ref tbChange, ref docPath);
}
else
{
    FileWork.SaveAsFile(ref notebox, ref tbChange, ref docPath);
}

Сохранить документ как...


FileWork.SaveAsFile(ref notebox, ref tbChange, ref docPath);

Параметры страницы печати


if (pageSetupDialog.ShowDialog() == DialogResult.OK)
{
    printDocument.DefaultPageSettings = pageSetupDialog.PageSettings;
}

Печать документа


if (printDialog.ShowDialog() == DialogResult.OK)
{
    try
    {
        printDocument.Print();
    }
    catch (Exception)
    {
        MessageBox.Show("Ошибка параметров печати.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    }
}

Выход из программы


Application.Exit();

Стоит предусмотреть ситуацию, когда пользователь мог забыть сохранить документ или кнопка закрытия программы и вовсе была нажата случайно. В таком случае, если документ был изменен, но не сохранен - предложим ему сохранить документ перед закрытием. Для этого добавим в событие MainForm.cs "FormClosing" следующий код:

if (tbChange == true)
{
    DialogResult message = MessageBox.Show("Сохранить текущий документ перед выходом?", "Выход из программы", MessageBoxButtons.YesNoCancel);
    if (message == DialogResult.Yes)
    {
        if (docPath != "")
        {
            FileWork.SaveFile(ref notebox, ref tbChange, ref docPath);
            Application.Exit();
        }
        else if (docPath == "")
        {
            FileWork.SaveAsFile(ref notebox, ref tbChange, ref docPath);
            Application.Exit();
        }
    }
    else if (message == DialogResult.Cancel)
    {
        e.Cancel = true;
    }
}

Открытие документов нашим приложением через функцию "Открыть с помощью"


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

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

В коде MainForm.cs изменяем код:

public MainForm()
{
    InitializeComponent();
}

на:

public MainForm()
{
    InitializeComponent();
    this.Text = Properties.Settings.Default.newDocName + " - " + Properties.Settings.Default.programmName;
}
public MainForm(string fileName) // Открытие программы документом
{
    InitializeComponent();
    if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
    {
        try
        {
            string programmName = Properties.Settings.Default.programmName;
            FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            StreamReader reader = new StreamReader(file, Encoding.Default);
            notebox.Text = reader.ReadToEnd();
            reader.Close();
            docPath = fileName;
            tbChange = false;
            this.Text = Path.GetFileName(fileName) + " — " + programmName;
            notebox.Select(0, 0);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

После этого заходим в класс Program.cs и меняем строку:

Application.Run(new MainForm());

на:

String[] arguments = Environment.GetCommandLineArgs();
if (arguments.Length >=2)
{
    Application.Run(new MainForm(arguments[1]));
}
else
{
    Application.Run(new MainForm());
}

На этом работа с файлами в нашем текстовом редакторе завершена. В следующей части мы напишем код для правки текста в редакторе.