Все необходимые для выполнения данной работы программы можно найти в Setup.zip.
Для работы с файлами библиотека .NET Framework имеет достаточное количество классов. Познакомимся с ними. Простейшим уровнем файловых операций является получение информации о файлах и каталогах, смена каталогов, получение сведений о размерности файлов, времени создания, режиме доступа, удаления, переименования, копирования и т.д. без их открытия. Для этого имеются следующие библиотечные классы пространства имен System.IO ( input, output ) сборки mscorlib.dll:
Если взглянуть на классы Directory и File через панель Object Browse r, то можно убедиться, что эти классы имеют только статические методы, приведенные ниже. Предполагается, что если есть методы чтения Get, то есть и дуальные методы Set, которые в таблицах не указаны.
| Метод | Описание |
|---|---|
CreateDirectory() |
Создает каталог вместе с указанным путем |
Delete() |
Удаляет пустой каталог, если второй необязательный параметр не указан или равен false, то каталог с содержимым метод удалять отказывается. Если второй параметр указан и равен true, то удаляет каталог со всем содержимым |
Exists() |
Возвращает флаг существования каталога |
GetCreationTime(), GetLastAccessTime(), GetLastWriteTime() |
Возвращают структуру DateTime, которая содержит время создания каталога, время последнего чтения или записи |
GetDirectories(), GetFiles(), GetLogicalDrives() |
Возвращает массив строк, по одной на каждый подкаталог, файл или устройство в указанном каталоге (в зависимости от используемого метода). Методы имеют перегрузку, задающую шаблон (фильтр) поиска (например, My*.txt, My???.*, *My.exe, *my*). Регистр в шаблонах поиска значения не имеет |
GetParent() |
Анализирует переданную строку и возвращает имя родительского каталога |
GetCurrentDirectory() |
Возвращает текущий каталог с полным путем |
Move() |
Принимает два строки - исходный каталог и целевой каталог с полными путями. Перемещает исходный каталог в указанное место в пределах одного дискового устройства (иначе, - копирует) |
GetAccessControl() |
Возвращает объект со списком контроля доступа Windows |
| Метод | Описание |
|---|---|
Copy() |
Копирует один файл в другой с возможной заменой содержимого существующего файла (если задан третий необязательный параметр true ) |
Delete() |
Удаляет указанный файл, но не возбуждает исключение, если файл не найден |
Exists() |
Проверяет существование файла и возвращает флаг |
GetAttributes() |
Возвращает атрибуты указанного файла |
GetCreationTime(), GetLastAccessTime(), GetLastWriteTime() |
Возвращают структуру DateTime, которая содержит время создания файла, время последнего чтения или записи |
Move() |
Перемещает или переименовывает файлы |
Create(), CreateText() |
Создают файл |
Open(), OpenText(), OpenRead(), OpenWrite() |
Открывают файл, открывают только для чтения, открывают только для записи |
ReadAllText(), ReadAllLines(), ReadAllBytes() |
Читают весь файл и возвращают его содержимое в виде строки, массива строк или массива байт |
WriteAllText(), WriteAllLines(), WriteAllBytes() |
Пишут весь файл за один прием, использую принятую строку, массив строк или массив байт. Если файл существует, то он перезаписывается |
GetAccessControl() |
Возвращает объект со списком контроля доступа Windows |
Создадим приложение, которое извлекает список файлов текущего каталога сборки. Вывод имен файлов организуем на консоль и в списочный элемент Windows Forms одновременно.
Создайте новые решение и проект командой File/New/Project в соответствии со снимком
Командой Project/App1 Properties преобразуйте проект в консольное приложение настройкой списка Output type
Откройте файл Program.cs и добавьте в него код, выделенный в следующем листингеusing System;
using System.Collections.Generic;
using System.Windows.Forms;
// Добавляем пространство имен
using System.IO;
namespace App1
{
class Program // Убрали слово static!!!
{
[STAThread]
static void Main()
{
// Вызов нашей функции
(new Program()).ConsoleOutput();// Через экземпляр - для куража!
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
// Вывод информации в консольное окно
void ConsoleOutput()
{
// Настраиваем консоль
Console.Clear();
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
Console.WindowWidth = 70;// 80 знакомест - по умолчанию
Console.WindowHeight = 20;
Console.Title = "Список файлов каталога сборки";
string dir = Directory.GetCurrentDirectory(); // Каталог сборки
string[] fileList = Directory.GetFiles(dir); // Список файлов
int i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
Console.WriteLine("{0}) {1}", ++i, path);
}
Console.WriteLine();
i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
string file = path.Substring(path.LastIndexOf('\\') + 1);// Экранируем символ
Console.WriteLine("{0}) {1}", ++i, file);
}
Console.WriteLine();
i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
string file = path.Substring(path.LastIndexOf(@"\") + 1);// Неинтерпретируемая строка
Console.WriteLine("{0}) {1}", ++i, file);
}
Console.WriteLine();
i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
string file = Path.GetFileName(path); // Сразу чистое имя файла
Console.WriteLine("{0}) {1}", ++i, file);
}
}
}
}
Обратите внимание, что мы убрали перед объявлением класса ключевое слово static (для разнообразия). Поэтому нам пришлось создавать экземпляр класса, чтобы вызвать
Запустите приложение - консольное окно наполняется вариантами вывода имен файловВначале мы распечатали полные имена файлов, которые вернул метод Directory. GetFiles(). Такие имена иногда называют квалифицированными ( the fully qualified file name ), поскольку в файловой системе компьютера они однозначно определяют файлы как единицы информации. Для получения укороченного имени ( the relative file name ) мы использовали варианты, которых для решения одной и той же задачи обычно можно придумать несколько - обычно именно с таким запасом устроена библиотека .NET Framework.
Вывод в консольное окно мы организовали до того, как приложение войдет в цикл сообщений Windows, иначе консольное окно вначале появилось бы пустым и заполнилось бы только после закрытия диалогового окна. Но тогда все равно пришлось бы ставить задержку консольного окна, например, методом Console.ReadLine(), чтобы увидеть результаты до закрытия приложения.
Теперь настроим окно Windows Forms.
Перейдите в режим Design файла Form1.c s и поместите из панели Toolbox на макет формы элемент ListBox, который настройте через панель Properties в соответствии с приведенной таблицей свойств| Компонент | Свойство | Значение |
|---|---|---|
ListBox |
(Name) |
listBox1 |
Dock |
Fill |
Вызовите правой кнопкой мыши для макета формы контекстное меню и выполните команду View Code, чтобы перейти в кодовую часть формы. Заполните конструктор формы такusing System;
using System.Windows.Forms;
// Дополнительное подключение пространств имен
using System.IO;
namespace App1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Привязка массива к списку для отображения и печать результатов
String[] fileList = Directory.GetFiles(Directory.GetCurrentDirectory());
for (int i = 0; i < fileList.Length; i++)
fileList[i] = String.Format("{0}) {1}", i + 1, Path.GetFileName(fileList[i]));
listBox1.DataSource = fileList;
listBox1.SelectionMode = SelectionMode.None;
}
}
}
Запустите проект - результат должен быть таким (два окна на одном снимке)
Это приложение можно было бы довести до ума, дополнив диалоговым окном смены каталога, но мы пока рассматриваем инструменты пространства имен System.IO и отложем эту мысль на потом.
Эти классы прописаны в библиотеке с ключевым словом sealed (
Классы DirectoryInfo и FileInfo имеют общего родителя - абстрактный класс FileSystemInfo, который наделяет их базовым набором методов и свойств. Некоторые из этих членов приведены в таблице.
| Член | Описание |
|---|---|
Attributes |
Свойство позволяет извлекать и устанавливать атрибуты для файла или каталога, используя комбинацию значений перечисления FileAttributes:Archive - элемент помечен как архивныйCompressed - элемент сжат. Разрешено читать, но устанавливается только через механизмы ОСDevice - атрибут зарезервирован и пока не используетсяDirectory - элемент является каталогомEncrypted - элемент зашифрован . Для файла это значит, что все данные в нем зашифрованы. Для каталога это означает, что режим шифрования включен по умолчанию для вновь. создаваемых файлов и подкаталогов. Разрешено читать, но устанавливается только через механизмы ОСHidden - элемент скрытNormal - элемент без атрибутов. Этот атрибут устанавливается по умолчанию, когда сняты все другие атрибутыNotContentIndexed - элемент не будет индексирован службой индексации содержимого операционной системыOffline - файл находится в автономном состоянии и в данный момент недоступенReadOnly - элемент доступен только для чтенияSystem - элемент, используемый исключительно операционной системойTemporary - элемент помечен как временный и может быть удален после завершения работы приложенияReparsePoint, SparseFile - только для файловой системы NTFS |
CreationTime,
LastAccessTime,
LastWriteTime |
Свойство типа DateTime, определяющее временные характеристики файловой единицы (время создания, время последнего доступа и время последнего обновления) |
CreationTimeUtc,
LastAccessTimeUtc,
LastWriteTimeUtc |
То же, что предыдущее, только по Гринвичу |
Exists |
true, если для созданного объекта-наследника данных файл или каталог существует |
FullName |
Свойство get типа String, содержащее полное квалифицированное имя файла или каталога |
Name |
|
Extension |
Свойство get типа String, содержит только расширение файла |
Delete() |
Абстрактная заготовка для удаления файла или каталога в производных классах |
Refresh() |
Синхронизирует созданный ранее объект данных каталога или файла с последними изменениями файловой системы, выполненными через другие инструменты |
Экземплярные классы DirectoryInfo и FileInfo не только реализуют унаследованные члены, но и дополняют (расширяют) базовый класс FileSystemInfo своими уникальными членами. Некоторые из них приведены в таблицах ниже.
| Член | Описание |
|---|---|
Create() |
Создает каталог, указанный ранее в конструкторе при создании экземпляра DirectoryInfo |
CreateSubdirectory() |
Создает подкаталог |
Delete() |
Удаляет пустой каталог |
Delete(bool recursive) |
Удаляет каталог вместе с деревом подкаталогов, если аргумент равен true |
GetDirectories() |
Возвращает список объектов данных подкаталогов типа DirectoryInfo для текущего каталога. Имеет перегрузки, которые допускают использование шаблонов поиска, удовлетворяющих требуемому условию |
GetFiles() |
Возвращает список объектов данных типа FileInfo для файлов текущего каталога. Метод имеет перегрузки, позволяющие использовать стандартные символы шаблонов поиска '*' и '?'. Шаблон '?' представляет любой единственный символ, а шаблон '*' - любую последовательность символов из нуля и более, например, *.txt ищет все текстовые файлы |
Parent |
Свойство возвращает объект данных DirectoryInfo родительского каталога для указанного подкаталога |
Root |
Свойство возвращает объект данных DirectoryInfo, представляющий корневой каталог |
| Член | Описание |
|---|---|
Directory |
Возвращает экземпляр класса DirectoryInfo, представляющий каталог размещения файла |
DirectoryName |
Возвращает строку с полным именем родительского каталога |
Exists |
Возвращает флаг, определяющий, существует ли указанный файл |
IsReadOnly |
Булево свойство определяет или устанавливает атрибут файла "только для чтения" |
Length |
Длинное целое, возвращающее размер файла в байтах |
Name |
Переопределенное свойство содержит имя файла |
CopyTo() |
Копирует связанный с объектом данных файл в новое место с указанным именем и возвращает новый объект данных FileInfo, представляющий новую копию. Перегрузка с флагом разрешает перезапись, если целевой файл уже существует |
Create() |
Создает файл и возвращает объект-оболочку потока FileStream, который можно использовать для записи |
CreateText() |
Создает файл и возвращает объект-оболочку потока StreamWriter, который можно использовать для записи |
AppendText() |
Создает и возвращает поток StreamWriter для добавления текста в файл, связанный с объектом FileInfo |
Open(System.IO.FileMode) |
Возвращает объект FileStream и открывает файл в заданном режиме FileMode:Append - добавлять в конец файлаCreate - если файл несуществует, то создать, иначе очистить существующий - создает и открывает новый файл. Если файл уже существует, выдает исключениеOpen - требует открыть файл в режиме, установленном перечислением FileAccess. Если файл не существует, выдается исключение FileNotFoundExceptionOpenOrCreate - существующий файл открыть, иначе создать файлTruncate - открыть файл с очисткой до нулевой длины |
Open(System.IO.FileMode, System.IO.FileAccess) |
Возвращает объект FileStream. Представляет вариации открытия файла с вариациями доступа, определяемыми перечислением FileAccess:Read - только для чтенияReadWrite - для чтения и записиWrite - только для записи |
Open(System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare) |
Возвращает объект FileStream. Представляет вариации открытия файла с вариациями доступа, включая возможность одновременного доступа других объектов-потоков FileStream к данному файлу. Права других потоков при совместном доступе определяются перечислением FileShare:Delete - разрешено удаление файлаNone - не разрешать совместное использование файлаRead - разрешить сторонним потокам только чтениеReadWrite - разрешить совместные чтение и записьWrite - разрешить совместную запись |
OpenRead() |
Создает поток FileStream только для чтения и возвращает его |
OpenWrite() |
Создает поток FileStream только для записи и возвращает его |
OpenText() |
Создает и возвращает поток StreamReader для чтения в кодировке UTF8 из существующего текстового файла |
Encrypt() |
Шифрует файл |
|
Дешифрует файл |
Delete() |
Удаляет файл физически |
Replace() |
Заменяет содержимое указанного файла содержимым файла, связанного с текущим FileInfo. Целевой файл удаляется, а вместо него создается резервная копия с указанным именем, но параметры целевого файла возвращаются методом в виде объекта FileInfo |
Экземплярные классы DirectoryInfo и FileInfo имеют только параметризованные конструкторы, ожидающие строку каталога или файла с полным путем. Путь не обязательно должен соответствовать реально существующему файлу или каталогу. Если указанный путь не существует, то свойство Exists объекта будет иметь значение false.
Свойства объектов извлекают информацию из файловой системы только один раз при первом обращении. При следующих обращениях они не обновляют эту информацию. Это может привести к расхождению с действительностью, если файл в данный момент будет модифицирован сторонней программой, а не этим объектом. Для обновления информации в экземпляре нужно выполнить метод Refresh().
Если указанный файл или каталог не существует, то их можно создать. Ниже приведен пример такой функции.
static void CreateFile(string fileName) // Нужно полное имя файла
{
string file = Path.GetFileName(fileName); // Только имя файла
string dir = Path.GetDirectoryName(fileName); // Только полный путь
string path = Path.Combine(dir, file); // Опять fileName
/*
Console.WriteLine("fileName = {0}", fileName);
Console.WriteLine("file = {0}", file);
Console.WriteLine("dir = {0}", dir);
Console.WriteLine("path = {0}", path);
*/
DirectoryInfo myDir = new DirectoryInfo(dir);
FileInfo myFile = new FileInfo(fileName);
if (!myDir.Exists) // Если каталог не существует
{
myDir.Create(); // Создать каталог
myFile.Create(); // Создать файл
}
else if (!myFile.Exists)// Если файл не существует
myFile.Create(); // Создать файл
}
Вызов функции CreateFile() может быть таким
Program.CreateFile(@"C:\Tmp111\TTT\fileName.txt");
Методы GetFiles() и GetDirectories() классов Directory или DirectoryInfo с заданными шаблонами поиска или без них осуществляют поиск информации только для текущего каталога. Чтобы пройтись по всем вложенным подкаталогам, нужно применить рекурсивную логику поиска. В следующем примере используется рекурсивный вызов функции для подсчета размера файлов, находящихся в указанном каталоге и во всех подчиненных ему подкаталогах файлового дерева.
static long GetDirectorySize(DirectoryInfo directory, bool includeSubdirectories)
{
Console.WriteLine(directory.FullName);
long totalSize = 0;
// Просуммировать размеры файлов в текущем каталоге
FileInfo[] files = directory.GetFiles();
foreach (FileInfo file in files)
totalSize += file.Length;
// Рекурсивно обходим подкаталоги
if (includeSubdirectories)
{
DirectoryInfo[] dirs = directory.GetDirectories();
foreach (DirectoryInfo dir in dirs)
totalSize += GetDirectorySize(dir, true);
}
return totalSize;
}
Вызов функции GetDirectorySize() может быть таким
String path = @"E:\Tmp";
long totalSize = Program.GetDirectorySize(new DirectoryInfo(path), true);
Console.WriteLine(totalSize);
Свойство Attributes, наследуемое экземплярными классами DirectoryInfo и FileInfo от класса FileSystemInfo, устанавливает особенность поведения операционной системы по отношению к элементам файловой системы. Оно обеспечивает доступ к двухбайтному базовому слову, каждый бит которого соответствует определенной поведенческой установке, закрепленной за файлом или каталогом.
Метод ToString() свойства Attributes возвращает строку с именами атрибутов элемента, разделенными запятыми. Следующий код распечатывает названия атрибутов всех файлов заданного каталога
static void Main(string[] args)
{
String currentDirectory = @"E:\Tmp\XX";//Directory.GetCurrentDirectory(); // Каталог сборки
//Console.WriteLine(currentDirectory);
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files = directory.GetFiles(); // Список файлов
foreach (FileInfo file in files)
{
Console.WriteLine("\nАтрибуты файла {0}", file.Name);
String[] attributes = file.Attributes.ToString().Split(',');
for (int i = 0; i < attributes.Length; i++)
Console.WriteLine(attributes[i].Trim());
}
Console.ReadLine();
}
Для работы с атрибутами используются побитовые операции. Элементы перечисления FileAttributes представляют собой слово (2 байта), в котором только один бит равен единице. Их можно считать побитовыми масками, с помощью которых можно установить или прочитать атрибут. Некоторые атрибуты не могут быть установлены программно. Например, атрибуты FileAttributes. Encrypted и FileAttributes.Compressed устанавливаются самой операционной системой через специальные механизмы.
Прежде, чем двигаться дальше, вспомним поразрадные операции C#. Они позволяют работать с данными на уровне бит. Все поразрядные операции используются только с целыми типами данных ( byte, sbyte, int, uint, short, ushort, long, ulong, char, decimal ), у которых в машинном представлении нет плавающей точки.
Существует 6 поразрядных операций, проиллюстрированных в следующих таблицах
| Операция | Наименование | Описание |
|---|---|---|
| поразрядное И | Бит результата устанавливается в 1, если соответствующие биты обеих операндов равны 1 | |
| | | поразрядное ИЛИ | Бит результата устанавливается в 1, если соответствующий бит хотя бы одного операнда равен 1 |
| ^ | поразрядное ИСКЛЮЧАЮЩЕЕ ИЛИ | Бит результата устанавливается в 1, если соответствующиий бит одного и только одного операнда равен 1 |
| ~ | поразрядное НЕ | Унарная операция, которая применяется только к одному операнду. Меняет каждый бит с 1 на 0 и наоборот |
| << | поразрядный сдвиг влево | Сдвигает биты первого операнда влево на количество позиций, заданных вторым операндом. При этом освобождающиеся справа биты заполняются нулями |
| >> | поразрядный сдвиг вправо | Сдвигает биты первого операнда вправо на количество позиций, заданных вторым операндом. При этом освобождающиеся слева биты заполняются нулями |
| binary | 0x | |
|---|---|---|
| 0000 | 0000 | 0x00 |
| 0000 | 0001 | 0x01 |
| 0000 | 0010 | 0x02 |
| 0000 | 0011 | 0x03 |
| 0000 | 0100 | 0x04 |
| 0000 | 0101 | 0x05 |
| 0000 | 0110 | 0x06 |
| 0000 | 0111 | 0x07 |
| 0000 | 1000 | 0x08 |
| 0000 | 1001 | 0x09 |
| 0000 | 1010 | 0x0A |
| 0000 | 1011 | 0x0B |
| 0000 | 1100 | 0x0C |
| 0000 | 1101 | 0x0D |
| 0000 | 1110 | 0x0E |
| 0000 | 1111 | 0x0F |
| a mask | a | mask | a ^ mask | ~mask | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| a | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | ||||||||
| mask | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
| result | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
| result ^ mask | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | ||||||||||||||||||||||||
| a << 5 | a >> 5 | |||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| a | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 |
| result | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
| = | Операция присваивания поразрядного И |
|---|---|
| | = | Операция присваивания поразрядного ИЛИ |
| ^ = | Операция присваивания поразрядного исключающего ИЛИ |
| << = | Операция присваивания сдвига влево |
| >> = | Операция присваивания сдвига вправо |
Следующее упражнение иллюстрирует побитовую арифметику с атрибутами.
Добавьте к решению FilesAndDirectory проект консольного приложения App2 и назначьте его стартовым в Solution Explorer командой контекстного меню Set as StartUp Project
Заполните файл Program.cs следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
String currentDirectory = @"E:\Tmp\XX";//Directory.GetCurrentDirectory(); // Каталог сборки
//Console.WriteLine(currentDirectory);
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files1 = directory.GetFiles("*.txt"); // Список текстовых файлов
// Создаем маску с атрибутами
FileAttributes mask = FileAttributes.Archive
| FileAttributes.ReadOnly
| FileAttributes.System
| FileAttributes.Offline
| FileAttributes.NotContentIndexed
| FileAttributes.Hidden
| FileAttributes.Temporary
| FileAttributes.Device;
// Устанавливаем для всех файлов указанные атрибуты
foreach (FileInfo file in files1)
{
// Добавляем атрибуты
file.Attributes |= mask;
}
// Создаем новый объект для чистоты эксперимента и печатаем атрибуты
FileInfo[] files2 = directory.GetFiles("*.txt"); // Список текстовых файлов
foreach (FileInfo file in files2)
{
Console.WriteLine("\nАтрибуты файла {0}", file.Name);
String[] attributes = file.Attributes.ToString().Split(',');
for (int i = 0; i < attributes.Length; i++)
Console.WriteLine(attributes[i].Trim());
}
// Определяем, имеет ли первый файл коллекции files2 атрибут ReadOnly
if ((files2[0].Attributes FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
Console.WriteLine(Environment.NewLine // Новая строка
+ "Файл {0} имеет атрибут ReadOnly", files2[0].Name);
else
Console.WriteLine("\n" // Новая строка
+ "Файл {0} не имеет атрибут ReadOnly", files2[0].Name);
// Исключаем из первого объекта добавленные ранее атрибуты
foreach (FileInfo file in files1)
{
// Исключаем атрибуты
file.Attributes = ~mask; // Побитовое умножение на инвертированную маску
}
// Печатаем результат через второй объект
// Если открыть комментарные скобки - результат будет неверным!!!
//*
for (int i = 0; i < files2.Length; i++)
files2[i].Refresh(); // Приводим в соответствие с состоянием файлов
//*/
foreach (FileInfo file in files2)
{
Console.WriteLine("\nАтрибуты файла {0}", file.Name);
String[] attributes = file.Attributes.ToString().Split(',');
for (int i = 0; i < attributes.Length; i++)
Console.WriteLine(attributes[i].Trim());
}
// Определяем, имеет ли первый файл коллекции files2 атрибут ReadOnly
if ((files2[0].Attributes FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
Console.WriteLine(Environment.NewLine // Новая строка
+ "Файл {0} имеет атрибут ReadOnly", files2[0].Name);
else
Console.WriteLine("\n" // Новая строка
+ "Файл {0} не имеет атрибут ReadOnly", files2[0].Name);
Console.ReadLine();
}
}
}
Запустите приложение - вывод программы для нескольких файлов будет примерно следующимАтрибуты файла Test.txt ReadOnly Hidden System Archive Temporary Offline NotContentIndexed Атрибуты файла xx.txt ReadOnly Hidden System Archive Temporary Offline NotContentIndexed Файл Test.txt имеет атрибут ReadOnly Атрибуты файла Test.txt Normal Атрибуты файла xx.txt Normal Файл Test.txt не имеет атрибут ReadOnly
Обратите внимание, что как только мы сняли с файлов все значимые атрибуты, то для них по умолчанию установился атрибут Normal. Следовательно, для проверки того, что файл не имеет атрибутов, достаточно проверить наличие атрибута Normal.
Экземплярный класс, который извлекает информацию о дисковом устройстве компьютера. Имя устройства задается в конструкторе класса при создании экземпляра и представляет собой строку с символом от " a " до " z " в любом регистре. Ниже приведены члены класса DriveInfo.
| Член | Описание |
|---|---|
TotalFreeSpace |
Возвращает объем свободного пространства в байтах |
AvailableFreeSpace |
Возвращает объем свободного пространства в байтах, доступного для данного процесса |
DriveFormat |
Свойство возвращает имя файловой системы ( NTFS или FAT32 ) |
DriveType |
Возвращает значение одноименного перечисления DriveType:CDRom - устройство оптического диска, такое как CD или DVD-ROMFixed - фиксированный дискNetwork - сетевойNoRootDirectory - у диска нет корневого каталогаRam - устройство RAMRemovable - сменное устройство хранения данных, такое как флэшкаUnknown - тип диска неопределен |
IsReady |
Определяет готовность устройства к чтению или записи. Например, если нет компакт-диска в CD -приводе, свойство IsReady вернет false. Фиксированные устройства всегда готовы |
Name |
Возвращает строку с именем диска, например, C: или E: |
RootDirectory |
Возвращает объект DirectoryInfo с информацией о корневом каталоге диска |
TotalSize |
Получает полный размер пространства памяти на диске |
VolumeLabel |
Свойство, извлекающее или устанавливающее метку тома |
GetDrives() |
Статический метод, который возвращает массив объектов DriveInfo, представляющих все логические устройства данного компьютера |
Перечисленные свойства возвращают информацию только для устройства, которое находится в состоянии готовности, иначе обращение к ним генерирует исключение. Поэтому, прежде всего нужно проверять флаг IsReady и если он равен true, только тогда можно приступать к использованию других свойств.
Проиллюстрируем использование класса DriveInfo.
Добавьте к решению консольное приложение App3 и назначьте его стартовым
Заполните файл Program.cs следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DEBUG = System.Diagnostics.Debug;// Псевдоним класса
namespace App3
{
class Program
{
static void Main(string[] args)
{
// Получаем список всех логических устройств компьютера
DriveInfo[] drives = DriveInfo.GetDrives();
// Перебираем список и печатаем свойства
bool rootFlag = false; // Чтобы напечатать только раз
foreach (DriveInfo drive in drives)
{
String message = String.Format("Устройство '{0}'", drive.Name);
// Если готово, извлекаем характеристики
if (drive.IsReady)
{
message += " ГОТОВО" + Environment.NewLine;
if (!rootFlag)
{
message += String.Format("RootDirectory: {0}\n", drive.RootDirectory);
rootFlag = !rootFlag;// Отключаем
}
message += String.Format("VolumeLabel: {0}\n", drive.VolumeLabel);
message += String.Format("DriveType: {0}\n", drive.DriveType.ToString());
message += String.Format("DriveFormat: {0}\n", drive.DriveFormat);
message += String.Format("TotalSize: {0}\n", drive.TotalSize);
message += String.Format("AvailableFreeSpace: {0}\n", drive.AvailableFreeSpace);
message += String.Format("TotalFreeSpace: {0}\n", drive.TotalFreeSpace);
}
else
message += " НЕГОТОВО\n";
DEBUG.WriteLine(message);
}
Console.ReadLine();
}
}
}
Обратите внимание, что псевдонимы можно определять не только для пространств имен, но и для классов. Свойство RootDirectory экземпляра DriveInfo для любого устройства возвращает одно и то же значение, поэтому мы используем его только раз (чтобы не мешало). Если мы хотим исследовать только одно устройство и знаем его имя, то можно создать объект DriveInfo с помощью параметризованного конструктора, указав в качестве аргумента строку с именем, например, так
DriveInfo drive = new DriveInfo("C");
Имя устройства задается только символом от a до z в любом регистре и без дополнительных символов слэша и двоеточия. А далее все равно нужно проверить его готовность IsReady, прежде чем обращаться к другим свойствам.
Удостовертесь, что включен режим Debug оболочки (выпадающий список над рабочей областью)
Запустите проект и откройте панель Output. На моем компьютере получился такой результатУстройство 'C:\' ГОТОВО RootDirectory: C:\ VolumeLabel: 18125 DriveType: Fixed DriveFormat: NTFS TotalSize: 31457275904 AvailableFreeSpace: 473616384 TotalFreeSpace: 473616384 Устройство 'D:\' ГОТОВО VolumeLabel: DriveType: Removable DriveFormat: FAT32 TotalSize: 261105664 AvailableFreeSpace: 244381696 TotalFreeSpace: 244381696 Устройство 'E:\' ГОТОВО VolumeLabel: Data DriveType: Fixed DriveFormat: NTFS TotalSize: 462356992000 AvailableFreeSpace: 425508450304 TotalFreeSpace: 425508450304 Устройство 'F:\' ГОТОВО VolumeLabel: ГАИ - 24.01.2010 DriveType: CDRom DriveFormat: CDFS TotalSize: 161966080 AvailableFreeSpace: 0 TotalFreeSpace: 0 Устройство 'G:\' НЕГОТОВО Устройство 'H:\' НЕГОТОВО Устройство 'I:\' НЕГОТОВО Устройство 'J:\' НЕГОТОВО Устройство 'K:\' ГОТОВО VolumeLabel: DriveType: Removable DriveFormat: FAT32 TotalSize: 31994216448 AvailableFreeSpace: 7246184448 TotalFreeSpace: 7246184448
Вывод информации мы организовали в панель Output оболочки, поэтому консольное окно будет пустым.
Включите режим Release оболочки и повторите запуск проектаМы видим, что отладочный код работает только в режиме Debug и автоматически исключается компилятором в режиме Release из готовой исполнимой сборки приложения. Это очень удобно при тестировании проектов.
Иногда может возникнуть необходимость получить дополнительную информацию о файле, включающую в себя авторские права, номер версии, наименование компании-производителя, информацию о торговой марке и т.д. Эта информация вшивается в сборку при компиляции EXE - или DLL -файлов (если укажем). Данные отображаются в диалоговом окне свойств, вызванном в Windows Explorer при выделении исполнимого файла правой кнопкой мыши и выбора опции Свойства, например

Информация о сборке на этапе проектирования размещается в файле AssemblyInfo.cs, который автоматически создается оболочкой в новом проекте в каталоге Properties. Метаданные можно редактировать через этот файл. Такая необходимость чаще всего возникает в том случае, если программа (сборка или библиотека) делается для продажи или передачи другим людям.

Добавьте к решению командой File/Add/New Project новый проект консольного приложения с именем App4 и назначьте его стартовым
В панели Solution Explorer откройте на редактирование файл AssemblyInfo.cs и ознакомьтесь с его содержимым, автоматически сгенерированным оболочкойusing System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("App4")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("App4")]
[assembly: AssemblyCopyright("Copyright © 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("749a9e12-8b91-41d5-942a-e174fe60800f")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Мы видим набор атрибутов в глобальном пространстве имен, определяющих для сборки дополнительную информацию, которую называют метаданными (данные о данных). Ее можно редактировать в файле AssemblyInfo.cs напрямую, но лучше это делать через страницу свойств проекта.
Выполните команду Project/App4 Properties меню оболочки и выделите вкладку Application страницы свойств проекта
Щелкните на кнопке Assembly Information, чтобы вызвать диалоговое окно редактирования метаданных сборки с текстовыми полями:
Заполните диалоговое окно свойств как показано на рисунке
Вновь откройте файл AssemblyInfo.cs и убедитесь, что его содержимое изменилосьИзвлечь информацию из сборки можно программно. Для этого предназначен класс System.Diagnostics. FileVersionInfo. Продемонстрируем его использование на примере.
Заполните файл Program.cs приложения App4 следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DEBUG = System.Diagnostics.Debug; // Псевдоним класса
using SD = System.Diagnostics; // Создаем alias пространства имен (необязательно!)
using System.Diagnostics; // Подключаем пространство имен напрямую
namespace App4
{
class Program
{
static void Main(string[] args)
{
// Локальные (стековые) переменные инициализировать обязательно
String currentDirectory = Directory.GetCurrentDirectory(); // Каталог сборки
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files = directory.GetFiles("*.exe"); // Список файлов по шаблону
// Создаем массив для информации обо всех файлах
SD.FileVersionInfo[] info =
new System.Diagnostics.FileVersionInfo[files.Length];
// Извлекаем из всех файлов дополнительную информацию
for (int i = 0; i < files.Length; i++)
{
info[i] = FileVersionInfo.GetVersionInfo(files[i].FullName);// Просто 'Name' мало!
}
// Накапливаем результат для печати
String message = String.Empty;
for (int i = 0; i < info.Length; i++)
{
// Используем отражение для сокращения кода (и куража!)
System.Reflection.PropertyInfo[] props = // Общедоступные свойства
info[i].GetType().GetProperties();
for (int j = 0; j < props.Length; j++)
{
message += props[j].Name + ":\t"; // Имя свойства, ':' и отступ
// Выполняем свойство, чтобы извлечь его значение
Object prop = props[j].GetGetMethod().Invoke(info[i], null);
if (prop is String) // Динамическая идентификация типа (необязательно)
message += prop.ToString();
else if (prop != null)
message += String.Format("{0}", prop);
message += Environment.NewLine; // Новая строка
}
message += "\r\n"; // Новая строка
}
Console.Write(message + "\n"); // Печатаем на консоль (Write и новая строка)
DEBUG.WriteLine(message); // Печатаем в панель Output оболочки
Console.ReadLine(); // Задержка консоли
}
}
}
В приведенном коде мы применили методику отражения типов ( Reflection - рефлексию), чтобы не перебирать все свойства явно. Кроме того, использовали разные способы новой строки, для разнообразия.
Запустите приложение - вывод информации для файлов каталога исполнимой сборки данного проекта будет такимComments: Читает метаданные (Description) CompanyName: Рога и копыта (Company) FileBuildPart: 0 FileDescription: Приложение App4 (Title) FileMajorPart: 1 FileMinorPart: 0 FileName: E:\Tmp\FilesAndDirectory\App4\bin\Debug\App4.exe FilePrivatePart: 5678 FileVersion: 1.0.0.5678 InternalName: App4.exe IsDebug: False IsPatched: False IsPrivateBuild: False IsPreRelease: False IsSpecialBuild: False Language: Независимо от языка LegalCopyright: Copyright © 2010 (Copyright) LegalTrademarks: Ни-Бэ-Ни-Мэ (Trademark) OriginalFilename: App4.exe PrivateBuild: ProductBuildPart: 0 ProductMajorPart: 1 ProductMinorPart: 0 ProductName: Суперприложение (Product) ProductPrivatePart: 5678 ProductVersion: 1.0.0.5678 SpecialBuild: Comments: vshost.exe CompanyName: Microsoft Corporation FileBuildPart: 21022 FileDescription: vshost.exe FileMajorPart: 9 FileMinorPart: 0 FileName: E:\Tmp\FilesAndDirectory\App4\bin\Debug\App4.vshost.exe FilePrivatePart: 8 FileVersion: 9.0.21022.8 InternalName: vshost.exe IsDebug: False IsPatched: False IsPrivateBuild: False IsPreRelease: False IsSpecialBuild: False Language: Независимо от языка LegalCopyright: © Microsoft Corporation. All rights reserved. LegalTrademarks: OriginalFilename: vshost.exe PrivateBuild: ProductBuildPart: 21022 ProductMajorPart: 9 ProductMinorPart: 0 ProductName: Microsoft (R) Visual Studio (R) 2008 ProductPrivatePart: 8 ProductVersion: 9.0.21022.8 SpecialBuild:
Здесь, на первый взгляд, казалось бы, наблюдается парадокс - исполнимая сборка проверяет саму себя! Ну и что, порядок ее создания таков: компилятор транслирует код и записывает его в файл. Затем оболочка загружает файл в память и запускает процесс CLR, который анализирует указанный нами файл на диске в соответствии с заложенным алгоритмом. Указали бы другой исполнимый или библиотечный файл, процесс проверял бы его. Конечно, можно было бы сделать и диалог для выбора файла, но не в этом суть данного упражнения.
В каталоге сборки bin оболочка создает еще один файл App4.vshost.exe, который используется для оптимизации отладки, поддержки политик безопасности, более детального контроля за работой приложения. Его создание для любого из режимов ( Debug и Release ) можно отключить в настройках оболочки.
Откройте окно свойств проекта командой Project/App4 Properties и на вкладке Debug сбросьте внизу флажок Enable the Visual Studio hosting process
В панели Solution Explorer щелкните на локальной пиктограмме Show All Files, разверните дерево каталогов проекта и удалите файл App4.vshost.exe
Запустите приложение - теперь метаданные выводятся только для файла самой сборки App4.exe, поскольку файл App4.vshost.exe уже не создается
В панели Solution Explorer командой Exclude From Project контекстного меню для файла AssemblyInfo.cs исключите его из текущего проекта и вновь запустите приложениеМы видим, что метаданные в сборку на сей раз не попали.
Верните через панель Solution Explorer файл AssemblyInfo.cs обратно в проект командой контекстного меню Include In Project, переименуйте его в произвольное имя (расширение .cs должно остаться!) и вновь запустите приложение - метаданные есть, значит имя файла с атрибутами дополнительной информации для компилятора несущественно, он их находит из общего входного потока компиляции
Откройте файл AssemblyInfo.cs, командой Edit/Select All выделите все его содержимое и скопируйте в верхнюю часть файла Program.cs перед оператором ' namespace App4 '
Удалите в панели Solution Explorer файл AssemblyInfo.cs физически командой Delete контекстного меню
Запустите проект - метаданные существуют и их можно добалять непосредственно в коде в глобальном пространстве именМожно задаться вопросом, а куда же вшивается дополнительная информация в сборке? Давайте посмотрим, куда. Для этого проанализируем сборку
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe
Раскройте меню Tools оболочки и запустите ILDasm
Если такой команды не окажется, то ее следует добавить. Для этого:
Выполните команду меню Tools/External Tools (внешние инструменты) и заполните диалоговое окно External Tools как показано на снимке
Теперь осталось воспользоваться инструментом ILDasm и проанализировать нашу сборку App4.exe на предмет просмотра ранее введенных атрибутов.
Запустите дизассемблер ILDasm и командой File/Open откройте файл E:\Tmp\FilesAndDirectory\App4\bin\Debug\App4.exe (у меня такой...)
Выполните команду View/MetaInfo/Show! (или выполните жест Ctrl+M ) - в конце документа окна MetaInfo будет секция Assembly, в которой и находятся введенные нами ранее атрибуты сборкиВот примерно так будет выглядеть секция Assembly (несколько укороченная) в документе окна MetaInfo
Assembly
-------------------------------------------------------
Token: 0x20000001
Name : App4
Public Key :
Hash Algorithm : 0x00008004
Version: 1.0.0.1234
Major Version: 0x00000001
Minor Version: 0x00000000
Build Number: 0x00000000
Revision Number: 0x000004d2
Locale: <null>
Flags : [none] (00000000)
CustomAttribute #1 (0c000001)
-------------------------------------------------------
CustomAttribute Type: 0a000001
CustomAttributeName: System.Reflection.AssemblyTitleAttribute :: instance void .ctor(class System.String)
Length: 38
Value : 01 00 21 d0 9f d1 80 d0 b8 d0 bb d0 be d0 b6 d0 > ! <
: b5 d0 bd d0 b8 d0 b5 20 41 70 70 34 20 28 54 69 > App4 (Ti<
: 74 6c 65 29 00 00 >tle) <
ctor args: ("Приложение App4 (Title)")
CustomAttribute #2 (0c000002)
-------------------------------------------------------
CustomAttribute Type: 0a000002
CustomAttributeName: System.Reflection.AssemblyDescriptionAttribute :: instance void .ctor(class System.String)
Length: 52
Value : 01 00 2f d0 a7 d0 b8 d1 82 d0 b0 d0 b5 d1 82 20 > / <
: d0 bc d0 b5 d1 82 d0 b0 d0 b4 d0 b0 d0 bd d0 bd > <
: d1 8b d0 b5 20 28 44 65 73 63 72 69 70 74 69 6f > (Descriptio<
: 6e 29 00 00 >n) <
ctor args: ("Читает метаданные (Description)")
CustomAttribute #4 (0c000004)
-------------------------------------------------------
CustomAttribute Type: 0a000004
CustomAttributeName: System.Reflection.AssemblyCompanyAttribute :: instance void .ctor(class System.String)
Length: 39
Value : 01 00 22 d0 a0 d0 be d0 b3 d0 b0 20 d0 b8 20 d0 > " <
: ba d0 be d0 bf d1 8b d1 82 d0 b0 20 28 43 6f 6d > (Com<
: 70 61 6e 79 29 00 00 >pany) <
ctor args: ("Рога и копыта (Company)")
CustomAttribute #5 (0c000005)
-------------------------------------------------------
CustomAttribute Type: 0a000005
CustomAttributeName: System.Reflection.AssemblyProductAttribute :: instance void .ctor(class System.String)
Length: 45
Value : 01 00 28 d0 a1 d1 83 d0 bf d0 b5 d1 80 d0 bf d1 > ( <
: 80 d0 b8 d0 bb d0 be d0 b6 d0 b5 d0 bd d0 b8 d0 > <
: b5 20 28 50 72 6f 64 75 63 74 29 00 00 > (Product) <
ctor args: ("Суперприложение (Product)")
CustomAttribute #6 (0c000006)
-------------------------------------------------------
CustomAttribute Type: 0a000006
CustomAttributeName: System.Reflection.AssemblyCopyrightAttribute :: instance void .ctor(class System.String)
Length: 35
Value : 01 00 1e 43 6f 70 79 72 69 67 68 74 20 c2 a9 20 > Copyright <
: 20 32 30 31 30 20 28 43 6f 70 79 72 69 67 68 74 > 2010 (Copyright<
: 29 00 00 >) <
ctor args: ("Copyright © 2010 (Copyright)")
CustomAttribute #7 (0c000007)
-------------------------------------------------------
CustomAttribute Type: 0a000007
CustomAttributeName: System.Reflection.AssemblyTrademarkAttribute :: instance void .ctor(class System.String)
Length: 36
Value : 01 00 1f d0 9d d0 b8 2d d0 91 d1 8d 2d d0 9d d0 > - - <
: b8 2d d0 9c d1 8d 20 28 54 72 61 64 65 6d 61 72 > - (Trademar<
: 6b 29 00 00 >k) <
ctor args: ("Ни-Бэ-Ни-Мэ (Trademark)")
CustomAttribute #10 (0c00000a)
-------------------------------------------------------
CustomAttribute Type: 0a00000c
CustomAttributeName: System.Reflection.AssemblyFileVersionAttribute :: instance void .ctor(class System.String)
Length: 15
Value : 01 00 0a 31 2e 30 2e 30 2e 35 36 37 38 00 00 > 1.0.0.5678 <
ctor args: ("1.0.0.5678")
CustomAttribute #11 (0c00000b)
-------------------------------------------------------
CustomAttribute Type: 0a00000d
CustomAttributeName: System.Resources.NeutralResourcesLanguageAttribute :: instance void .ctor(class System.String)
Length: 10
Value : 01 00 05 72 75 2d 52 55 00 00 > ru-RU <
ctor args: ("ru-RU")
Более подробная информация о сборках приведена, например, в http://www.intuit.ru/department/internet/prdevwebms/19/
Статический класс System.IO. Path содержит статические методы и поля, которые выполняют общие задачи манипуляции с путями. Путем называется строка, которая предшествует укороченному имени файла или каталога перед конечным обратным слэшем ' \ ' (разделителем составного имени).
| Метод | Описание |
|---|---|
GetDirectoryName() |
Возвращает имя каталога по полному имени файла |
GetFullPath() |
Возвращает полное имя файла по укороченному имени |
GetFileName() |
Возвращает укороченное имя файла по полному имени |
GetFileNameWithoutExtension() |
Возвращает укороченное имя файла без расширения |
GetExtension() |
Возвращает расширение файла с разделителем '.' |
Combine() |
Объединяет путь и имя файла, вставляя между ними разделитеь '\' |
GetPathRoot() |
Возвращает имя устройства, например, 'C:\' |
GetRandomFileName() |
Генерирует имя файла случайным образом базового формата DOS |
GetTempFileName() |
Генерирует заготовку полного имени временного файла для размещения в стандартном каталоге, который (каталог), возможно, еще предстоит создать |
GetTempPath() |
Генерирует заготовку полного имени каталога для размещения временных файлов, который, возможно, еще предстоит создать |
AltDirectorySeparatorChar, DirectorySeparatorChar, PathSeparator, VolumeSeparatorChar |
Общедоступные статические поля, содержащие образцы разделителей, используемых в системе: '/' '\' ';' ':' |
Добавьте к решению проект ( Console Application ) с именем App5 и назначьте его стартовым. Для этого выполните через меню оболочки вначале команду File/Add/New Project, а затем сразу команду Project/Set as StartUp Project (пока созданный проект оболочка считает текущим)
Заполните файл Program.cs следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DEBUG = System.Diagnostics.Debug; // Псевдоним класса
namespace App5
{
class Program
{
static void Main(string[] args)
{
String currentDirectory = Directory.GetCurrentDirectory(); // Каталог сборки
String message = "Без использования Path\n".ToUpper();
message += "currentDirectory: " + currentDirectory + "\n";
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files = directory.GetFiles("*.exe"); // Список файлов по шаблону
FileInfo file;
if (files.Length > 0)
file = files[0]; // Берем первый попавшийся для анализа
else
return; // Нечего анализировать
string fullName = file.FullName;
string name = file.Name;
message += "fullName: " + fullName + "\n";
message += string.Format("name: {0}\n", name);
message += string.Format("\nС использованием Path\n").ToUpper();
String path;
message += "path = Path.GetDirectoryName(fullName): "
+ (path = Path.GetDirectoryName(fullName)) + "\n";
message += "Path.GetFullPath(name): "
+ Path.GetFullPath(name) + "\n";
message += "Path.GetFileName(fullName): "
+ Path.GetFileName(fullName) + "\n";
message += "Path.GetFileNameWithoutExtension(fullName): "
+ Path.GetFileNameWithoutExtension(fullName) + "\n";
message += "Path.GetExtension(fullName): "
+ Path.GetExtension(fullName) + "\n";
message += "Path.Combine(path, name): "
+ Path.Combine(path, name) + "\n";
message += "Path.GetPathRoot(path): "
+ Path.GetPathRoot(path) + "\n";
message += "Path.GetPathRoot(fullName): "
+ Path.GetPathRoot(fullName) + "\n";
message += "Path.GetRandomFileName(): "
+ Path.GetRandomFileName() + "\n";
message += "Path.GetRandomFileName(): "
+ Path.GetRandomFileName() + "\n";
message += "Path.GetRandomFileName(): "
+ Path.GetRandomFileName() + "\n";
message += "Path.GetTempFileName(): "
+ Path.GetTempFileName() + "\n";
message += "Path.GetTempFileName(): "
+ Path.GetTempFileName() + "\n";
message += "Path.GetTempPath(): "
+ Path.GetTempPath() + "\n";
message += "AltDirectorySeparatorChar: '"
+ Path.AltDirectorySeparatorChar + "'\n";
message += "DirectorySeparatorChar: '"
+ Path.DirectorySeparatorChar + "'\n";
message += "PathSeparator: '"
+ Path.PathSeparator + "'\n";
message += "VolumeSeparatorChar: '"
+ Path.VolumeSeparatorChar + "'\n";
String unsafeFileName = "..\\..\\..\\" + name; // Опасное имя файла
String safeFileName = Path.GetFileName(unsafeFileName);
message += "\nОпасное имя файла: " + unsafeFileName + "\n";
message += "Безопасное имя файла: " + safeFileName + "\n";
message += "Полное опасное имя: " + Path.Combine(path, unsafeFileName) + Environment.NewLine;
message += "Полное опасное имя: " + Path.GetFullPath(Path.Combine(path, unsafeFileName)) + "\n";
message += "Полное безопасное имя: " + Path.Combine(path, safeFileName);
// Печать результатов
Console.WriteLine(message); // На консоль
DEBUG.WriteLine(message); // На панель Output
Console.ReadLine(); // Задержка окна
}
}
}
Запустите проект - результат должен быть примерно такимБЕЗ ИСПОЛЬЗОВАНИЯ PATH currentDirectory: E:\Tmp\FilesAndDirectory\App5\bin\Debug fullName: E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe name: App5.exe С ИСПОЛЬЗОВАНИЕМ PATH path = Path.GetDirectoryName(fullName): E:\Tmp\FilesAndDirectory\App5\bin\Debug Path.GetFullPath(name): E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe Path.GetFileName(fullName): App5.exe Path.GetFileNameWithoutExtension(fullName): App5 Path.GetExtension(fullName): .exe Path.Combine(path, name): E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe Path.GetPathRoot(path): E:\ Path.GetPathRoot(fullName): E:\ Path.GetRandomFileName(): epviufbp.0k1 Path.GetRandomFileName(): tyzaz4fb.so2 Path.GetRandomFileName(): ucnivb2v.idh Path.GetTempFileName(): C:\Users\CHETKOB\AppData\Local\Temp\tmp15B4.tmp Path.GetTempFileName(): C:\Users\CHETKOB\AppData\Local\Temp\tmp15B5.tmp Path.GetTempPath(): C:\Users\CHETKOB\AppData\Local\Temp\ AltDirectorySeparatorChar: '/' DirectorySeparatorChar: '\' PathSeparator: ';' VolumeSeparatorChar: ':' Опасное имя файла: ..\..\..\App5.exe Безопасное имя файла: App5.exe Полное опасное имя: E:\Tmp\FilesAndDirectory\App5\bin\Debug\..\..\..\App5.exe Полное опасное имя: E:\Tmp\FilesAndDirectory\App5.exe Полное безопасное имя: E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe
Построим диалоговое приложение, позволяющее перемещаться по файловой системе и просматривать характеристики файлов. С оформлением пользовательского интерфейса сильно стараться не будем, а уделим больше внимания функциональности.
Добавьте к решению командой File/Add/New Project новый проект оконного приложения Windows Forms с именем App6 и назначьте его стартовым
В режиме Design выделите форму и установите для нее в панели Properties значения свойств
В режиме Design выделите форму и поместите из панели Toolbox дочерний компонент Panel (двойным щелчком на нем, или перетащите курсором). Установите для него в панели Properties значения свойств
Опять выделите саму форму, щелкнув на ее заголовке (или свободном месте клиентской области), поместите еще один дочерний элемент Panel и установите для него свойство
Через выпадающий список панели Properties выделите объект panel1, поместите на него дочерний компонент Button и настройте свойства так:
Снова выделите на форме объект panel1 и добавьте на него текстовую метку Label со свойствами:
Выделите объект panel2, поместите на него дочерний компонент SplitContainer и сместите у него разделитель влево, чтобы ширина его левой панели ( Panel1 ) была меньше ширины правой ( Panel2 ) и составляла от нее примерно одну четверть
Выделите левую панель ( Panel1 ) объекта splitContainer1 и поместите в нее компонент ListBox со свойствами:
Выделите правую панель ( Panel2 ) объекта splitContainer1 и поместите в нее компонент DataGridView со свойствами:На этом разработка пользовательского интерфейса закончена. Остальное, если возникнет необходимость, мы донастроим в процедурном коде, а пока форма будет выглядеть примерно так

Щелкните правой кнопкой мыши в любом месте на форме и выполните команду контекстного меню View Code, чтобы открыть кодовую часть - файл Form1.cs
Заполните файл Form1.cs следующим кодомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace App6
{
public partial class Form1 : Form
{
Panel fileDetails;// Поле, инициализируется неявно нулевым адресом: null
public Form1()
{
InitializeComponent();
// Программные донастройки
gridFile.ReadOnly = true; // Только для просмотра
gridFile.SelectionMode =
DataGridViewSelectionMode.FullRowSelect;// Выделять всю строку
// Начальная информация
currentPath = Directory.GetCurrentDirectory();
ShowDirectoryContents(currentPath);
fileDetails = splitContainer1.Panel2;
fileDetails.AutoScroll = true;
}
String currentPath;// Поле, инициализируется неявно пустой строкой
void ShowDirectoryContents(String path)
{
// Получить объект справки текущего каталога
DirectoryInfo curDir = new DirectoryInfo(path);
// Получить для текущего каталога
// объекты справки файлов и подкаталогов
FileInfo[] files = curDir.GetFiles();
DirectoryInfo[] dirs = curDir.GetDirectories();
lblCurrentDir.Text = "Текущий каталог: " + path;
// Привязываем к элементам отображения
listDir.DataSource = dirs;
listDir.DisplayMember = "Name"; // Отображаемый вектор
listDir.ValueMember = "Name"; // Скрытый вектор
gridFile.DataSource = files; // Показываем все
}
private void btnUp_Click(object sender, EventArgs e)
{
// Уже "Мой компьютер" и выше некуда
if (currentPath == String.Empty)
return;
// Хотим подняться выше текущего корня,
// поэтому выводим доступные устройства
else if (currentPath == Path.GetPathRoot(currentPath))
{
System.Collections.ArrayList array =
new System.Collections.ArrayList();
DriveInfo[] drives = DriveInfo.GetDrives();
// Фильтруем готовые устройства
foreach (DriveInfo drive in drives)
{
if (drive.IsReady)
{
String name = drive.Name;
name = name.Split('\\')[0];// Убираем слэш
array.Add(name);
}
}
listDir.DataSource = array; // Привязываем список доступных устройств
gridFile.DataSource = null; // Нет файлов
currentPath = String.Empty; // Пути нет
lblCurrentDir.Text = "Мой компьютер"; // Меняем заголовок метки
return;
}
// Находимся ниже корня текущего устройства
// и есть куда подниматься
else
{
String path = Path.Combine(currentPath, "..");
currentPath = Path.GetFullPath(path); // Этажом выше без '..'
ShowDirectoryContents(currentPath);
}
}
private void listDir_DoubleClick(object sender, EventArgs e)
{
String dir = listDir.SelectedValue.ToString();// Извлекаем из списка
currentPath = Path.Combine(currentPath, dir);
if (currentPath.IndexOf('\\') == -1)
currentPath += "\\"; // Если только что спустились в корень
ShowDirectoryContents(currentPath);
}
private void gridFile_Click(object sender, EventArgs e)
{
if (gridFile.Rows.Count == 0)
return; // Нечего выделять
// Локальные переменные инициализировать обязательно, а поля нет
int selectIndex = gridFile.CurrentCell.RowIndex;
DataGridViewRow row = gridFile.Rows[selectIndex];
DataGridViewCellCollection cells = gridFile.Rows[selectIndex].Cells;
String message = "";
// Извлекаем информацию из текущей строки
for (int i = 0; i < gridFile.Columns.Count; i++)
{
String columnName = gridFile.Columns[i].Name;
String columnValue = row.Cells[i].Value.ToString();
message += columnName + ": " + columnValue + "\n";
}
// Создаем окно и выводим информацию
Form frm = new Form();
frm.Width = 500;
frm.Height = 320;
frm.Text = "Подробности"; // Заголовок окна
frm.MinimizeBox = false; // Убираем системные кнопки
frm.MaximizeBox = false; // Убираем системные кнопки
frm.ShowInTaskbar = false; // Не отображать в панели задач
frm.StartPosition = FormStartPosition.CenterParent;// По центру родителя
// Промежуточная панель - для скролирования
Panel panel = new Panel();
panel.BackColor = System.Drawing.Color.White;
panel.Dock = DockStyle.Fill;
panel.AutoScroll = true;
frm.Controls.Add(panel);// Отдаем окну
// Отображающая текстовая метка
Label lbl = new Label();
lbl.AutoSize = true;
lbl.Parent = panel; // Отдаем панели
// panel.Controls.Add(lbl);// Вариант - отдаем панели
lbl.Text = message;
// Заряжаем дополнительные окна вывода раньше диалога
Console.Clear(); Console.Write(message); // !!!!!!!!!!!!!!!!!!!!
System.Diagnostics.Debug.Write(message + Environment.NewLine);
frm.ShowDialog(); // Показываем окно в модальном режиме
}
}
}
Запустите приложение - пока оно не реагирует на действия пользователя, поскольку интерфейсные элементы не присоединены к своим обработчикам
В режиме Design выделите кнопку btnUp, зайдите в панель Properties, щелкните на пиктограмме Events, раскройте список поля для события Click и выберите обработчик btnUp_Click
Подобным же образом присоедините к событию DoubleClick объекта списка listDir обработчик listDir_DoubleClick, и к событию Click объекта сетки gridFile - обработчик gridFile_Click
Запустите приложение - при щелчке на строке сетки выбрасывается исключение
Причина в том, что в коде обработчика gridFile_Click делается попытка вывода информации в консольное окно, которое не создано. Если мы не хотим использовать консольное окно, то следует закомментировать или удалить строку
Console.Clear(); Console.Write(message); // !!!!!!!!!!!!!!!!!!!!
Но мы добавим к приложению консольное окно, чтобы вспомнить, как это делается.
В панели Solution Explorer для узла App6 вызовите контекстное меню и выполните команду ProperiesДругой способ - в панели Solution Explorer выделить узел проекта App6 (или любой член проекта App6 ) и выполнить команду главного меню оболочки Project/App6 Properties.
На вкладке Application окна настроек проекта в выпадающем списке Output type установите значение Console Application
Запустите и испытайте разработанное приложениеОдин из рабочих моментов может выглядеть так

Еще один канал вывода подробностей, предусмотренный нами, - панель Output оболочки.
Разберитесь с кодомИногда может потребоваться упорядоченный список номеров, сгенерированных случайным образом. В данном упражнении мы разработаем такую утилиту, используя рассмотренные классы работы с файлами. Номера будем размещать в текстовом файле. Настройку утилиты организуем через диалоговое окно, а для контроля процесса добавим консольный вывод.
Добавьте к решению командой File/Add/New Project новый проект типа Windows Forms Application с именем App7 и назначьте его стартовым
Командой Project/App7 Properties вызовите диалоговое окно настройки проекта и в выпадающем списке Output type вкладки Application установите значение Console Application
Разработайте интерфейс диалогового окна в соответствии с таблицей свойств| Элемент | Свойство | Значение | Пояснения |
|---|---|---|---|
Form |
FormBorderStyle |
FixedDialog |
Фиксированный размер |
Size |
375; 385 | Ширина и высота окна | |
MaximizeBox |
False |
Скрыть системную кнопку | |
MinimizeBox |
False |
Скрыть системную кнопку | |
Text |
Генератор случайных номеров | Заголовок окна | |
StartPosition |
CenterScreen |
Начальное положение | |
Font |
Microsoft Sans Serif; 7,8pt |
Родительский шрифт (по умолчанию) | |
Label |
AutoSize |
False |
Подстройка размера под содержимое |
BackColor |
Info |
Цвет фона | |
Text |
Генерирует список номеров случайным образом | Содержимое | |
Font |
Arial; 12pt; style=Bold |
Шрифт | |
ForeColor |
Blue |
Цвет текста | |
TextAlign |
MiddleCenter |
Центрирование текста | |
Location |
16; 8 | Позиция относительно клиентской области формы | |
Label |
Text |
Куда разместить файл: | |
Button |
(Name) |
btnDirTarget |
Имя экземпляра кнопки |
Text |
Browse |
Метка кнопки | |
TextBox |
(Name) |
txtDirTarget |
Путь к файлу |
Label |
Text |
Имя текстового файла: | Метка для txtFileName |
TextBox |
(Name) |
txtFileName |
Текстовое поле для имени файла |
Text |
ResultNumber.txt |
Имя файла по умолчанию | |
Label |
Text |
Нижняя граница диапазона: | Текстовая метка |
TextBox |
(Name) |
txtMin |
Текстовое поле для нижней границы генерируемых номеров |
Text |
0 | Значение по умолчанию | |
Label |
Text |
Постоянный шаг: | Текстовая метка |
TextBox |
(Name) |
txtStep |
Дискрета приращения диапазона |
Text |
250 | Шаг по умолчанию | |
Label |
Text |
Количество точек: | Текстовая метка |
TextBox |
(Name) |
txtCount |
Число генерируемых номеров |
Text |
2000 | Значение по умолчанию | |
Label |
Text |
Верхняя граница диапазона: | Метка |
TextBox |
(Name) |
txtMax |
Расчет верхней границы |
Text |
Пусто | ||
ReadOnly |
True |
Только для чтения | |
Button |
(Name) |
btnExecute |
Кнопка выполнения |
Text |
Execute |
Метка кнопки |
Необязательно дословно следовать приведенной таблице, можно настроить окно и по своему. Главное, чтобы значимые элементы имели те же имена для согласованного управления в коде (который, я уверен, вами будет скопирован механически - программисты должны экономить свои силы, но лучше тогда, когда станут дорого стоить). При размещении элементов на форме важно следить за тем, чтобы они не были вложенными друг в друга, а каждый принадлежал только форме как своему ближайшему родителю.
Запустите приложение - пользовательский интерфейс будет выглядеть так
Создадим и заполним обработчики элементов управления в процедурном коде файла Form1.cs.
Добавьте в начало файла Form1.cs подключение библиотечного пространства имен System. IO для классов ввода-выводаusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Подключение дополнительных пространств имен
using System.IO;
В режиме Design двойным щелчком на заголовке формы (или ином незанятом компонентами месте) создайте обработчик начальной загрузки и заполните его такint min, step, count, max;
FolderBrowserDialog dialogFolder = new FolderBrowserDialog();
private void Form1_Load(object sender, EventArgs e)
{
if (txtDirTarget.Text == String.Empty ||
Directory.Exists(txtDirTarget.Text) == false)
{
dialogFolder.RootFolder =
System.Environment.SpecialFolder.MyComputer;
dialogFolder.SelectedPath = "";
txtDirTarget.Text = String.Empty;
}
else
{
dialogFolder.SelectedPath = txtDirTarget.Text;
}
dialogFolder.Description =
"Выберите место размещения текстового файла";
min = Convert.ToInt32(txtMin.Text);
step = Convert.ToInt32(txtStep.Text);
count = Convert.ToInt32(txtCount.Text);
max = min + step * count;
txtMax.Text = max.ToString();
}
За пределы функции вынесены поля-члены для их видимости в других обработчиках класса. В самом обработчике выполняется начальная инициализация этих переменных. Для выбора каталога используется стандартный диалог FolderBrowserDialog, который при запуске приложения устанавливается на вершину файловой системы компьютера, если выполняются указанные условия. Для явного преобразования типов используется библиотечный класс System.Convert.
В режиме Design выделите кнопку btnDirTarget, откройте панель Properties, щелкните на пиктограмме Events, найдите событие Click и двойным щелчком на правой ячейке сетки свойств создайте обработчик, который заполните такprivate void btnDirTarget_Click(object sender, EventArgs e)
{
dialogFolder.ShowDialog();
txtDirTarget.Text = dialogFolder.SelectedPath;
}
Обработчик запускает диалог выбора папки, при закрытии которого путь сохраняется и отображается в текстовом поле txtDirTarget. Далее мы планируем этот путь использовать для создания текстового файла с именем из поля txtFileName. Выбранный путь записывается в поле.
Здесь есть одна тонкость. Текстовое поле может редактироваться пользователем и, таким образом, может содержать недостоверный путь. Можно, конечно, создавать каталог программно, если он не существует, но для тренировки мы просто заблокируем это поле для редактирования. Если присвоить txtDirTarget свойство ReadOnly=True, то оно будет серым и некрасивым. Другая возможность - просто запретить ввод символов в это поле, что мы сейчас и сделаем.
Выделите текстовое поле txtDirTarget, в панели Properties установите режим Events и двойным щелчком на поле значений события KeyPress создайте обработчик со следующим кодомprivate void txtDirTarget_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = true;
}
Запустите приложение, выберите папку и попробуйте редактировать поле - стрелки и клавиша Delete продолжают функционироватьДобъем это поле окончательно.
Выделите текстовое поле txtDirTarget, в панели Properties установите режим Events и двойным щелчком на поле значений события KeyDown создайте обработчик со следующим кодомprivate void txtDirTarget_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
Запустите приложение - теперь поле полностью нередактируется, хотя и принимает курсор и фокус вводаТаким образом, мы видим, что с помощью событий KeyDown и KeyPress можно контролировать пользовательский ввод в экземпляр текстового поля. Применим этот факт для фильтрации цифровых клавиш в текстовых полях txtMin, txtStep и txtCount, которые должны принимать только целые числа. Создадим для них общие обработчики, поскольку условие ограничения по вводу только цифр для них общее. Но прежде нам нужно узнать, в какой последовательности срабатывают события KeyDown и KeyPress.
В режиме Design выделите (можно частично) с помощью лассо курсора (по русски - просто обведите, а используемое здесь словоблудие, чтобы привыкали к терминам) сразу 3 упомянутых текстовых поля и в панели Properties/Events задайте для них имена обработчиков, как показано на снимке
Заполните созданные обработчики кодом вывода информации на консольprivate void txt_KeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine("KeyDown");
}
private void txt_KeyPress(object sender, KeyPressEventArgs e)
{
Console.WriteLine("KeyPress");
}
Запустите приложение - получится последовательностьKeyDown KeyPress
Зная эту последовательность срабатывания, можно запрограммировать фильтр для пропуска только цифр.
Заполните общие обработчики следующим кодомbool isNumber;
private void txt_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
isNumber =
e.KeyCode >= Keys.D0 e.KeyCode <= Keys.D9 // keyboard - основная клавиатура
|| e.KeyCode >= Keys.NumPad0 e.KeyCode <= Keys.NumPad9 // keypad - дополнительная клавиатура
|| e.KeyCode == Keys.Back;
}
private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
// Запрещаем в текстовом поле лишние символы
if (!isNumber)
e.Handled = true;
}
В первом обработчике формируется флаг isNumber, а во втором обработчике этот флаг используется для отфильтровывания цифр. Последовательность срабатывания событий обеспечивает нам нужный конвейер действий.
Создадим еще один обработчик, в котором 'на лету' будет пересчитываться значение последнего текстового поля txtMax по данным других полей. Но прежде добавим маркеры исходных текстовых полей в код начальной инициализации, чтобы в общем обработчике можно было их идентифицировать (различать) и индивидуально обрабатывать.
Добавьте в функцию Form1_Load() следующий кодprivate void Form1_Load(object sender, EventArgs e)
{
...................................................
// Маркируем элементы для распознавания в общем обработчике
txtMin.Name = "Min";
txtStep.Name = "Step";
txtCount.Name = "Count";
}
В режиме Design выделите все три поля txtMin, txtStep и txtCount одновременно и создайте для них в панели Properties для события TextChanged общий обработчик с именем (нужно вручную ввести в поле) result_TextChanged, который заполните такprivate void result_TextChanged(object sender, EventArgs e)
{
// Следим, чтобы редактируемое поле не оказалось пустым
TextBox txtBox = (TextBox)sender;// Приводим ссылку к типу элемента
if (txtBox.Text == String.Empty)
{
switch (txtBox.Name)
{
case "Min":
txtMin.Text = "0";
break;
case "Step":
txtStep.Text = "0";
break;
case "Count":
txtCount.Text = "0";
break;
}
}
// Конвертируем разными способами
min = Convert.ToInt32(txtMin.Text);
step = Int32.Parse(txtStep.Text);
int.TryParse(txtCount.Text, out count);
max = min + step * count;
txtMax.Text = max.ToString();
}
В этом коде скрыт один коварный нюанс, на который следует обратить внимание. Дело в том, что текстовое поле способно принимать сколь угодно длинные тексты. А в нашем коде обработчика result_TextChanged набор цифр преобразуется в целый тип Int32, имеющий конечную длину. Соответственно, длинный ввод пользователя может вызвать исключение. Не погружаясь глубоко в эту проблему, в которой нужно было бы еще учесть операции умножения предельно допустимых значений ввода (или изменить тип int на long ), просто ограничим количество вводимых цифр барьером, способным уместиться в целом типе. Это явно приближенный подход, но просто для примера, только для тренировки!
Модифицируйте обработчик txt_KeyPress( ) полей ввода цифр такБыло
private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
// Запрещаем в текстовом поле лишние символы
if (!isNumber)
e.Handled = true;
}
Стало
private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
TextBox txtBox = sender as TextBox;// Приведение ссылки
int maxLength = int.MaxValue.ToString().Length - 1;// Длина ввода (с запасом)
// Запрещаем в текстовом поле лишние символы
// и ограничиваем длину пользовательского ввода
if (!isNumber || txtBox.Text.Length >= maxLength)
e.Handled = true;
}
Теперь осталось запрограммировать кнопку btnExecute и выполнить небольшие действия по сохранению состояния приложения.
В режиме Design двойным щелчком на кнопке btnExecute создайте обработчик события по умолчанию, который заполните такprivate void btnExecute_Click(object sender, System.EventArgs e)
{
String fileName = txtFileName.Text; // Копируем ссылку для удобства
// Заполнено ли текстовое поле
if (fileName.Length <= 0 || txtDirTarget.Text == String.Empty)
{
MessageBox.Show("Вы не задали имя файла или каталога",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
// Есть ли расширение txt
int pos0 = fileName.LastIndexOf('.');// Ищем с конца
if (pos0 == -1 || !String.Equals(fileName.Substring(pos0), ".txt"))
{
MessageBox.Show("Вы не задали расширение .txt",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
// Не допускать составное имя
int pos1 = fileName.IndexOf('.'); // Ищем с начала
if (pos0 != pos1) // Если точек несколько
{
MessageBox.Show("Задайте одно расширение .txt",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
// Добавляем путь = полное имя файла
fileName = Path.Combine(txtDirTarget.Text, fileName);
if (!File.Exists(fileName))
{
FileStream stream = File.Create(fileName);// Создать файл
stream.Close(); // Освободить ресурс
}
Console.Clear(); // Очистить консоль
Random rand = new Random();// Генератор случайных чисел по равномерному закону
int result;
StreamWriter writeFile = new StreamWriter(fileName, false,
System.Text.ASCIIEncoding.GetEncoding(1251));
for (int i = 0; i < count; i++)
{
result = min + i * step + rand.Next(step);
Console.WriteLine(result.ToString()); // Выводим на консоль
writeFile.WriteLine(result.ToString()); // Выводим в файл
}
writeFile.WriteLine("--------"); // Отделяем итог
writeFile.WriteLine(count.ToString() + " шт.");
writeFile.Close(); // Освободить ресурс
}
Запустите приложение - все должно работатьВ этом месте мне пришла мысль добавить еще одно текстовое поле для ввода серии (как в паспортах). Для этого нужно немного переделать пользовательский интерфейс формы. Заодно вспомним групповые операции выравнивания визуального конструктора форм.
В режиме Design не меняя размеров формы сдвиньте (стрелкой 'Вниз') кнопку Execute немного вниз
Охватите лассо курсора четыре нижних текстовых поля вместе с метками (до 'Имя текстового файла:') и тоже стрелкой 'Вниз' сдвиньте их вниз
Выделите текстовую метку 'Имя текстового файла:' вместе с ее полем, выполните Copy и сразу Paste
Выделите все пять текстовых полей и щелкните на кнопке Make Vertical Spacing Equal панели инструментов Layout, чтобы равномерно распределить элементы
То же самое действие примените к текстовым меткам
Настройте новые элементы в соответствии с стаблицей свойств| Элемент | Свойство | Значение |
|---|---|---|
Label |
Text |
Серия: |
TextBox |
( Name ) |
txtSeries |
Text |
Пусто |
Запустите приложение - интерфейс должен выглядеть примерно так
Теперь осталось включить значение серии в вывод программы.
Отредактируйте фрагмент кода обработчика btnExecute_Click( ) такБыло
for (int i = 0; i < count; i++)
{
result = min + i * step + rand.Next(step);
Console.WriteLine(result.ToString()); // Выводим на консоль
writeFile.WriteLine(result.ToString()); // Выводим в файл
}
Стало
for (int i = 0; i < count; i++)
{
result = min + i * step + rand.Next(step);
Console.WriteLine(txtSeries.Text.Trim() + result.ToString()); // Выводим на консоль
writeFile.WriteLine(txtSeries.Text.Trim() + result.ToString()); // Выводим в файл
}
В завершение, на примере последнего упражнения, добавим средства сохранения состояния, чтобы при новом запуске прежние настройки текстовых полей и текущий каталог остались. Существует несколько (рассмотрим три) стандартных способов решения этой задачи и все они основаны на использовании абстрактного класса ApplicationSettingsBase:
Два последних способа относятся к режиму разработки приложения (не к режиму выполнения). Их подробное описание можно найти в документации по ссылке: http://msdn.microsoft.com/ru-ru/library/a65txexh(VS.90).aspx
Способ 1.
Вначале рассмотрим программный способ (мы же программисты!).
Командой Project/Add Class добавьте новый файл с именем MySettings.cs, который заполните такusing System;
using System.Configuration;
namespace App7
{
class MySettings : ApplicationSettingsBase
{
// Параметр типа 'Приложение' менять нельзя во время работы приложения
// Параметр типа 'Пользователь' можно менять во время работы приложения
//[ApplicationScopedSetting()]// Область действия - 'Приложение'
[UserScopedSettingAttribute()] // Область действия - 'Пользователь'
[DefaultSettingValueAttribute("0")] // Значение параметра по умолчанию
public String Min
{
get
{
return ((String)this["Min"]);
}
set
{
this["Min"] = (String)value;
}
}
[UserScopedSettingAttribute()]
[DefaultSettingValueAttribute("250")]
public String Step
{
get
{
return ((String)this["Step"]);
}
set
{
this["Step"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("2000")]
public String Count
{
get
{
return ((String)this["Count"]);
}
set
{
this["Count"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("ResultNumber.txt")]
public String FileName
{
get
{
return ((String)this["FileName"]);
}
set
{
this["FileName"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("")]
public String DirTarget
{
get
{
return ((String)this["DirTarget"]);
}
set
{
this["DirTarget"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("")]
public String Series
{
get
{
return ((String)this["Series"]);
}
set
{
this["Series"] = (String)value;
}
}
}
}
Здесь мы определили общедоступные свойства с ассоциативными именами (чтобы лучше различать) и прикрепили к каждому из них аттрибуты, определяющие область видимости (действия) параметра и значение по умолчанию. Параметр типа UserScopedSetting (или UserScopedSettingAttribute - полное имя) пользователь может менять во время работы приложения, а типа ApplicationScopedSetting - нет (например, строки подключения).
Теперь нужно создать экземпляр этого класса и связать его свойства, которые как раз и будут считаться именами сохраняемых параметров, со свойствами элементов управления.
Модифицируйте в файле Form1.cs конструктор класса формы приложения следующим образом// Объявили и инициализировали поле для объекта параметров
MySettings settings = new MySettings();
public Form1()
{
InitializeComponent();
// Привязка именованных параметров к
// сохраняемым свойствам элементов управления
txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
}
Запустите/завершите приложение - пока значения текстовых полей не сохраняютсяПричина здесь в том, что привязанные параметры нужно явно записывать при завершении приложения. Обычно это делается в обработчике события FormClosing формы.
Выделите форму в режиме Design, перейдите в панель Properies/Events и двойным щелчком на поле события FormClosing создайте для него обработчик, который заполните такprivate void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
settings.Save();
}
Запустите/завершите приложение - значения текстовых полей сохраняются, кроме поля txtDirTargetНестабильность в сохранении значения текстового поля txtDirTarget объясняется тем, что мы его заполняем программно и элемент только показывает присвоенное значение, а не принимает его, пока не получит фокус ввода. Здесь можно ввести дополнительную переменную, в ней хранить значение текстового поля и ее же сохранять. Но чтобы много не переделывать, можно просто программно передавать фокус ввода текстовому полю каждый раз после получения им нового значения от диалога выбора каталога.
Дополните обработчик кнопки btnDirTarget строкой передачи фокуса текстовому полю txtDirTarget такprivate void btnDirTarget_Click(object sender, EventArgs e)
{
dialogFolder.ShowDialog();
txtDirTarget.Text = dialogFolder.SelectedPath;
txtDirTarget.Focus();// Без этого не сохраняет параметр в settings
}
Последний оператор устанавливает на текстовое поле txtDirTarget фокус ввода, без чего значение Text не сохраняется в установках приложения при его закрытии (если только явно не щелкнуть по нему курсором).
Запустите/завершите приложение - теперь состояние приложения сохраняется полностьюКод, помещенный нами ранее в начало функции Form1_Load(), выполняет проверку на случай первого запуска приложения или когда между запусками сохраненное значение каталога изменилось. Чтобы испытать его работу, можно добавить такой обработчик двойного щелчка формы
private void Form1_DoubleClick(object sender, EventArgs e)
{
settings.DirTarget = "";
txtDirTarget.Text = "";
dialogFolder.SelectedPath = "";
dialogFolder.RootFolder =
System.Environment.SpecialFolder.MyComputer;
}
Способ 2.
При создании нового проекта мастер оболочки попутно создает заготовки файлов Settings.settings и Settings.Designer.cs, в которые на этапе проектирования можно записать для компилятора исходные данные параметров, подлежащих сохранению. Эти файлы находятся в служебной папке Properties каталога проекта. Для удобства выполнения такой работы оболочка имеет вкладку (страницу) Settings панели конструктора проектов.
Чтобы продолжить рассмотрение Способа 2, необходимо вначале нейтрализовать весь код Способа 1, поскольку сохранять мы будем те же самые свойства текстовых полей.
В панели Solution Explorer выделите файл MySettings.cs и командой Exclude From Project контекстного меню (либо в разделе Project меню оболочки) исключите его из проекта
Закомментируйте (не удаляйте!) в конструкторе класса формы код привязки сохраняемых параметров к свойствам элементов// Объявили и инициализировали поле для объекта параметров
//MySettings settings = new MySettings();
public Form1()
{
InitializeComponent();
// Привязка именованных параметров к
// сохраняемым свойствам элементов управления
/*////////////////////////////////////////////////////
txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
////////////////////////////////////////////////////*/
}
Командой Project/App7 Properties откройте панель конструктора проектов на вкладке Settings и настройте ее как показано на снимке
Если щелкнуть на пиктограмме View Code вкладки Settings, то оболочка создаст в корневом каталоге проекта файл Settings.cs с еще одной частью класса Settings (первая часть находится в Properties\Settings.Designer.cs ) для более тонкого управления параметрами.
Сохраните документ командой File/Save All - оболочка создала конфигурационный файл приложения app.config и добавила в него наши настройкиТеперь осталось подключить эти настройки к коду приложения.
Модифицируйте в файле Form1.cs члены класса формы следующим образом// Объявили и инициализировали поле для объекта параметров
//MySettings settings = new MySettings();
Properties.Settings settings = new App7.Properties.Settings();
public Form1()
{
InitializeComponent();
// Привязка именованных параметров к
// сохраняемым свойствам элементов управления
/*////////////////////////////////////////////////////
txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
////////////////////////////////////////////////////*/
// Чтение параметров
//*////////////////////////////////////////////////////
txtMin.Text = settings.Min;
txtStep.Text = settings.Step;
txtCount.Text = settings.Count;
txtFileName.Text = settings.FileName;
txtDirTarget.Text = settings.DirTarget;
txtSeries.Text = settings.Series;
////////////////////////////////////////////////////*/
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Запись параметров
//*////////////////////////////////////////////////////
settings.Min = txtMin.Text;
settings.Step = txtStep.Text;
settings.Count = txtCount.Text;
settings.FileName = txtFileName.Text;
settings.DirTarget = txtDirTarget.Text;
settings.Series = txtSeries.Text;
////////////////////////////////////////////////////*/
settings.Save();
}
Запустите проект в режиме Release, измените настройки и закройте приложение - параметры сохраняются
В панели Solution Explorer щелкните на пиктограмме Show All Files - к исполнимой сборке добавился файл конфигурации App7.exe.configТеперь без этого файла приложение работать не будет и при развертывании приложения на других компьютерах файл придется включать в установочный пакет (это не страшно, только бы не забыть!). Файл App7.exe.config имеет такое же содержимое (откройте его!), что и app.config, но носит имя приложения.
Действием, приведенным в последнем листинге, нам пришлось добавить код чтения и записи параметров, потому что перед этим мы закомментировали (но не удалили!) код их привязки к свойствам интерфейсных элементов. Если теперь закомментировать свежие блоки кода, а раскомментировать код привязки, то функциональность приложения не изменится. Проверьте это, просто убрав или добавив слэш перед открывающей комментарной скобкой.
Способ 3.
Этот способ выполняет создание и привязку параметров через панель свойств и подробно описан в документации http://msdn.microsoft.com/ru-ru/library/wabtadw6%28v=VS.90%29.aspx
Как мы сейчас увидим, в конечном итоге это то же самое, что и предыдущий способ сохранения параметров на этапе разработки. Только он больше подходит для сохранения свойств именно компонентов, а не прочих объектов приложения. Рассмотрим его применительно к нашей задаче.
Командой Project/App7 Properties откройте панель конструктора проектов на вкладке Settings и удалите прежние параметры (по одному или сразу все)
Через Solution Explorer удалите файл app.config из корневого узла текущего проекта
В режиме Design выделите элемент txtMin, в панели Properties раскройте узел ApplicationSettings и щелкните на кнопке с многоточием в поле свойства PropertyBinding
Выделите привязываемое свойство Text элемента txtMin и в раскрывающемся списке выполните ссылку New для вызова окна создания нового параметра
Заполните окно как показано на снимке
Аналогичным образом создайте и настройте параметры и для остальных элементов со значениями из предыдущих способовОбратите внимание, что начальные значения параметров (если они есть) подставляются оболочкой из свойств компонентов, которые мы определяли при их создании. В итоге мы получим точно такой же результат, что и в предыдущем способе. Поэтому код в классе Form1, связанный с настройками, менять не будем - он у нас уже есть.
Запустите проект - параметры сохраняются, но опять при развертывании (распространении программы) нужно таскать и файл app.config (или точно такой же App7.exe.config )Итак, в данном упражнении мы спроектировали инструмент, который может оказаться удобным при формировании некоторых номеров случайным образом (чтобы враги уж совсем не угадали, можно еще и зашифровать). При этом применили знания по теме данной работы. Из расссмотренных способов мне больше нравится первый. Чтобы к нему вернуться, нужно вновь добавить в проект файл MySettings.cs и удалить из него все, связанное с двумя последними способами.
Следует заметить, что содержимое файла Settings.Designer.cs в папке Properties проекта, полученное после настроек с использованием любого из последних двух способов, почти полностью идентично содержимому файла MySettings.cs, который мы формировали вручную в первом способе. Отсюда мораль: если не хотим таскать за собой файл app.config, то вначале быстренько создаем параметры через оболочку, затем перетаскиваем созданный ею код в свой класс. После этого опять удаляем все параметры (как перед выполнением способа 3) и файл конфигурации, а используем уже наш класс с параметрами программным образом.
(
Что называется, - 'немного подъехать' на возможностях оболочки!)
Все необходимые для выполнения данной работы программы можно найти в Setup.zip.
Для работы с файлами библиотека .NET Framework имеет достаточное количество классов. Познакомимся с ними. Простейшим уровнем файловых операций является получение информации о файлах и каталогах, смена каталогов, получение сведений о размерности файлов, времени создания, режиме доступа, удаления, переименования, копирования и т.д. без их открытия. Для этого имеются следующие библиотечные классы пространства имен System.IO ( input, output ) сборки mscorlib.dll:
Если взглянуть на классы Directory и File через панель Object Browse r, то можно убедиться, что эти классы имеют только статические методы, приведенные ниже. Предполагается, что если есть методы чтения Get, то есть и дуальные методы Set, которые в таблицах не указаны.
| Метод | Описание |
|---|---|
CreateDirectory() |
Создает каталог вместе с указанным путем |
Delete() |
Удаляет пустой каталог, если второй необязательный параметр не указан или равен false, то каталог с содержимым метод удалять отказывается. Если второй параметр указан и равен true, то удаляет каталог со всем содержимым |
Exists() |
Возвращает флаг существования каталога |
GetCreationTime(), GetLastAccessTime(), GetLastWriteTime() |
Возвращают структуру DateTime, которая содержит время создания каталога, время последнего чтения или записи |
GetDirectories(), GetFiles(), GetLogicalDrives() |
Возвращает массив строк, по одной на каждый подкаталог, файл или устройство в указанном каталоге (в зависимости от используемого метода). Методы имеют перегрузку, задающую шаблон (фильтр) поиска (например, My*.txt, My???.*, *My.exe, *my*). Регистр в шаблонах поиска значения не имеет |
GetParent() |
Анализирует переданную строку и возвращает имя родительского каталога |
GetCurrentDirectory() |
Возвращает текущий каталог с полным путем |
Move() |
Принимает два строки - исходный каталог и целевой каталог с полными путями. Перемещает исходный каталог в указанное место в пределах одного дискового устройства (иначе, - копирует) |
GetAccessControl() |
Возвращает объект со списком контроля доступа Windows |
| Метод | Описание |
|---|---|
Copy() |
Копирует один файл в другой с возможной заменой содержимого существующего файла (если задан третий необязательный параметр true ) |
Delete() |
Удаляет указанный файл, но не возбуждает исключение, если файл не найден |
Exists() |
Проверяет существование файла и возвращает флаг |
GetAttributes() |
Возвращает атрибуты указанного файла |
GetCreationTime(), GetLastAccessTime(), GetLastWriteTime() |
Возвращают структуру DateTime, которая содержит время создания файла, время последнего чтения или записи |
Move() |
Перемещает или переименовывает файлы |
Create(), CreateText() |
Создают файл |
Open(), OpenText(), OpenRead(), OpenWrite() |
Открывают файл, открывают только для чтения, открывают только для записи |
ReadAllText(), ReadAllLines(), ReadAllBytes() |
Читают весь файл и возвращают его содержимое в виде строки, массива строк или массива байт |
WriteAllText(), WriteAllLines(), WriteAllBytes() |
Пишут весь файл за один прием, использую принятую строку, массив строк или массив байт. Если файл существует, то он перезаписывается |
GetAccessControl() |
Возвращает объект со списком контроля доступа Windows |
Создадим приложение, которое извлекает список файлов текущего каталога сборки. Вывод имен файлов организуем на консоль и в списочный элемент Windows Forms одновременно.
Создайте новые решение и проект командой File/New/Project в соответствии со снимком
Командой Project/App1 Properties преобразуйте проект в консольное приложение настройкой списка Output type
Откройте файл Program.cs и добавьте в него код, выделенный в следующем листингеusing System;
using System.Collections.Generic;
using System.Windows.Forms;
// Добавляем пространство имен
using System.IO;
namespace App1
{
class Program // Убрали слово static!!!
{
[STAThread]
static void Main()
{
// Вызов нашей функции
(new Program()).ConsoleOutput();// Через экземпляр - для куража!
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
// Вывод информации в консольное окно
void ConsoleOutput()
{
// Настраиваем консоль
Console.Clear();
Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = false;
Console.WindowWidth = 70;// 80 знакомест - по умолчанию
Console.WindowHeight = 20;
Console.Title = "Список файлов каталога сборки";
string dir = Directory.GetCurrentDirectory(); // Каталог сборки
string[] fileList = Directory.GetFiles(dir); // Список файлов
int i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
Console.WriteLine("{0}) {1}", ++i, path);
}
Console.WriteLine();
i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
string file = path.Substring(path.LastIndexOf('\\') + 1);// Экранируем символ
Console.WriteLine("{0}) {1}", ++i, file);
}
Console.WriteLine();
i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
string file = path.Substring(path.LastIndexOf(@"\") + 1);// Неинтерпретируемая строка
Console.WriteLine("{0}) {1}", ++i, file);
}
Console.WriteLine();
i = 0; // Счетчик
foreach (string path in fileList) // Выводим список
{
string file = Path.GetFileName(path); // Сразу чистое имя файла
Console.WriteLine("{0}) {1}", ++i, file);
}
}
}
}
Обратите внимание, что мы убрали перед объявлением класса ключевое слово static (для разнообразия). Поэтому нам пришлось создавать экземпляр класса, чтобы вызвать
Запустите приложение - консольное окно наполняется вариантами вывода имен файловВначале мы распечатали полные имена файлов, которые вернул метод Directory. GetFiles(). Такие имена иногда называют квалифицированными ( the fully qualified file name ), поскольку в файловой системе компьютера они однозначно определяют файлы как единицы информации. Для получения укороченного имени ( the relative file name ) мы использовали варианты, которых для решения одной и той же задачи обычно можно придумать несколько - обычно именно с таким запасом устроена библиотека .NET Framework.
Вывод в консольное окно мы организовали до того, как приложение войдет в цикл сообщений Windows, иначе консольное окно вначале появилось бы пустым и заполнилось бы только после закрытия диалогового окна. Но тогда все равно пришлось бы ставить задержку консольного окна, например, методом Console.ReadLine(), чтобы увидеть результаты до закрытия приложения.
Теперь настроим окно Windows Forms.
Перейдите в режим Design файла Form1.c s и поместите из панели Toolbox на макет формы элемент ListBox, который настройте через панель Properties в соответствии с приведенной таблицей свойств| Компонент | Свойство | Значение |
|---|---|---|
ListBox |
(Name) |
listBox1 |
Dock |
Fill |
Вызовите правой кнопкой мыши для макета формы контекстное меню и выполните команду View Code, чтобы перейти в кодовую часть формы. Заполните конструктор формы такusing System;
using System.Windows.Forms;
// Дополнительное подключение пространств имен
using System.IO;
namespace App1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Привязка массива к списку для отображения и печать результатов
String[] fileList = Directory.GetFiles(Directory.GetCurrentDirectory());
for (int i = 0; i < fileList.Length; i++)
fileList[i] = String.Format("{0}) {1}", i + 1, Path.GetFileName(fileList[i]));
listBox1.DataSource = fileList;
listBox1.SelectionMode = SelectionMode.None;
}
}
}
Запустите проект - результат должен быть таким (два окна на одном снимке)
Это приложение можно было бы довести до ума, дополнив диалоговым окном смены каталога, но мы пока рассматриваем инструменты пространства имен System.IO и отложем эту мысль на потом.
Эти классы прописаны в библиотеке с ключевым словом sealed (
Классы DirectoryInfo и FileInfo имеют общего родителя - абстрактный класс FileSystemInfo, который наделяет их базовым набором методов и свойств. Некоторые из этих членов приведены в таблице.
| Член | Описание |
|---|---|
Attributes |
Свойство позволяет извлекать и устанавливать атрибуты для файла или каталога, используя комбинацию значений перечисления FileAttributes:Archive - элемент помечен как архивныйCompressed - элемент сжат. Разрешено читать, но устанавливается только через механизмы ОСDevice - атрибут зарезервирован и пока не используетсяDirectory - элемент является каталогомEncrypted - элемент зашифрован . Для файла это значит, что все данные в нем зашифрованы. Для каталога это означает, что режим шифрования включен по умолчанию для вновь. создаваемых файлов и подкаталогов. Разрешено читать, но устанавливается только через механизмы ОСHidden - элемент скрытNormal - элемент без атрибутов. Этот атрибут устанавливается по умолчанию, когда сняты все другие атрибутыNotContentIndexed - элемент не будет индексирован службой индексации содержимого операционной системыOffline - файл находится в автономном состоянии и в данный момент недоступенReadOnly - элемент доступен только для чтенияSystem - элемент, используемый исключительно операционной системойTemporary - элемент помечен как временный и может быть удален после завершения работы приложенияReparsePoint, SparseFile - только для файловой системы NTFS |
CreationTime,
LastAccessTime,
LastWriteTime |
Свойство типа DateTime, определяющее временные характеристики файловой единицы (время создания, время последнего доступа и время последнего обновления) |
CreationTimeUtc,
LastAccessTimeUtc,
LastWriteTimeUtc |
То же, что предыдущее, только по Гринвичу |
Exists |
true, если для созданного объекта-наследника данных файл или каталог существует |
FullName |
Свойство get типа String, содержащее полное квалифицированное имя файла или каталога |
Name |
|
Extension |
Свойство get типа String, содержит только расширение файла |
Delete() |
Абстрактная заготовка для удаления файла или каталога в производных классах |
Refresh() |
Синхронизирует созданный ранее объект данных каталога или файла с последними изменениями файловой системы, выполненными через другие инструменты |
Экземплярные классы DirectoryInfo и FileInfo не только реализуют унаследованные члены, но и дополняют (расширяют) базовый класс FileSystemInfo своими уникальными членами. Некоторые из них приведены в таблицах ниже.
| Член | Описание |
|---|---|
Create() |
Создает каталог, указанный ранее в конструкторе при создании экземпляра DirectoryInfo |
CreateSubdirectory() |
Создает подкаталог |
Delete() |
Удаляет пустой каталог |
Delete(bool recursive) |
Удаляет каталог вместе с деревом подкаталогов, если аргумент равен true |
GetDirectories() |
Возвращает список объектов данных подкаталогов типа DirectoryInfo для текущего каталога. Имеет перегрузки, которые допускают использование шаблонов поиска, удовлетворяющих требуемому условию |
GetFiles() |
Возвращает список объектов данных типа FileInfo для файлов текущего каталога. Метод имеет перегрузки, позволяющие использовать стандартные символы шаблонов поиска '*' и '?'. Шаблон '?' представляет любой единственный символ, а шаблон '*' - любую последовательность символов из нуля и более, например, *.txt ищет все текстовые файлы |
Parent |
Свойство возвращает объект данных DirectoryInfo родительского каталога для указанного подкаталога |
Root |
Свойство возвращает объект данных DirectoryInfo, представляющий корневой каталог |
| Член | Описание |
|---|---|
Directory |
Возвращает экземпляр класса DirectoryInfo, представляющий каталог размещения файла |
DirectoryName |
Возвращает строку с полным именем родительского каталога |
Exists |
Возвращает флаг, определяющий, существует ли указанный файл |
IsReadOnly |
Булево свойство определяет или устанавливает атрибут файла "только для чтения" |
Length |
Длинное целое, возвращающее размер файла в байтах |
Name |
Переопределенное свойство содержит имя файла |
CopyTo() |
Копирует связанный с объектом данных файл в новое место с указанным именем и возвращает новый объект данных FileInfo, представляющий новую копию. Перегрузка с флагом разрешает перезапись, если целевой файл уже существует |
Create() |
Создает файл и возвращает объект-оболочку потока FileStream, который можно использовать для записи |
CreateText() |
Создает файл и возвращает объект-оболочку потока StreamWriter, который можно использовать для записи |
AppendText() |
Создает и возвращает поток StreamWriter для добавления текста в файл, связанный с объектом FileInfo |
Open(System.IO.FileMode) |
Возвращает объект FileStream и открывает файл в заданном режиме FileMode:Append - добавлять в конец файлаCreate - если файл несуществует, то создать, иначе очистить существующий - создает и открывает новый файл. Если файл уже существует, выдает исключениеOpen - требует открыть файл в режиме, установленном перечислением FileAccess. Если файл не существует, выдается исключение FileNotFoundExceptionOpenOrCreate - существующий файл открыть, иначе создать файлTruncate - открыть файл с очисткой до нулевой длины |
Open(System.IO.FileMode, System.IO.FileAccess) |
Возвращает объект FileStream. Представляет вариации открытия файла с вариациями доступа, определяемыми перечислением FileAccess:Read - только для чтенияReadWrite - для чтения и записиWrite - только для записи |
Open(System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare) |
Возвращает объект FileStream. Представляет вариации открытия файла с вариациями доступа, включая возможность одновременного доступа других объектов-потоков FileStream к данному файлу. Права других потоков при совместном доступе определяются перечислением FileShare:Delete - разрешено удаление файлаNone - не разрешать совместное использование файлаRead - разрешить сторонним потокам только чтениеReadWrite - разрешить совместные чтение и записьWrite - разрешить совместную запись |
OpenRead() |
Создает поток FileStream только для чтения и возвращает его |
OpenWrite() |
Создает поток FileStream только для записи и возвращает его |
OpenText() |
Создает и возвращает поток StreamReader для чтения в кодировке UTF8 из существующего текстового файла |
Encrypt() |
Шифрует файл |
|
Дешифрует файл |
Delete() |
Удаляет файл физически |
Replace() |
Заменяет содержимое указанного файла содержимым файла, связанного с текущим FileInfo. Целевой файл удаляется, а вместо него создается резервная копия с указанным именем, но параметры целевого файла возвращаются методом в виде объекта FileInfo |
Экземплярные классы DirectoryInfo и FileInfo имеют только параметризованные конструкторы, ожидающие строку каталога или файла с полным путем. Путь не обязательно должен соответствовать реально существующему файлу или каталогу. Если указанный путь не существует, то свойство Exists объекта будет иметь значение false.
Свойства объектов извлекают информацию из файловой системы только один раз при первом обращении. При следующих обращениях они не обновляют эту информацию. Это может привести к расхождению с действительностью, если файл в данный момент будет модифицирован сторонней программой, а не этим объектом. Для обновления информации в экземпляре нужно выполнить метод Refresh().
Если указанный файл или каталог не существует, то их можно создать. Ниже приведен пример такой функции.
static void CreateFile(string fileName) // Нужно полное имя файла
{
string file = Path.GetFileName(fileName); // Только имя файла
string dir = Path.GetDirectoryName(fileName); // Только полный путь
string path = Path.Combine(dir, file); // Опять fileName
/*
Console.WriteLine("fileName = {0}", fileName);
Console.WriteLine("file = {0}", file);
Console.WriteLine("dir = {0}", dir);
Console.WriteLine("path = {0}", path);
*/
DirectoryInfo myDir = new DirectoryInfo(dir);
FileInfo myFile = new FileInfo(fileName);
if (!myDir.Exists) // Если каталог не существует
{
myDir.Create(); // Создать каталог
myFile.Create(); // Создать файл
}
else if (!myFile.Exists)// Если файл не существует
myFile.Create(); // Создать файл
}
Вызов функции CreateFile() может быть таким
Program.CreateFile(@"C:\Tmp111\TTT\fileName.txt");
Методы GetFiles() и GetDirectories() классов Directory или DirectoryInfo с заданными шаблонами поиска или без них осуществляют поиск информации только для текущего каталога. Чтобы пройтись по всем вложенным подкаталогам, нужно применить рекурсивную логику поиска. В следующем примере используется рекурсивный вызов функции для подсчета размера файлов, находящихся в указанном каталоге и во всех подчиненных ему подкаталогах файлового дерева.
static long GetDirectorySize(DirectoryInfo directory, bool includeSubdirectories)
{
Console.WriteLine(directory.FullName);
long totalSize = 0;
// Просуммировать размеры файлов в текущем каталоге
FileInfo[] files = directory.GetFiles();
foreach (FileInfo file in files)
totalSize += file.Length;
// Рекурсивно обходим подкаталоги
if (includeSubdirectories)
{
DirectoryInfo[] dirs = directory.GetDirectories();
foreach (DirectoryInfo dir in dirs)
totalSize += GetDirectorySize(dir, true);
}
return totalSize;
}
Вызов функции GetDirectorySize() может быть таким
String path = @"E:\Tmp";
long totalSize = Program.GetDirectorySize(new DirectoryInfo(path), true);
Console.WriteLine(totalSize);
Свойство Attributes, наследуемое экземплярными классами DirectoryInfo и FileInfo от класса FileSystemInfo, устанавливает особенность поведения операционной системы по отношению к элементам файловой системы. Оно обеспечивает доступ к двухбайтному базовому слову, каждый бит которого соответствует определенной поведенческой установке, закрепленной за файлом или каталогом.
Метод ToString() свойства Attributes возвращает строку с именами атрибутов элемента, разделенными запятыми. Следующий код распечатывает названия атрибутов всех файлов заданного каталога
static void Main(string[] args)
{
String currentDirectory = @"E:\Tmp\XX";//Directory.GetCurrentDirectory(); // Каталог сборки
//Console.WriteLine(currentDirectory);
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files = directory.GetFiles(); // Список файлов
foreach (FileInfo file in files)
{
Console.WriteLine("\nАтрибуты файла {0}", file.Name);
String[] attributes = file.Attributes.ToString().Split(',');
for (int i = 0; i < attributes.Length; i++)
Console.WriteLine(attributes[i].Trim());
}
Console.ReadLine();
}
Для работы с атрибутами используются побитовые операции. Элементы перечисления FileAttributes представляют собой слово (2 байта), в котором только один бит равен единице. Их можно считать побитовыми масками, с помощью которых можно установить или прочитать атрибут. Некоторые атрибуты не могут быть установлены программно. Например, атрибуты FileAttributes. Encrypted и FileAttributes.Compressed устанавливаются самой операционной системой через специальные механизмы.
Прежде, чем двигаться дальше, вспомним поразрадные операции C#. Они позволяют работать с данными на уровне бит. Все поразрядные операции используются только с целыми типами данных ( byte, sbyte, int, uint, short, ushort, long, ulong, char, decimal ), у которых в машинном представлении нет плавающей точки.
Существует 6 поразрядных операций, проиллюстрированных в следующих таблицах
| Операция | Наименование | Описание |
|---|---|---|
| поразрядное И | Бит результата устанавливается в 1, если соответствующие биты обеих операндов равны 1 | |
| | | поразрядное ИЛИ | Бит результата устанавливается в 1, если соответствующий бит хотя бы одного операнда равен 1 |
| ^ | поразрядное ИСКЛЮЧАЮЩЕЕ ИЛИ | Бит результата устанавливается в 1, если соответствующиий бит одного и только одного операнда равен 1 |
| ~ | поразрядное НЕ | Унарная операция, которая применяется только к одному операнду. Меняет каждый бит с 1 на 0 и наоборот |
| << | поразрядный сдвиг влево | Сдвигает биты первого операнда влево на количество позиций, заданных вторым операндом. При этом освобождающиеся справа биты заполняются нулями |
| >> | поразрядный сдвиг вправо | Сдвигает биты первого операнда вправо на количество позиций, заданных вторым операндом. При этом освобождающиеся слева биты заполняются нулями |
| binary | 0x | |
|---|---|---|
| 0000 | 0000 | 0x00 |
| 0000 | 0001 | 0x01 |
| 0000 | 0010 | 0x02 |
| 0000 | 0011 | 0x03 |
| 0000 | 0100 | 0x04 |
| 0000 | 0101 | 0x05 |
| 0000 | 0110 | 0x06 |
| 0000 | 0111 | 0x07 |
| 0000 | 1000 | 0x08 |
| 0000 | 1001 | 0x09 |
| 0000 | 1010 | 0x0A |
| 0000 | 1011 | 0x0B |
| 0000 | 1100 | 0x0C |
| 0000 | 1101 | 0x0D |
| 0000 | 1110 | 0x0E |
| 0000 | 1111 | 0x0F |
| a mask | a | mask | a ^ mask | ~mask | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| a | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | ||||||||
| mask | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
| result | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
| result ^ mask | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | ||||||||||||||||||||||||
| a << 5 | a >> 5 | |||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| a | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 |
| result | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
| = | Операция присваивания поразрядного И |
|---|---|
| | = | Операция присваивания поразрядного ИЛИ |
| ^ = | Операция присваивания поразрядного исключающего ИЛИ |
| << = | Операция присваивания сдвига влево |
| >> = | Операция присваивания сдвига вправо |
Следующее упражнение иллюстрирует побитовую арифметику с атрибутами.
Добавьте к решению FilesAndDirectory проект консольного приложения App2 и назначьте его стартовым в Solution Explorer командой контекстного меню Set as StartUp Project
Заполните файл Program.cs следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
String currentDirectory = @"E:\Tmp\XX";//Directory.GetCurrentDirectory(); // Каталог сборки
//Console.WriteLine(currentDirectory);
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files1 = directory.GetFiles("*.txt"); // Список текстовых файлов
// Создаем маску с атрибутами
FileAttributes mask = FileAttributes.Archive
| FileAttributes.ReadOnly
| FileAttributes.System
| FileAttributes.Offline
| FileAttributes.NotContentIndexed
| FileAttributes.Hidden
| FileAttributes.Temporary
| FileAttributes.Device;
// Устанавливаем для всех файлов указанные атрибуты
foreach (FileInfo file in files1)
{
// Добавляем атрибуты
file.Attributes |= mask;
}
// Создаем новый объект для чистоты эксперимента и печатаем атрибуты
FileInfo[] files2 = directory.GetFiles("*.txt"); // Список текстовых файлов
foreach (FileInfo file in files2)
{
Console.WriteLine("\nАтрибуты файла {0}", file.Name);
String[] attributes = file.Attributes.ToString().Split(',');
for (int i = 0; i < attributes.Length; i++)
Console.WriteLine(attributes[i].Trim());
}
// Определяем, имеет ли первый файл коллекции files2 атрибут ReadOnly
if ((files2[0].Attributes FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
Console.WriteLine(Environment.NewLine // Новая строка
+ "Файл {0} имеет атрибут ReadOnly", files2[0].Name);
else
Console.WriteLine("\n" // Новая строка
+ "Файл {0} не имеет атрибут ReadOnly", files2[0].Name);
// Исключаем из первого объекта добавленные ранее атрибуты
foreach (FileInfo file in files1)
{
// Исключаем атрибуты
file.Attributes = ~mask; // Побитовое умножение на инвертированную маску
}
// Печатаем результат через второй объект
// Если открыть комментарные скобки - результат будет неверным!!!
//*
for (int i = 0; i < files2.Length; i++)
files2[i].Refresh(); // Приводим в соответствие с состоянием файлов
//*/
foreach (FileInfo file in files2)
{
Console.WriteLine("\nАтрибуты файла {0}", file.Name);
String[] attributes = file.Attributes.ToString().Split(',');
for (int i = 0; i < attributes.Length; i++)
Console.WriteLine(attributes[i].Trim());
}
// Определяем, имеет ли первый файл коллекции files2 атрибут ReadOnly
if ((files2[0].Attributes FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
Console.WriteLine(Environment.NewLine // Новая строка
+ "Файл {0} имеет атрибут ReadOnly", files2[0].Name);
else
Console.WriteLine("\n" // Новая строка
+ "Файл {0} не имеет атрибут ReadOnly", files2[0].Name);
Console.ReadLine();
}
}
}
Запустите приложение - вывод программы для нескольких файлов будет примерно следующимАтрибуты файла Test.txt ReadOnly Hidden System Archive Temporary Offline NotContentIndexed Атрибуты файла xx.txt ReadOnly Hidden System Archive Temporary Offline NotContentIndexed Файл Test.txt имеет атрибут ReadOnly Атрибуты файла Test.txt Normal Атрибуты файла xx.txt Normal Файл Test.txt не имеет атрибут ReadOnly
Обратите внимание, что как только мы сняли с файлов все значимые атрибуты, то для них по умолчанию установился атрибут Normal. Следовательно, для проверки того, что файл не имеет атрибутов, достаточно проверить наличие атрибута Normal.
Экземплярный класс, который извлекает информацию о дисковом устройстве компьютера. Имя устройства задается в конструкторе класса при создании экземпляра и представляет собой строку с символом от " a " до " z " в любом регистре. Ниже приведены члены класса DriveInfo.
| Член | Описание |
|---|---|
TotalFreeSpace |
Возвращает объем свободного пространства в байтах |
AvailableFreeSpace |
Возвращает объем свободного пространства в байтах, доступного для данного процесса |
DriveFormat |
Свойство возвращает имя файловой системы ( NTFS или FAT32 ) |
DriveType |
Возвращает значение одноименного перечисления DriveType:CDRom - устройство оптического диска, такое как CD или DVD-ROMFixed - фиксированный дискNetwork - сетевойNoRootDirectory - у диска нет корневого каталогаRam - устройство RAMRemovable - сменное устройство хранения данных, такое как флэшкаUnknown - тип диска неопределен |
IsReady |
Определяет готовность устройства к чтению или записи. Например, если нет компакт-диска в CD -приводе, свойство IsReady вернет false. Фиксированные устройства всегда готовы |
Name |
Возвращает строку с именем диска, например, C: или E: |
RootDirectory |
Возвращает объект DirectoryInfo с информацией о корневом каталоге диска |
TotalSize |
Получает полный размер пространства памяти на диске |
VolumeLabel |
Свойство, извлекающее или устанавливающее метку тома |
GetDrives() |
Статический метод, который возвращает массив объектов DriveInfo, представляющих все логические устройства данного компьютера |
Перечисленные свойства возвращают информацию только для устройства, которое находится в состоянии готовности, иначе обращение к ним генерирует исключение. Поэтому, прежде всего нужно проверять флаг IsReady и если он равен true, только тогда можно приступать к использованию других свойств.
Проиллюстрируем использование класса DriveInfo.
Добавьте к решению консольное приложение App3 и назначьте его стартовым
Заполните файл Program.cs следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DEBUG = System.Diagnostics.Debug;// Псевдоним класса
namespace App3
{
class Program
{
static void Main(string[] args)
{
// Получаем список всех логических устройств компьютера
DriveInfo[] drives = DriveInfo.GetDrives();
// Перебираем список и печатаем свойства
bool rootFlag = false; // Чтобы напечатать только раз
foreach (DriveInfo drive in drives)
{
String message = String.Format("Устройство '{0}'", drive.Name);
// Если готово, извлекаем характеристики
if (drive.IsReady)
{
message += " ГОТОВО" + Environment.NewLine;
if (!rootFlag)
{
message += String.Format("RootDirectory: {0}\n", drive.RootDirectory);
rootFlag = !rootFlag;// Отключаем
}
message += String.Format("VolumeLabel: {0}\n", drive.VolumeLabel);
message += String.Format("DriveType: {0}\n", drive.DriveType.ToString());
message += String.Format("DriveFormat: {0}\n", drive.DriveFormat);
message += String.Format("TotalSize: {0}\n", drive.TotalSize);
message += String.Format("AvailableFreeSpace: {0}\n", drive.AvailableFreeSpace);
message += String.Format("TotalFreeSpace: {0}\n", drive.TotalFreeSpace);
}
else
message += " НЕГОТОВО\n";
DEBUG.WriteLine(message);
}
Console.ReadLine();
}
}
}
Обратите внимание, что псевдонимы можно определять не только для пространств имен, но и для классов. Свойство RootDirectory экземпляра DriveInfo для любого устройства возвращает одно и то же значение, поэтому мы используем его только раз (чтобы не мешало). Если мы хотим исследовать только одно устройство и знаем его имя, то можно создать объект DriveInfo с помощью параметризованного конструктора, указав в качестве аргумента строку с именем, например, так
DriveInfo drive = new DriveInfo("C");
Имя устройства задается только символом от a до z в любом регистре и без дополнительных символов слэша и двоеточия. А далее все равно нужно проверить его готовность IsReady, прежде чем обращаться к другим свойствам.
Удостовертесь, что включен режим Debug оболочки (выпадающий список над рабочей областью)
Запустите проект и откройте панель Output. На моем компьютере получился такой результатУстройство 'C:\' ГОТОВО RootDirectory: C:\ VolumeLabel: 18125 DriveType: Fixed DriveFormat: NTFS TotalSize: 31457275904 AvailableFreeSpace: 473616384 TotalFreeSpace: 473616384 Устройство 'D:\' ГОТОВО VolumeLabel: DriveType: Removable DriveFormat: FAT32 TotalSize: 261105664 AvailableFreeSpace: 244381696 TotalFreeSpace: 244381696 Устройство 'E:\' ГОТОВО VolumeLabel: Data DriveType: Fixed DriveFormat: NTFS TotalSize: 462356992000 AvailableFreeSpace: 425508450304 TotalFreeSpace: 425508450304 Устройство 'F:\' ГОТОВО VolumeLabel: ГАИ - 24.01.2010 DriveType: CDRom DriveFormat: CDFS TotalSize: 161966080 AvailableFreeSpace: 0 TotalFreeSpace: 0 Устройство 'G:\' НЕГОТОВО Устройство 'H:\' НЕГОТОВО Устройство 'I:\' НЕГОТОВО Устройство 'J:\' НЕГОТОВО Устройство 'K:\' ГОТОВО VolumeLabel: DriveType: Removable DriveFormat: FAT32 TotalSize: 31994216448 AvailableFreeSpace: 7246184448 TotalFreeSpace: 7246184448
Вывод информации мы организовали в панель Output оболочки, поэтому консольное окно будет пустым.
Включите режим Release оболочки и повторите запуск проектаМы видим, что отладочный код работает только в режиме Debug и автоматически исключается компилятором в режиме Release из готовой исполнимой сборки приложения. Это очень удобно при тестировании проектов.
Иногда может возникнуть необходимость получить дополнительную информацию о файле, включающую в себя авторские права, номер версии, наименование компании-производителя, информацию о торговой марке и т.д. Эта информация вшивается в сборку при компиляции EXE - или DLL -файлов (если укажем). Данные отображаются в диалоговом окне свойств, вызванном в Windows Explorer при выделении исполнимого файла правой кнопкой мыши и выбора опции Свойства, например

Информация о сборке на этапе проектирования размещается в файле AssemblyInfo.cs, который автоматически создается оболочкой в новом проекте в каталоге Properties. Метаданные можно редактировать через этот файл. Такая необходимость чаще всего возникает в том случае, если программа (сборка или библиотека) делается для продажи или передачи другим людям.

Добавьте к решению командой File/Add/New Project новый проект консольного приложения с именем App4 и назначьте его стартовым
В панели Solution Explorer откройте на редактирование файл AssemblyInfo.cs и ознакомьтесь с его содержимым, автоматически сгенерированным оболочкойusing System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("App4")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("App4")]
[assembly: AssemblyCopyright("Copyright © 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("749a9e12-8b91-41d5-942a-e174fe60800f")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Мы видим набор атрибутов в глобальном пространстве имен, определяющих для сборки дополнительную информацию, которую называют метаданными (данные о данных). Ее можно редактировать в файле AssemblyInfo.cs напрямую, но лучше это делать через страницу свойств проекта.
Выполните команду Project/App4 Properties меню оболочки и выделите вкладку Application страницы свойств проекта
Щелкните на кнопке Assembly Information, чтобы вызвать диалоговое окно редактирования метаданных сборки с текстовыми полями:
Заполните диалоговое окно свойств как показано на рисунке
Вновь откройте файл AssemblyInfo.cs и убедитесь, что его содержимое изменилосьИзвлечь информацию из сборки можно программно. Для этого предназначен класс System.Diagnostics. FileVersionInfo. Продемонстрируем его использование на примере.
Заполните файл Program.cs приложения App4 следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DEBUG = System.Diagnostics.Debug; // Псевдоним класса
using SD = System.Diagnostics; // Создаем alias пространства имен (необязательно!)
using System.Diagnostics; // Подключаем пространство имен напрямую
namespace App4
{
class Program
{
static void Main(string[] args)
{
// Локальные (стековые) переменные инициализировать обязательно
String currentDirectory = Directory.GetCurrentDirectory(); // Каталог сборки
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files = directory.GetFiles("*.exe"); // Список файлов по шаблону
// Создаем массив для информации обо всех файлах
SD.FileVersionInfo[] info =
new System.Diagnostics.FileVersionInfo[files.Length];
// Извлекаем из всех файлов дополнительную информацию
for (int i = 0; i < files.Length; i++)
{
info[i] = FileVersionInfo.GetVersionInfo(files[i].FullName);// Просто 'Name' мало!
}
// Накапливаем результат для печати
String message = String.Empty;
for (int i = 0; i < info.Length; i++)
{
// Используем отражение для сокращения кода (и куража!)
System.Reflection.PropertyInfo[] props = // Общедоступные свойства
info[i].GetType().GetProperties();
for (int j = 0; j < props.Length; j++)
{
message += props[j].Name + ":\t"; // Имя свойства, ':' и отступ
// Выполняем свойство, чтобы извлечь его значение
Object prop = props[j].GetGetMethod().Invoke(info[i], null);
if (prop is String) // Динамическая идентификация типа (необязательно)
message += prop.ToString();
else if (prop != null)
message += String.Format("{0}", prop);
message += Environment.NewLine; // Новая строка
}
message += "\r\n"; // Новая строка
}
Console.Write(message + "\n"); // Печатаем на консоль (Write и новая строка)
DEBUG.WriteLine(message); // Печатаем в панель Output оболочки
Console.ReadLine(); // Задержка консоли
}
}
}
В приведенном коде мы применили методику отражения типов ( Reflection - рефлексию), чтобы не перебирать все свойства явно. Кроме того, использовали разные способы новой строки, для разнообразия.
Запустите приложение - вывод информации для файлов каталога исполнимой сборки данного проекта будет такимComments: Читает метаданные (Description) CompanyName: Рога и копыта (Company) FileBuildPart: 0 FileDescription: Приложение App4 (Title) FileMajorPart: 1 FileMinorPart: 0 FileName: E:\Tmp\FilesAndDirectory\App4\bin\Debug\App4.exe FilePrivatePart: 5678 FileVersion: 1.0.0.5678 InternalName: App4.exe IsDebug: False IsPatched: False IsPrivateBuild: False IsPreRelease: False IsSpecialBuild: False Language: Независимо от языка LegalCopyright: Copyright © 2010 (Copyright) LegalTrademarks: Ни-Бэ-Ни-Мэ (Trademark) OriginalFilename: App4.exe PrivateBuild: ProductBuildPart: 0 ProductMajorPart: 1 ProductMinorPart: 0 ProductName: Суперприложение (Product) ProductPrivatePart: 5678 ProductVersion: 1.0.0.5678 SpecialBuild: Comments: vshost.exe CompanyName: Microsoft Corporation FileBuildPart: 21022 FileDescription: vshost.exe FileMajorPart: 9 FileMinorPart: 0 FileName: E:\Tmp\FilesAndDirectory\App4\bin\Debug\App4.vshost.exe FilePrivatePart: 8 FileVersion: 9.0.21022.8 InternalName: vshost.exe IsDebug: False IsPatched: False IsPrivateBuild: False IsPreRelease: False IsSpecialBuild: False Language: Независимо от языка LegalCopyright: © Microsoft Corporation. All rights reserved. LegalTrademarks: OriginalFilename: vshost.exe PrivateBuild: ProductBuildPart: 21022 ProductMajorPart: 9 ProductMinorPart: 0 ProductName: Microsoft (R) Visual Studio (R) 2008 ProductPrivatePart: 8 ProductVersion: 9.0.21022.8 SpecialBuild:
Здесь, на первый взгляд, казалось бы, наблюдается парадокс - исполнимая сборка проверяет саму себя! Ну и что, порядок ее создания таков: компилятор транслирует код и записывает его в файл. Затем оболочка загружает файл в память и запускает процесс CLR, который анализирует указанный нами файл на диске в соответствии с заложенным алгоритмом. Указали бы другой исполнимый или библиотечный файл, процесс проверял бы его. Конечно, можно было бы сделать и диалог для выбора файла, но не в этом суть данного упражнения.
В каталоге сборки bin оболочка создает еще один файл App4.vshost.exe, который используется для оптимизации отладки, поддержки политик безопасности, более детального контроля за работой приложения. Его создание для любого из режимов ( Debug и Release ) можно отключить в настройках оболочки.
Откройте окно свойств проекта командой Project/App4 Properties и на вкладке Debug сбросьте внизу флажок Enable the Visual Studio hosting process
В панели Solution Explorer щелкните на локальной пиктограмме Show All Files, разверните дерево каталогов проекта и удалите файл App4.vshost.exe
Запустите приложение - теперь метаданные выводятся только для файла самой сборки App4.exe, поскольку файл App4.vshost.exe уже не создается
В панели Solution Explorer командой Exclude From Project контекстного меню для файла AssemblyInfo.cs исключите его из текущего проекта и вновь запустите приложениеМы видим, что метаданные в сборку на сей раз не попали.
Верните через панель Solution Explorer файл AssemblyInfo.cs обратно в проект командой контекстного меню Include In Project, переименуйте его в произвольное имя (расширение .cs должно остаться!) и вновь запустите приложение - метаданные есть, значит имя файла с атрибутами дополнительной информации для компилятора несущественно, он их находит из общего входного потока компиляции
Откройте файл AssemblyInfo.cs, командой Edit/Select All выделите все его содержимое и скопируйте в верхнюю часть файла Program.cs перед оператором ' namespace App4 '
Удалите в панели Solution Explorer файл AssemblyInfo.cs физически командой Delete контекстного меню
Запустите проект - метаданные существуют и их можно добалять непосредственно в коде в глобальном пространстве именМожно задаться вопросом, а куда же вшивается дополнительная информация в сборке? Давайте посмотрим, куда. Для этого проанализируем сборку
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe
Раскройте меню Tools оболочки и запустите ILDasm
Если такой команды не окажется, то ее следует добавить. Для этого:
Выполните команду меню Tools/External Tools (внешние инструменты) и заполните диалоговое окно External Tools как показано на снимке
Теперь осталось воспользоваться инструментом ILDasm и проанализировать нашу сборку App4.exe на предмет просмотра ранее введенных атрибутов.
Запустите дизассемблер ILDasm и командой File/Open откройте файл E:\Tmp\FilesAndDirectory\App4\bin\Debug\App4.exe (у меня такой...)
Выполните команду View/MetaInfo/Show! (или выполните жест Ctrl+M ) - в конце документа окна MetaInfo будет секция Assembly, в которой и находятся введенные нами ранее атрибуты сборкиВот примерно так будет выглядеть секция Assembly (несколько укороченная) в документе окна MetaInfo
Assembly
-------------------------------------------------------
Token: 0x20000001
Name : App4
Public Key :
Hash Algorithm : 0x00008004
Version: 1.0.0.1234
Major Version: 0x00000001
Minor Version: 0x00000000
Build Number: 0x00000000
Revision Number: 0x000004d2
Locale: <null>
Flags : [none] (00000000)
CustomAttribute #1 (0c000001)
-------------------------------------------------------
CustomAttribute Type: 0a000001
CustomAttributeName: System.Reflection.AssemblyTitleAttribute :: instance void .ctor(class System.String)
Length: 38
Value : 01 00 21 d0 9f d1 80 d0 b8 d0 bb d0 be d0 b6 d0 > ! <
: b5 d0 bd d0 b8 d0 b5 20 41 70 70 34 20 28 54 69 > App4 (Ti<
: 74 6c 65 29 00 00 >tle) <
ctor args: ("Приложение App4 (Title)")
CustomAttribute #2 (0c000002)
-------------------------------------------------------
CustomAttribute Type: 0a000002
CustomAttributeName: System.Reflection.AssemblyDescriptionAttribute :: instance void .ctor(class System.String)
Length: 52
Value : 01 00 2f d0 a7 d0 b8 d1 82 d0 b0 d0 b5 d1 82 20 > / <
: d0 bc d0 b5 d1 82 d0 b0 d0 b4 d0 b0 d0 bd d0 bd > <
: d1 8b d0 b5 20 28 44 65 73 63 72 69 70 74 69 6f > (Descriptio<
: 6e 29 00 00 >n) <
ctor args: ("Читает метаданные (Description)")
CustomAttribute #4 (0c000004)
-------------------------------------------------------
CustomAttribute Type: 0a000004
CustomAttributeName: System.Reflection.AssemblyCompanyAttribute :: instance void .ctor(class System.String)
Length: 39
Value : 01 00 22 d0 a0 d0 be d0 b3 d0 b0 20 d0 b8 20 d0 > " <
: ba d0 be d0 bf d1 8b d1 82 d0 b0 20 28 43 6f 6d > (Com<
: 70 61 6e 79 29 00 00 >pany) <
ctor args: ("Рога и копыта (Company)")
CustomAttribute #5 (0c000005)
-------------------------------------------------------
CustomAttribute Type: 0a000005
CustomAttributeName: System.Reflection.AssemblyProductAttribute :: instance void .ctor(class System.String)
Length: 45
Value : 01 00 28 d0 a1 d1 83 d0 bf d0 b5 d1 80 d0 bf d1 > ( <
: 80 d0 b8 d0 bb d0 be d0 b6 d0 b5 d0 bd d0 b8 d0 > <
: b5 20 28 50 72 6f 64 75 63 74 29 00 00 > (Product) <
ctor args: ("Суперприложение (Product)")
CustomAttribute #6 (0c000006)
-------------------------------------------------------
CustomAttribute Type: 0a000006
CustomAttributeName: System.Reflection.AssemblyCopyrightAttribute :: instance void .ctor(class System.String)
Length: 35
Value : 01 00 1e 43 6f 70 79 72 69 67 68 74 20 c2 a9 20 > Copyright <
: 20 32 30 31 30 20 28 43 6f 70 79 72 69 67 68 74 > 2010 (Copyright<
: 29 00 00 >) <
ctor args: ("Copyright © 2010 (Copyright)")
CustomAttribute #7 (0c000007)
-------------------------------------------------------
CustomAttribute Type: 0a000007
CustomAttributeName: System.Reflection.AssemblyTrademarkAttribute :: instance void .ctor(class System.String)
Length: 36
Value : 01 00 1f d0 9d d0 b8 2d d0 91 d1 8d 2d d0 9d d0 > - - <
: b8 2d d0 9c d1 8d 20 28 54 72 61 64 65 6d 61 72 > - (Trademar<
: 6b 29 00 00 >k) <
ctor args: ("Ни-Бэ-Ни-Мэ (Trademark)")
CustomAttribute #10 (0c00000a)
-------------------------------------------------------
CustomAttribute Type: 0a00000c
CustomAttributeName: System.Reflection.AssemblyFileVersionAttribute :: instance void .ctor(class System.String)
Length: 15
Value : 01 00 0a 31 2e 30 2e 30 2e 35 36 37 38 00 00 > 1.0.0.5678 <
ctor args: ("1.0.0.5678")
CustomAttribute #11 (0c00000b)
-------------------------------------------------------
CustomAttribute Type: 0a00000d
CustomAttributeName: System.Resources.NeutralResourcesLanguageAttribute :: instance void .ctor(class System.String)
Length: 10
Value : 01 00 05 72 75 2d 52 55 00 00 > ru-RU <
ctor args: ("ru-RU")
Более подробная информация о сборках приведена, например, в http://www.intuit.ru/department/internet/prdevwebms/19/
Статический класс System.IO. Path содержит статические методы и поля, которые выполняют общие задачи манипуляции с путями. Путем называется строка, которая предшествует укороченному имени файла или каталога перед конечным обратным слэшем ' \ ' (разделителем составного имени).
| Метод | Описание |
|---|---|
GetDirectoryName() |
Возвращает имя каталога по полному имени файла |
GetFullPath() |
Возвращает полное имя файла по укороченному имени |
GetFileName() |
Возвращает укороченное имя файла по полному имени |
GetFileNameWithoutExtension() |
Возвращает укороченное имя файла без расширения |
GetExtension() |
Возвращает расширение файла с разделителем '.' |
Combine() |
Объединяет путь и имя файла, вставляя между ними разделитеь '\' |
GetPathRoot() |
Возвращает имя устройства, например, 'C:\' |
GetRandomFileName() |
Генерирует имя файла случайным образом базового формата DOS |
GetTempFileName() |
Генерирует заготовку полного имени временного файла для размещения в стандартном каталоге, который (каталог), возможно, еще предстоит создать |
GetTempPath() |
Генерирует заготовку полного имени каталога для размещения временных файлов, который, возможно, еще предстоит создать |
AltDirectorySeparatorChar, DirectorySeparatorChar, PathSeparator, VolumeSeparatorChar |
Общедоступные статические поля, содержащие образцы разделителей, используемых в системе: '/' '\' ';' ':' |
Добавьте к решению проект ( Console Application ) с именем App5 и назначьте его стартовым. Для этого выполните через меню оболочки вначале команду File/Add/New Project, а затем сразу команду Project/Set as StartUp Project (пока созданный проект оболочка считает текущим)
Заполните файл Program.cs следующим кодомusing System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DEBUG = System.Diagnostics.Debug; // Псевдоним класса
namespace App5
{
class Program
{
static void Main(string[] args)
{
String currentDirectory = Directory.GetCurrentDirectory(); // Каталог сборки
String message = "Без использования Path\n".ToUpper();
message += "currentDirectory: " + currentDirectory + "\n";
DirectoryInfo directory = new DirectoryInfo(currentDirectory);
FileInfo[] files = directory.GetFiles("*.exe"); // Список файлов по шаблону
FileInfo file;
if (files.Length > 0)
file = files[0]; // Берем первый попавшийся для анализа
else
return; // Нечего анализировать
string fullName = file.FullName;
string name = file.Name;
message += "fullName: " + fullName + "\n";
message += string.Format("name: {0}\n", name);
message += string.Format("\nС использованием Path\n").ToUpper();
String path;
message += "path = Path.GetDirectoryName(fullName): "
+ (path = Path.GetDirectoryName(fullName)) + "\n";
message += "Path.GetFullPath(name): "
+ Path.GetFullPath(name) + "\n";
message += "Path.GetFileName(fullName): "
+ Path.GetFileName(fullName) + "\n";
message += "Path.GetFileNameWithoutExtension(fullName): "
+ Path.GetFileNameWithoutExtension(fullName) + "\n";
message += "Path.GetExtension(fullName): "
+ Path.GetExtension(fullName) + "\n";
message += "Path.Combine(path, name): "
+ Path.Combine(path, name) + "\n";
message += "Path.GetPathRoot(path): "
+ Path.GetPathRoot(path) + "\n";
message += "Path.GetPathRoot(fullName): "
+ Path.GetPathRoot(fullName) + "\n";
message += "Path.GetRandomFileName(): "
+ Path.GetRandomFileName() + "\n";
message += "Path.GetRandomFileName(): "
+ Path.GetRandomFileName() + "\n";
message += "Path.GetRandomFileName(): "
+ Path.GetRandomFileName() + "\n";
message += "Path.GetTempFileName(): "
+ Path.GetTempFileName() + "\n";
message += "Path.GetTempFileName(): "
+ Path.GetTempFileName() + "\n";
message += "Path.GetTempPath(): "
+ Path.GetTempPath() + "\n";
message += "AltDirectorySeparatorChar: '"
+ Path.AltDirectorySeparatorChar + "'\n";
message += "DirectorySeparatorChar: '"
+ Path.DirectorySeparatorChar + "'\n";
message += "PathSeparator: '"
+ Path.PathSeparator + "'\n";
message += "VolumeSeparatorChar: '"
+ Path.VolumeSeparatorChar + "'\n";
String unsafeFileName = "..\\..\\..\\" + name; // Опасное имя файла
String safeFileName = Path.GetFileName(unsafeFileName);
message += "\nОпасное имя файла: " + unsafeFileName + "\n";
message += "Безопасное имя файла: " + safeFileName + "\n";
message += "Полное опасное имя: " + Path.Combine(path, unsafeFileName) + Environment.NewLine;
message += "Полное опасное имя: " + Path.GetFullPath(Path.Combine(path, unsafeFileName)) + "\n";
message += "Полное безопасное имя: " + Path.Combine(path, safeFileName);
// Печать результатов
Console.WriteLine(message); // На консоль
DEBUG.WriteLine(message); // На панель Output
Console.ReadLine(); // Задержка окна
}
}
}
Запустите проект - результат должен быть примерно такимБЕЗ ИСПОЛЬЗОВАНИЯ PATH currentDirectory: E:\Tmp\FilesAndDirectory\App5\bin\Debug fullName: E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe name: App5.exe С ИСПОЛЬЗОВАНИЕМ PATH path = Path.GetDirectoryName(fullName): E:\Tmp\FilesAndDirectory\App5\bin\Debug Path.GetFullPath(name): E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe Path.GetFileName(fullName): App5.exe Path.GetFileNameWithoutExtension(fullName): App5 Path.GetExtension(fullName): .exe Path.Combine(path, name): E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe Path.GetPathRoot(path): E:\ Path.GetPathRoot(fullName): E:\ Path.GetRandomFileName(): epviufbp.0k1 Path.GetRandomFileName(): tyzaz4fb.so2 Path.GetRandomFileName(): ucnivb2v.idh Path.GetTempFileName(): C:\Users\CHETKOB\AppData\Local\Temp\tmp15B4.tmp Path.GetTempFileName(): C:\Users\CHETKOB\AppData\Local\Temp\tmp15B5.tmp Path.GetTempPath(): C:\Users\CHETKOB\AppData\Local\Temp\ AltDirectorySeparatorChar: '/' DirectorySeparatorChar: '\' PathSeparator: ';' VolumeSeparatorChar: ':' Опасное имя файла: ..\..\..\App5.exe Безопасное имя файла: App5.exe Полное опасное имя: E:\Tmp\FilesAndDirectory\App5\bin\Debug\..\..\..\App5.exe Полное опасное имя: E:\Tmp\FilesAndDirectory\App5.exe Полное безопасное имя: E:\Tmp\FilesAndDirectory\App5\bin\Debug\App5.exe
Построим диалоговое приложение, позволяющее перемещаться по файловой системе и просматривать характеристики файлов. С оформлением пользовательского интерфейса сильно стараться не будем, а уделим больше внимания функциональности.
Добавьте к решению командой File/Add/New Project новый проект оконного приложения Windows Forms с именем App6 и назначьте его стартовым
В режиме Design выделите форму и установите для нее в панели Properties значения свойств
В режиме Design выделите форму и поместите из панели Toolbox дочерний компонент Panel (двойным щелчком на нем, или перетащите курсором). Установите для него в панели Properties значения свойств
Опять выделите саму форму, щелкнув на ее заголовке (или свободном месте клиентской области), поместите еще один дочерний элемент Panel и установите для него свойство
Через выпадающий список панели Properties выделите объект panel1, поместите на него дочерний компонент Button и настройте свойства так:
Снова выделите на форме объект panel1 и добавьте на него текстовую метку Label со свойствами:
Выделите объект panel2, поместите на него дочерний компонент SplitContainer и сместите у него разделитель влево, чтобы ширина его левой панели ( Panel1 ) была меньше ширины правой ( Panel2 ) и составляла от нее примерно одну четверть
Выделите левую панель ( Panel1 ) объекта splitContainer1 и поместите в нее компонент ListBox со свойствами:
Выделите правую панель ( Panel2 ) объекта splitContainer1 и поместите в нее компонент DataGridView со свойствами:На этом разработка пользовательского интерфейса закончена. Остальное, если возникнет необходимость, мы донастроим в процедурном коде, а пока форма будет выглядеть примерно так

Щелкните правой кнопкой мыши в любом месте на форме и выполните команду контекстного меню View Code, чтобы открыть кодовую часть - файл Form1.cs
Заполните файл Form1.cs следующим кодомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace App6
{
public partial class Form1 : Form
{
Panel fileDetails;// Поле, инициализируется неявно нулевым адресом: null
public Form1()
{
InitializeComponent();
// Программные донастройки
gridFile.ReadOnly = true; // Только для просмотра
gridFile.SelectionMode =
DataGridViewSelectionMode.FullRowSelect;// Выделять всю строку
// Начальная информация
currentPath = Directory.GetCurrentDirectory();
ShowDirectoryContents(currentPath);
fileDetails = splitContainer1.Panel2;
fileDetails.AutoScroll = true;
}
String currentPath;// Поле, инициализируется неявно пустой строкой
void ShowDirectoryContents(String path)
{
// Получить объект справки текущего каталога
DirectoryInfo curDir = new DirectoryInfo(path);
// Получить для текущего каталога
// объекты справки файлов и подкаталогов
FileInfo[] files = curDir.GetFiles();
DirectoryInfo[] dirs = curDir.GetDirectories();
lblCurrentDir.Text = "Текущий каталог: " + path;
// Привязываем к элементам отображения
listDir.DataSource = dirs;
listDir.DisplayMember = "Name"; // Отображаемый вектор
listDir.ValueMember = "Name"; // Скрытый вектор
gridFile.DataSource = files; // Показываем все
}
private void btnUp_Click(object sender, EventArgs e)
{
// Уже "Мой компьютер" и выше некуда
if (currentPath == String.Empty)
return;
// Хотим подняться выше текущего корня,
// поэтому выводим доступные устройства
else if (currentPath == Path.GetPathRoot(currentPath))
{
System.Collections.ArrayList array =
new System.Collections.ArrayList();
DriveInfo[] drives = DriveInfo.GetDrives();
// Фильтруем готовые устройства
foreach (DriveInfo drive in drives)
{
if (drive.IsReady)
{
String name = drive.Name;
name = name.Split('\\')[0];// Убираем слэш
array.Add(name);
}
}
listDir.DataSource = array; // Привязываем список доступных устройств
gridFile.DataSource = null; // Нет файлов
currentPath = String.Empty; // Пути нет
lblCurrentDir.Text = "Мой компьютер"; // Меняем заголовок метки
return;
}
// Находимся ниже корня текущего устройства
// и есть куда подниматься
else
{
String path = Path.Combine(currentPath, "..");
currentPath = Path.GetFullPath(path); // Этажом выше без '..'
ShowDirectoryContents(currentPath);
}
}
private void listDir_DoubleClick(object sender, EventArgs e)
{
String dir = listDir.SelectedValue.ToString();// Извлекаем из списка
currentPath = Path.Combine(currentPath, dir);
if (currentPath.IndexOf('\\') == -1)
currentPath += "\\"; // Если только что спустились в корень
ShowDirectoryContents(currentPath);
}
private void gridFile_Click(object sender, EventArgs e)
{
if (gridFile.Rows.Count == 0)
return; // Нечего выделять
// Локальные переменные инициализировать обязательно, а поля нет
int selectIndex = gridFile.CurrentCell.RowIndex;
DataGridViewRow row = gridFile.Rows[selectIndex];
DataGridViewCellCollection cells = gridFile.Rows[selectIndex].Cells;
String message = "";
// Извлекаем информацию из текущей строки
for (int i = 0; i < gridFile.Columns.Count; i++)
{
String columnName = gridFile.Columns[i].Name;
String columnValue = row.Cells[i].Value.ToString();
message += columnName + ": " + columnValue + "\n";
}
// Создаем окно и выводим информацию
Form frm = new Form();
frm.Width = 500;
frm.Height = 320;
frm.Text = "Подробности"; // Заголовок окна
frm.MinimizeBox = false; // Убираем системные кнопки
frm.MaximizeBox = false; // Убираем системные кнопки
frm.ShowInTaskbar = false; // Не отображать в панели задач
frm.StartPosition = FormStartPosition.CenterParent;// По центру родителя
// Промежуточная панель - для скролирования
Panel panel = new Panel();
panel.BackColor = System.Drawing.Color.White;
panel.Dock = DockStyle.Fill;
panel.AutoScroll = true;
frm.Controls.Add(panel);// Отдаем окну
// Отображающая текстовая метка
Label lbl = new Label();
lbl.AutoSize = true;
lbl.Parent = panel; // Отдаем панели
// panel.Controls.Add(lbl);// Вариант - отдаем панели
lbl.Text = message;
// Заряжаем дополнительные окна вывода раньше диалога
Console.Clear(); Console.Write(message); // !!!!!!!!!!!!!!!!!!!!
System.Diagnostics.Debug.Write(message + Environment.NewLine);
frm.ShowDialog(); // Показываем окно в модальном режиме
}
}
}
Запустите приложение - пока оно не реагирует на действия пользователя, поскольку интерфейсные элементы не присоединены к своим обработчикам
В режиме Design выделите кнопку btnUp, зайдите в панель Properties, щелкните на пиктограмме Events, раскройте список поля для события Click и выберите обработчик btnUp_Click
Подобным же образом присоедините к событию DoubleClick объекта списка listDir обработчик listDir_DoubleClick, и к событию Click объекта сетки gridFile - обработчик gridFile_Click
Запустите приложение - при щелчке на строке сетки выбрасывается исключение
Причина в том, что в коде обработчика gridFile_Click делается попытка вывода информации в консольное окно, которое не создано. Если мы не хотим использовать консольное окно, то следует закомментировать или удалить строку
Console.Clear(); Console.Write(message); // !!!!!!!!!!!!!!!!!!!!
Но мы добавим к приложению консольное окно, чтобы вспомнить, как это делается.
В панели Solution Explorer для узла App6 вызовите контекстное меню и выполните команду ProperiesДругой способ - в панели Solution Explorer выделить узел проекта App6 (или любой член проекта App6 ) и выполнить команду главного меню оболочки Project/App6 Properties.
На вкладке Application окна настроек проекта в выпадающем списке Output type установите значение Console Application
Запустите и испытайте разработанное приложениеОдин из рабочих моментов может выглядеть так

Еще один канал вывода подробностей, предусмотренный нами, - панель Output оболочки.
Разберитесь с кодомИногда может потребоваться упорядоченный список номеров, сгенерированных случайным образом. В данном упражнении мы разработаем такую утилиту, используя рассмотренные классы работы с файлами. Номера будем размещать в текстовом файле. Настройку утилиты организуем через диалоговое окно, а для контроля процесса добавим консольный вывод.
Добавьте к решению командой File/Add/New Project новый проект типа Windows Forms Application с именем App7 и назначьте его стартовым
Командой Project/App7 Properties вызовите диалоговое окно настройки проекта и в выпадающем списке Output type вкладки Application установите значение Console Application
Разработайте интерфейс диалогового окна в соответствии с таблицей свойств| Элемент | Свойство | Значение | Пояснения |
|---|---|---|---|
Form |
FormBorderStyle |
FixedDialog |
Фиксированный размер |
Size |
375; 385 | Ширина и высота окна | |
MaximizeBox |
False |
Скрыть системную кнопку | |
MinimizeBox |
False |
Скрыть системную кнопку | |
Text |
Генератор случайных номеров | Заголовок окна | |
StartPosition |
CenterScreen |
Начальное положение | |
Font |
Microsoft Sans Serif; 7,8pt |
Родительский шрифт (по умолчанию) | |
Label |
AutoSize |
False |
Подстройка размера под содержимое |
BackColor |
Info |
Цвет фона | |
Text |
Генерирует список номеров случайным образом | Содержимое | |
Font |
Arial; 12pt; style=Bold |
Шрифт | |
ForeColor |
Blue |
Цвет текста | |
TextAlign |
MiddleCenter |
Центрирование текста | |
Location |
16; 8 | Позиция относительно клиентской области формы | |
Label |
Text |
Куда разместить файл: | |
Button |
(Name) |
btnDirTarget |
Имя экземпляра кнопки |
Text |
Browse |
Метка кнопки | |
TextBox |
(Name) |
txtDirTarget |
Путь к файлу |
Label |
Text |
Имя текстового файла: | Метка для txtFileName |
TextBox |
(Name) |
txtFileName |
Текстовое поле для имени файла |
Text |
ResultNumber.txt |
Имя файла по умолчанию | |
Label |
Text |
Нижняя граница диапазона: | Текстовая метка |
TextBox |
(Name) |
txtMin |
Текстовое поле для нижней границы генерируемых номеров |
Text |
0 | Значение по умолчанию | |
Label |
Text |
Постоянный шаг: | Текстовая метка |
TextBox |
(Name) |
txtStep |
Дискрета приращения диапазона |
Text |
250 | Шаг по умолчанию | |
Label |
Text |
Количество точек: | Текстовая метка |
TextBox |
(Name) |
txtCount |
Число генерируемых номеров |
Text |
2000 | Значение по умолчанию | |
Label |
Text |
Верхняя граница диапазона: | Метка |
TextBox |
(Name) |
txtMax |
Расчет верхней границы |
Text |
Пусто | ||
ReadOnly |
True |
Только для чтения | |
Button |
(Name) |
btnExecute |
Кнопка выполнения |
Text |
Execute |
Метка кнопки |
Необязательно дословно следовать приведенной таблице, можно настроить окно и по своему. Главное, чтобы значимые элементы имели те же имена для согласованного управления в коде (который, я уверен, вами будет скопирован механически - программисты должны экономить свои силы, но лучше тогда, когда станут дорого стоить). При размещении элементов на форме важно следить за тем, чтобы они не были вложенными друг в друга, а каждый принадлежал только форме как своему ближайшему родителю.
Запустите приложение - пользовательский интерфейс будет выглядеть так
Создадим и заполним обработчики элементов управления в процедурном коде файла Form1.cs.
Добавьте в начало файла Form1.cs подключение библиотечного пространства имен System. IO для классов ввода-выводаusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Подключение дополнительных пространств имен
using System.IO;
В режиме Design двойным щелчком на заголовке формы (или ином незанятом компонентами месте) создайте обработчик начальной загрузки и заполните его такint min, step, count, max;
FolderBrowserDialog dialogFolder = new FolderBrowserDialog();
private void Form1_Load(object sender, EventArgs e)
{
if (txtDirTarget.Text == String.Empty ||
Directory.Exists(txtDirTarget.Text) == false)
{
dialogFolder.RootFolder =
System.Environment.SpecialFolder.MyComputer;
dialogFolder.SelectedPath = "";
txtDirTarget.Text = String.Empty;
}
else
{
dialogFolder.SelectedPath = txtDirTarget.Text;
}
dialogFolder.Description =
"Выберите место размещения текстового файла";
min = Convert.ToInt32(txtMin.Text);
step = Convert.ToInt32(txtStep.Text);
count = Convert.ToInt32(txtCount.Text);
max = min + step * count;
txtMax.Text = max.ToString();
}
За пределы функции вынесены поля-члены для их видимости в других обработчиках класса. В самом обработчике выполняется начальная инициализация этих переменных. Для выбора каталога используется стандартный диалог FolderBrowserDialog, который при запуске приложения устанавливается на вершину файловой системы компьютера, если выполняются указанные условия. Для явного преобразования типов используется библиотечный класс System.Convert.
В режиме Design выделите кнопку btnDirTarget, откройте панель Properties, щелкните на пиктограмме Events, найдите событие Click и двойным щелчком на правой ячейке сетки свойств создайте обработчик, который заполните такprivate void btnDirTarget_Click(object sender, EventArgs e)
{
dialogFolder.ShowDialog();
txtDirTarget.Text = dialogFolder.SelectedPath;
}
Обработчик запускает диалог выбора папки, при закрытии которого путь сохраняется и отображается в текстовом поле txtDirTarget. Далее мы планируем этот путь использовать для создания текстового файла с именем из поля txtFileName. Выбранный путь записывается в поле.
Здесь есть одна тонкость. Текстовое поле может редактироваться пользователем и, таким образом, может содержать недостоверный путь. Можно, конечно, создавать каталог программно, если он не существует, но для тренировки мы просто заблокируем это поле для редактирования. Если присвоить txtDirTarget свойство ReadOnly=True, то оно будет серым и некрасивым. Другая возможность - просто запретить ввод символов в это поле, что мы сейчас и сделаем.
Выделите текстовое поле txtDirTarget, в панели Properties установите режим Events и двойным щелчком на поле значений события KeyPress создайте обработчик со следующим кодомprivate void txtDirTarget_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = true;
}
Запустите приложение, выберите папку и попробуйте редактировать поле - стрелки и клавиша Delete продолжают функционироватьДобъем это поле окончательно.
Выделите текстовое поле txtDirTarget, в панели Properties установите режим Events и двойным щелчком на поле значений события KeyDown создайте обработчик со следующим кодомprivate void txtDirTarget_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
Запустите приложение - теперь поле полностью нередактируется, хотя и принимает курсор и фокус вводаТаким образом, мы видим, что с помощью событий KeyDown и KeyPress можно контролировать пользовательский ввод в экземпляр текстового поля. Применим этот факт для фильтрации цифровых клавиш в текстовых полях txtMin, txtStep и txtCount, которые должны принимать только целые числа. Создадим для них общие обработчики, поскольку условие ограничения по вводу только цифр для них общее. Но прежде нам нужно узнать, в какой последовательности срабатывают события KeyDown и KeyPress.
В режиме Design выделите (можно частично) с помощью лассо курсора (по русски - просто обведите, а используемое здесь словоблудие, чтобы привыкали к терминам) сразу 3 упомянутых текстовых поля и в панели Properties/Events задайте для них имена обработчиков, как показано на снимке
Заполните созданные обработчики кодом вывода информации на консольprivate void txt_KeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine("KeyDown");
}
private void txt_KeyPress(object sender, KeyPressEventArgs e)
{
Console.WriteLine("KeyPress");
}
Запустите приложение - получится последовательностьKeyDown KeyPress
Зная эту последовательность срабатывания, можно запрограммировать фильтр для пропуска только цифр.
Заполните общие обработчики следующим кодомbool isNumber;
private void txt_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
isNumber =
e.KeyCode >= Keys.D0 e.KeyCode <= Keys.D9 // keyboard - основная клавиатура
|| e.KeyCode >= Keys.NumPad0 e.KeyCode <= Keys.NumPad9 // keypad - дополнительная клавиатура
|| e.KeyCode == Keys.Back;
}
private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
// Запрещаем в текстовом поле лишние символы
if (!isNumber)
e.Handled = true;
}
В первом обработчике формируется флаг isNumber, а во втором обработчике этот флаг используется для отфильтровывания цифр. Последовательность срабатывания событий обеспечивает нам нужный конвейер действий.
Создадим еще один обработчик, в котором 'на лету' будет пересчитываться значение последнего текстового поля txtMax по данным других полей. Но прежде добавим маркеры исходных текстовых полей в код начальной инициализации, чтобы в общем обработчике можно было их идентифицировать (различать) и индивидуально обрабатывать.
Добавьте в функцию Form1_Load() следующий кодprivate void Form1_Load(object sender, EventArgs e)
{
...................................................
// Маркируем элементы для распознавания в общем обработчике
txtMin.Name = "Min";
txtStep.Name = "Step";
txtCount.Name = "Count";
}
В режиме Design выделите все три поля txtMin, txtStep и txtCount одновременно и создайте для них в панели Properties для события TextChanged общий обработчик с именем (нужно вручную ввести в поле) result_TextChanged, который заполните такprivate void result_TextChanged(object sender, EventArgs e)
{
// Следим, чтобы редактируемое поле не оказалось пустым
TextBox txtBox = (TextBox)sender;// Приводим ссылку к типу элемента
if (txtBox.Text == String.Empty)
{
switch (txtBox.Name)
{
case "Min":
txtMin.Text = "0";
break;
case "Step":
txtStep.Text = "0";
break;
case "Count":
txtCount.Text = "0";
break;
}
}
// Конвертируем разными способами
min = Convert.ToInt32(txtMin.Text);
step = Int32.Parse(txtStep.Text);
int.TryParse(txtCount.Text, out count);
max = min + step * count;
txtMax.Text = max.ToString();
}
В этом коде скрыт один коварный нюанс, на который следует обратить внимание. Дело в том, что текстовое поле способно принимать сколь угодно длинные тексты. А в нашем коде обработчика result_TextChanged набор цифр преобразуется в целый тип Int32, имеющий конечную длину. Соответственно, длинный ввод пользователя может вызвать исключение. Не погружаясь глубоко в эту проблему, в которой нужно было бы еще учесть операции умножения предельно допустимых значений ввода (или изменить тип int на long ), просто ограничим количество вводимых цифр барьером, способным уместиться в целом типе. Это явно приближенный подход, но просто для примера, только для тренировки!
Модифицируйте обработчик txt_KeyPress( ) полей ввода цифр такБыло
private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
// Запрещаем в текстовом поле лишние символы
if (!isNumber)
e.Handled = true;
}
Стало
private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
TextBox txtBox = sender as TextBox;// Приведение ссылки
int maxLength = int.MaxValue.ToString().Length - 1;// Длина ввода (с запасом)
// Запрещаем в текстовом поле лишние символы
// и ограничиваем длину пользовательского ввода
if (!isNumber || txtBox.Text.Length >= maxLength)
e.Handled = true;
}
Теперь осталось запрограммировать кнопку btnExecute и выполнить небольшие действия по сохранению состояния приложения.
В режиме Design двойным щелчком на кнопке btnExecute создайте обработчик события по умолчанию, который заполните такprivate void btnExecute_Click(object sender, System.EventArgs e)
{
String fileName = txtFileName.Text; // Копируем ссылку для удобства
// Заполнено ли текстовое поле
if (fileName.Length <= 0 || txtDirTarget.Text == String.Empty)
{
MessageBox.Show("Вы не задали имя файла или каталога",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
// Есть ли расширение txt
int pos0 = fileName.LastIndexOf('.');// Ищем с конца
if (pos0 == -1 || !String.Equals(fileName.Substring(pos0), ".txt"))
{
MessageBox.Show("Вы не задали расширение .txt",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
// Не допускать составное имя
int pos1 = fileName.IndexOf('.'); // Ищем с начала
if (pos0 != pos1) // Если точек несколько
{
MessageBox.Show("Задайте одно расширение .txt",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
// Добавляем путь = полное имя файла
fileName = Path.Combine(txtDirTarget.Text, fileName);
if (!File.Exists(fileName))
{
FileStream stream = File.Create(fileName);// Создать файл
stream.Close(); // Освободить ресурс
}
Console.Clear(); // Очистить консоль
Random rand = new Random();// Генератор случайных чисел по равномерному закону
int result;
StreamWriter writeFile = new StreamWriter(fileName, false,
System.Text.ASCIIEncoding.GetEncoding(1251));
for (int i = 0; i < count; i++)
{
result = min + i * step + rand.Next(step);
Console.WriteLine(result.ToString()); // Выводим на консоль
writeFile.WriteLine(result.ToString()); // Выводим в файл
}
writeFile.WriteLine("--------"); // Отделяем итог
writeFile.WriteLine(count.ToString() + " шт.");
writeFile.Close(); // Освободить ресурс
}
Запустите приложение - все должно работатьВ этом месте мне пришла мысль добавить еще одно текстовое поле для ввода серии (как в паспортах). Для этого нужно немного переделать пользовательский интерфейс формы. Заодно вспомним групповые операции выравнивания визуального конструктора форм.
В режиме Design не меняя размеров формы сдвиньте (стрелкой 'Вниз') кнопку Execute немного вниз
Охватите лассо курсора четыре нижних текстовых поля вместе с метками (до 'Имя текстового файла:') и тоже стрелкой 'Вниз' сдвиньте их вниз
Выделите текстовую метку 'Имя текстового файла:' вместе с ее полем, выполните Copy и сразу Paste
Выделите все пять текстовых полей и щелкните на кнопке Make Vertical Spacing Equal панели инструментов Layout, чтобы равномерно распределить элементы
То же самое действие примените к текстовым меткам
Настройте новые элементы в соответствии с стаблицей свойств| Элемент | Свойство | Значение |
|---|---|---|
Label |
Text |
Серия: |
TextBox |
( Name ) |
txtSeries |
Text |
Пусто |
Запустите приложение - интерфейс должен выглядеть примерно так
Теперь осталось включить значение серии в вывод программы.
Отредактируйте фрагмент кода обработчика btnExecute_Click( ) такБыло
for (int i = 0; i < count; i++)
{
result = min + i * step + rand.Next(step);
Console.WriteLine(result.ToString()); // Выводим на консоль
writeFile.WriteLine(result.ToString()); // Выводим в файл
}
Стало
for (int i = 0; i < count; i++)
{
result = min + i * step + rand.Next(step);
Console.WriteLine(txtSeries.Text.Trim() + result.ToString()); // Выводим на консоль
writeFile.WriteLine(txtSeries.Text.Trim() + result.ToString()); // Выводим в файл
}
В завершение, на примере последнего упражнения, добавим средства сохранения состояния, чтобы при новом запуске прежние настройки текстовых полей и текущий каталог остались. Существует несколько (рассмотрим три) стандартных способов решения этой задачи и все они основаны на использовании абстрактного класса ApplicationSettingsBase:
Два последних способа относятся к режиму разработки приложения (не к режиму выполнения). Их подробное описание можно найти в документации по ссылке: http://msdn.microsoft.com/ru-ru/library/a65txexh(VS.90).aspx
Способ 1.
Вначале рассмотрим программный способ (мы же программисты!).
Командой Project/Add Class добавьте новый файл с именем MySettings.cs, который заполните такusing System;
using System.Configuration;
namespace App7
{
class MySettings : ApplicationSettingsBase
{
// Параметр типа 'Приложение' менять нельзя во время работы приложения
// Параметр типа 'Пользователь' можно менять во время работы приложения
//[ApplicationScopedSetting()]// Область действия - 'Приложение'
[UserScopedSettingAttribute()] // Область действия - 'Пользователь'
[DefaultSettingValueAttribute("0")] // Значение параметра по умолчанию
public String Min
{
get
{
return ((String)this["Min"]);
}
set
{
this["Min"] = (String)value;
}
}
[UserScopedSettingAttribute()]
[DefaultSettingValueAttribute("250")]
public String Step
{
get
{
return ((String)this["Step"]);
}
set
{
this["Step"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("2000")]
public String Count
{
get
{
return ((String)this["Count"]);
}
set
{
this["Count"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("ResultNumber.txt")]
public String FileName
{
get
{
return ((String)this["FileName"]);
}
set
{
this["FileName"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("")]
public String DirTarget
{
get
{
return ((String)this["DirTarget"]);
}
set
{
this["DirTarget"] = (String)value;
}
}
[UserScopedSetting()]
[DefaultSettingValue("")]
public String Series
{
get
{
return ((String)this["Series"]);
}
set
{
this["Series"] = (String)value;
}
}
}
}
Здесь мы определили общедоступные свойства с ассоциативными именами (чтобы лучше различать) и прикрепили к каждому из них аттрибуты, определяющие область видимости (действия) параметра и значение по умолчанию. Параметр типа UserScopedSetting (или UserScopedSettingAttribute - полное имя) пользователь может менять во время работы приложения, а типа ApplicationScopedSetting - нет (например, строки подключения).
Теперь нужно создать экземпляр этого класса и связать его свойства, которые как раз и будут считаться именами сохраняемых параметров, со свойствами элементов управления.
Модифицируйте в файле Form1.cs конструктор класса формы приложения следующим образом// Объявили и инициализировали поле для объекта параметров
MySettings settings = new MySettings();
public Form1()
{
InitializeComponent();
// Привязка именованных параметров к
// сохраняемым свойствам элементов управления
txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
}
Запустите/завершите приложение - пока значения текстовых полей не сохраняютсяПричина здесь в том, что привязанные параметры нужно явно записывать при завершении приложения. Обычно это делается в обработчике события FormClosing формы.
Выделите форму в режиме Design, перейдите в панель Properies/Events и двойным щелчком на поле события FormClosing создайте для него обработчик, который заполните такprivate void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
settings.Save();
}
Запустите/завершите приложение - значения текстовых полей сохраняются, кроме поля txtDirTargetНестабильность в сохранении значения текстового поля txtDirTarget объясняется тем, что мы его заполняем программно и элемент только показывает присвоенное значение, а не принимает его, пока не получит фокус ввода. Здесь можно ввести дополнительную переменную, в ней хранить значение текстового поля и ее же сохранять. Но чтобы много не переделывать, можно просто программно передавать фокус ввода текстовому полю каждый раз после получения им нового значения от диалога выбора каталога.
Дополните обработчик кнопки btnDirTarget строкой передачи фокуса текстовому полю txtDirTarget такprivate void btnDirTarget_Click(object sender, EventArgs e)
{
dialogFolder.ShowDialog();
txtDirTarget.Text = dialogFolder.SelectedPath;
txtDirTarget.Focus();// Без этого не сохраняет параметр в settings
}
Последний оператор устанавливает на текстовое поле txtDirTarget фокус ввода, без чего значение Text не сохраняется в установках приложения при его закрытии (если только явно не щелкнуть по нему курсором).
Запустите/завершите приложение - теперь состояние приложения сохраняется полностьюКод, помещенный нами ранее в начало функции Form1_Load(), выполняет проверку на случай первого запуска приложения или когда между запусками сохраненное значение каталога изменилось. Чтобы испытать его работу, можно добавить такой обработчик двойного щелчка формы
private void Form1_DoubleClick(object sender, EventArgs e)
{
settings.DirTarget = "";
txtDirTarget.Text = "";
dialogFolder.SelectedPath = "";
dialogFolder.RootFolder =
System.Environment.SpecialFolder.MyComputer;
}
Способ 2.
При создании нового проекта мастер оболочки попутно создает заготовки файлов Settings.settings и Settings.Designer.cs, в которые на этапе проектирования можно записать для компилятора исходные данные параметров, подлежащих сохранению. Эти файлы находятся в служебной папке Properties каталога проекта. Для удобства выполнения такой работы оболочка имеет вкладку (страницу) Settings панели конструктора проектов.
Чтобы продолжить рассмотрение Способа 2, необходимо вначале нейтрализовать весь код Способа 1, поскольку сохранять мы будем те же самые свойства текстовых полей.
В панели Solution Explorer выделите файл MySettings.cs и командой Exclude From Project контекстного меню (либо в разделе Project меню оболочки) исключите его из проекта
Закомментируйте (не удаляйте!) в конструкторе класса формы код привязки сохраняемых параметров к свойствам элементов// Объявили и инициализировали поле для объекта параметров
//MySettings settings = new MySettings();
public Form1()
{
InitializeComponent();
// Привязка именованных параметров к
// сохраняемым свойствам элементов управления
/*////////////////////////////////////////////////////
txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
////////////////////////////////////////////////////*/
}
Командой Project/App7 Properties откройте панель конструктора проектов на вкладке Settings и настройте ее как показано на снимке
Если щелкнуть на пиктограмме View Code вкладки Settings, то оболочка создаст в корневом каталоге проекта файл Settings.cs с еще одной частью класса Settings (первая часть находится в Properties\Settings.Designer.cs ) для более тонкого управления параметрами.
Сохраните документ командой File/Save All - оболочка создала конфигурационный файл приложения app.config и добавила в него наши настройкиТеперь осталось подключить эти настройки к коду приложения.
Модифицируйте в файле Form1.cs члены класса формы следующим образом// Объявили и инициализировали поле для объекта параметров
//MySettings settings = new MySettings();
Properties.Settings settings = new App7.Properties.Settings();
public Form1()
{
InitializeComponent();
// Привязка именованных параметров к
// сохраняемым свойствам элементов управления
/*////////////////////////////////////////////////////
txtMin.DataBindings.Add(new Binding("Text", settings, "Min"));
txtStep.DataBindings.Add(new Binding("Text", settings, "Step"));
txtCount.DataBindings.Add(new Binding("Text", settings, "Count"));
txtFileName.DataBindings.Add(new Binding("Text", settings, "FileName"));
txtDirTarget.DataBindings.Add(new Binding("Text", settings, "DirTarget"));
txtSeries.DataBindings.Add(new Binding("Text", settings, "Series"));
////////////////////////////////////////////////////*/
// Чтение параметров
//*////////////////////////////////////////////////////
txtMin.Text = settings.Min;
txtStep.Text = settings.Step;
txtCount.Text = settings.Count;
txtFileName.Text = settings.FileName;
txtDirTarget.Text = settings.DirTarget;
txtSeries.Text = settings.Series;
////////////////////////////////////////////////////*/
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Запись параметров
//*////////////////////////////////////////////////////
settings.Min = txtMin.Text;
settings.Step = txtStep.Text;
settings.Count = txtCount.Text;
settings.FileName = txtFileName.Text;
settings.DirTarget = txtDirTarget.Text;
settings.Series = txtSeries.Text;
////////////////////////////////////////////////////*/
settings.Save();
}
Запустите проект в режиме Release, измените настройки и закройте приложение - параметры сохраняются
В панели Solution Explorer щелкните на пиктограмме Show All Files - к исполнимой сборке добавился файл конфигурации App7.exe.configТеперь без этого файла приложение работать не будет и при развертывании приложения на других компьютерах файл придется включать в установочный пакет (это не страшно, только бы не забыть!). Файл App7.exe.config имеет такое же содержимое (откройте его!), что и app.config, но носит имя приложения.
Действием, приведенным в последнем листинге, нам пришлось добавить код чтения и записи параметров, потому что перед этим мы закомментировали (но не удалили!) код их привязки к свойствам интерфейсных элементов. Если теперь закомментировать свежие блоки кода, а раскомментировать код привязки, то функциональность приложения не изменится. Проверьте это, просто убрав или добавив слэш перед открывающей комментарной скобкой.
Способ 3.
Этот способ выполняет создание и привязку параметров через панель свойств и подробно описан в документации http://msdn.microsoft.com/ru-ru/library/wabtadw6%28v=VS.90%29.aspx
Как мы сейчас увидим, в конечном итоге это то же самое, что и предыдущий способ сохранения параметров на этапе разработки. Только он больше подходит для сохранения свойств именно компонентов, а не прочих объектов приложения. Рассмотрим его применительно к нашей задаче.
Командой Project/App7 Properties откройте панель конструктора проектов на вкладке Settings и удалите прежние параметры (по одному или сразу все)
Через Solution Explorer удалите файл app.config из корневого узла текущего проекта
В режиме Design выделите элемент txtMin, в панели Properties раскройте узел ApplicationSettings и щелкните на кнопке с многоточием в поле свойства PropertyBinding
Выделите привязываемое свойство Text элемента txtMin и в раскрывающемся списке выполните ссылку New для вызова окна создания нового параметра
Заполните окно как показано на снимке
Аналогичным образом создайте и настройте параметры и для остальных элементов со значениями из предыдущих способовОбратите внимание, что начальные значения параметров (если они есть) подставляются оболочкой из свойств компонентов, которые мы определяли при их создании. В итоге мы получим точно такой же результат, что и в предыдущем способе. Поэтому код в классе Form1, связанный с настройками, менять не будем - он у нас уже есть.
Запустите проект - параметры сохраняются, но опять при развертывании (распространении программы) нужно таскать и файл app.config (или точно такой же App7.exe.config )Итак, в данном упражнении мы спроектировали инструмент, который может оказаться удобным при формировании некоторых номеров случайным образом (чтобы враги уж совсем не угадали, можно еще и зашифровать). При этом применили знания по теме данной работы. Из расссмотренных способов мне больше нравится первый. Чтобы к нему вернуться, нужно вновь добавить в проект файл MySettings.cs и удалить из него все, связанное с двумя последними способами.
Следует заметить, что содержимое файла Settings.Designer.cs в папке Properties проекта, полученное после настроек с использованием любого из последних двух способов, почти полностью идентично содержимому файла MySettings.cs, который мы формировали вручную в первом способе. Отсюда мораль: если не хотим таскать за собой файл app.config, то вначале быстренько создаем параметры через оболочку, затем перетаскиваем созданный ею код в свой класс. После этого опять удаляем все параметры (как перед выполнением способа 3) и файл конфигурации, а используем уже наш класс с параметрами программным образом.
(
Что называется, - 'немного подъехать' на возможностях оболочки!)
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.