истинге 7.8. Сначала надо установить ссылку на пространство имен
Microsoft.WindowsCE.Forms
. После этого следует просто использовать нужные свойства класса SystemSettings
.Листинг 7.8.using Microsoft.WindowsCE.Forms;
// запоминаем настройки экрана
ScreenOrientation initialOrientation = SystemSettings.ScreenOrientation;
private void butRot90_Click(object sender, EventArgs e) {
// поворачиваем экран на 90 градусов
SystemSettings.ScreenOrientation = ScreenOrientation.Angle90;
}
private void butRestore_Click(object sender, EventArgs e) {
// восстанавливаем старую ориентацию
if (SystemSettings.ScreenOrientation != initialOrientation) {
try {
SystemSettings.ScreenOrientation = initialOrientation;
} catch (Exception) {
// Невозможно вернуться к старым настройкам
MessageBox.Show("He могу восстановить " +
"предыдущую ориентацию экрана.");
}
}
}
Рекомендации по дизайну форм
Компания Microsoft выработала определенные рекомендации по дизайну форм, которых следует придерживаться. Эти рекомендации можно найти в документации MSDN. Так, например, в одной из статей указывается, что в некоторых случаях пользователь предпочитает пользоваться пальцами вместо стилуса. Поэтому, если форма содержит кнопки, то они должны быть достаточно большими, чтобы было удобно нажимать на них.
Рекомендуемые размеры кнопок для Pocket PC составляют 21×21 пикселов, если пользователь применяет стилус, и 38×38 пикселов, если он предпочитает нажимать кнопки пальцами. Также надо предусмотреть свободное пространство между элементами, чтобы избежать ошибочных нажатий. Если маленькие кнопки для копирования и удаления файлов находятся рядом, то пользователи не раз вспомнят вас недобрым словом при неаккуратном нажатии на кнопки.
В то же время следует группировать часто используемые элементы, чтобы пользователю не пришлось часто перемещать стилус. Это тоже достаточно утомительно. Понятно, что к играм эти рекомендации не относятся. В них действуют свои законы.
Готовые приложения
До сих пор мы с вами изучали примеры, которые могли бы стать частью настоящих программ. Но сейчас пришло время написать несколько приложений, которые уже можно использовать в реальной жизни. Так получилось, что первые две программы были написаны для смартфонов, о которых речь пойдет в дальнейших главах. Но при помощи этих примеров можно получить представление о программировании для этого класса устройств. К тому же, на их основе можно написать аналогичный пример для карманных компьютеров, что поможет увидеть сходство в написании приложений для разных типов устройств.
Файловый менеджер для смартфона
Смартфоны под управлением Windows Mobile 2005 не имеют в составе системы приложения, которое позволяет просматривать содержимое папок и файлов. В этом разделе будет рассматриваться аналог Проводника для смартфона. Основой данного примера послужил великолепный проект с открытым исходным кодом, который находится на сайте www.businessanyplace.net/?p=spfileman. Автор проекта Кристиан Форсберг (Christian Forsberg) любезно разрешил использовать его программу в качестве учебного пособия для этой книги.
Интересна история создания этого приложения. Сам автор оригинальной версии писал программу еще на Visual Studio .NET 2003 для смартфонов под управлением системы Smartphone 2003. Когда я скачал исходный код и попытался запустить его, то среда разработки Visual Studio .NET 2005 предложила конвертировать проект. Я согласился, в результате получил новую версию проекта. Теперь, после переделки, проект запускался в Visual Studio 2005 и использовал эмулятор Smartphone 2003. Но мне захотелось использовать пример для смартфонов Windows Mobile 2005. Для этого достаточно было в свойствах проекта выбрать другую платформу. Снова несколько минут раздумий, и Visual Studio выдает еще раз переделанный проект. Но и этого мне мало. Проект по-прежнему использует .NET Compact Framework 1.0. После выбора нужных значений свойств проекта, он стал использовать .NET Compact Framework 2.0. Осталось лишь перевести некоторые команды на русский язык и ввести новые возможности .NET Compact Framework 2.0 вместо старых конструкций, которые применялись для .NET Compact Framework 1.0.
Зачем нужен файловый менеджер
Файловый менеджер необходим как для разработчиков, так и для пользователей. С его помощью можно просматривать содержимое папок, удалять лишние файлы, создавать новые папки и выполнять другие операции с файлами. Трудно сказать, почему Microsoft решила не включать программу подобного рода в состав системы Windows Mobile. Кстати, производители смартфонов самостоятельно добавляют файловые менеджеры собственной разработки в состав стандартных программ. Но мы с вами напишем свою программу, что гораздо интереснее.
Графический интерфейс программы
У создаваемого приложения будет своя пиктограмма. При запуске программа будет отображать содержимое папки
My Documents
. Сам графический интерфейс программы очень прост и понятен. Навигация по папкам осуществляется простым выделением нужной папки и нажатием кнопки Enter
. Для перехода на один уровень вверх нужно выделить папку, обозначенную двумя точками. Пункт меню Выход
закрывает файловый менеджер, а пункт Меню
позволяет выбрать выполняемую операцию (рис. 7.3).Рис. 7.3. Общий вид файлового менеджера
Меню содержит команды для всех стандартных файловых операций. Пользователь может удалять, копировать, добавлять и переименовывать файлы. Также имеется возможность работы с ярлыками. Чтобы использовать эту возможность, нужно сначала выбрать файл, выполнить команду Копировать, затем перейти в нужную папку и выполнить команду Вставить ярлык. При выборе команды Свойства появляется соответствующее окно (рис. 7.4).
Рис. 7.4. Окно свойств
В этом окне отображается справочная информация о файле или папке. Пользователь сможет найти размер файла, дату его создания и атрибуты файла, которые можно модифицировать.
Код программы
Теперь можно приступить к написанию кода. При запуске программы выполняется обработчик события
Form_Load
. При загрузке основной формы MainForm
работает код, приведенный в листинге 7.9.Листинг 7.9ListViewHelper.SetGradient(listView);
string bs = Path.DirectorySeparatorChar.ToString();
// Устанавливаем начальную папку
this.path = bs + "My Documents" + bs;
// Заполняем список папок и файлов
fillList();
Сначала устанавливается внешний вид элемента
listView
с градиентной закраской фона. Затем устанавливается папка по умолчанию My Documents
, которую программа открывает при загрузке. Метод fillList
заполняет список ListView
содержимым открываемой папки. Сам код метода приведен в листинге 7.10.Листинг 7.10///
/// Заполнение ListView списком папок и файлов
///
private void fillList() {
Cursor.Current = Cursors.WaitCursor;
// Заполняем ListView списком папок и файлов
// в выбранной папке
ListViewItem lvi;
listView.BeginUpdate();
listView.Items.Clear();
// Если не корневая папка
if (path.Length > 1) {
// Добавляем папку "Вверх"
lvi = new ListViewItem(UPDIR);
lvi.ImageIndex = 0;
listView.Items.Add(lvi);
}
// Добавляем папки
string[] dirs = Directory.GetDirectories(path);
ArrayList list = new ArrayList(dirs.Length);
for(int i = 0; i < dirs.Length; i++)
list.Add(dirs[i]);
list.Sort(new SimpleComparer());
foreach(string dir in list) {
lvi = new ListViewItem(Path.GetFileName(dir));
lvi.ImageIndex = 0;
listView.Items.Add(lvi);
}
// Добавляем файлы
string[] files = Directory.GetFiles(path);
list = new ArrayList(files.Length);
for(int i = 0; i < files.Length; i++)
list.Add(files[i]);
list.Sort(new SimpleComparer());
foreach(string file in list) {
lvi = new ListViewItem(Path.GetFileName(file));
lvi.ImageIndex = 1;
listView.Items.Add(lvi);
}
listView.EndUpdate();
if (listView.Items.Count > 0) {
// выделяем первый элемент
listView.Items[0].Selected = true;
listView.Items[0].Focused = true;
}
Cursor.Current = Cursors.Default;
}
Итак, посмотрим, что делает метод
fillList
. Перед заполнением элемента списком файлов надо очистить его содержимое от предыдущих записей при помощи метода Clear
. После очистки списка надо проверить, является ли папка корневой. Если папка не корневая, то в список добавляется специальная папка «На один уровень выше». Затем в список добавляются все папки в отсортированном порядке.После этого наступает очередь файлов. Они сортируются и заносятся в список. Наконец, первый элемент списка выделяется другим цветом. Заодно на первом элементе устанавливается фокус ввода. Навигация по папкам и файлам осуществляется с помощью кнопок и дополняется кнопкой
Action
. Код навигации приведен в листинге 7.11.Листинг 7.11///
/// Навигация по папкам и файлам
///
///
///
private void listView_ItemActivate(object sender, System.EventArgs e) {
Cursor.Current = Cursors.WaitCursor;
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
if (lvi.Text == UPDIR) {
path = path.Substring(0,
path.Substring(0,
path.Length - 1).LastIndexOf(Path.DirectorySeparatorChar) + 1);
fillList();
} else if (isFolder) {
path += lvi.Text + Path.DirectorySeparatorChar;
fillList();
} else
ShellExecute.Start(path + lvi.Text);
Cursor.Current = Cursors.Default;
}
После нажатия кнопки действия приложение получает информацию о выделенном пункте. Если выделена специальная папка перехода на один уровень выше, то текущий путь заменяется путем к родительской папке. Если выделена папка, то путь меняется на путь к выделенной папке. Если выделен файл, то приложение пытается запустить его с помощью ассоциированной программы.
Теперь разберем код для команд меню. Для команды
Вырезать
код приведен в листинге 7.12.Листинг 7.12private void cutMenuItem_Click(object sender, System.EventArgs e) {
ListViewItem lvi =
listView.Items[listView.SelectedIndices[0]];
clipboardFileName = this.path + lvi.Text;
clipboardAction = ClipboardAction.Cut;
}
Путь к текущему выбранному файлу сопоставляется с производимым действием. Код, выполняющийся после выбора команды
Копировать
, приведен в листинге 7.13.Листинг 7.13private void copyMenuItem_Click(object sender, System.EventArgs e) {
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
clipboardFileName = path + lvi.Text;
clipboardAction = ClipboardAction.Copy;
}
Для команды меню
Вставить
код немного усложняется. Он приведен в листинге 7.14.Листинг 7.14private void pasteMenuItem_Click(object sender, System.EventArgs e) {
// Если файл существует
string dest = path + Path.GetFileName(clipboardFileName);
if (File.Exists(dest)) {
if (MessageBox.Show("Файл уже существует, перезаписать?", this.Text,
MessageBoxButtons.YesNo, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) == DialogResult.Yes)
File.Delete(dest);
else return;
}
// Перемещаем или копируем
string s = path.Substring(0, path.Length - 1);
switch(clipboardAction) {
case ClipboardAction.Cut:
File.Move(clipboardFileName, dest);
break;
case ClipboardAction.Copy:
File.Copy(clipboardFileName, dest, false);
break;
}
clipboardAction = ClipboardAction.None;
clipboardFileName = string.Empty;
fillList();
}
Перед тем как вставить файл в другую папку, нужно удостовериться, что в ней нет файла с таким именем. Если же такой файл существует, то надо предупредить пользователя и узнать, что он хочет сделать. Код для команды
Вставить ярлык
приведен в листинге 7.15.Листинг 7.15private void pasteShortcutMenuItem_Click(object sender, System.EventArgs e) {
int i = 2;
string s = string.Empty;
string dest;
while(true) {
dest = path + "Shortcut" + s + " to " +
Path.GetFileName(Path.GetFileNameWithoutExtension(clipboardFileName) +
".lnk");
if (!File.Exists(dest)) break;
s = " (" + i.ToString() + ")";
i++;
}
StreamWriter sw = new StreamWriter(dest);
s = clipboardFileName;
if(s.IndexOf(" ") > 0)
s = "\"" + s + "\"";
s = s. Length.ToString() + "#" + s;
sw.WriteLine(s);
sw.Close();
fillList();
}
В этом коде создается уникальное имя ярлыка, которое затем записывается в виде файла с добавлением. К имени ярлыка добавляется расширение
.LNK
.Код для команды
Переименовать
приведен в листинге 7.16.Листинг 7.16private void renameMenuItem_Click(object sender, System.EventArgs e) {
Cursor.Current = Cursors.WaitCursor;
istViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex = 0;
string s;
if (isFolder)
s = "папку";
else s = "файл";
NameForm nameForm =
new NameForm(this, "Переименовать " + s, lvi.Text,
new SetNameDelegate(SetRename));
if (nameForm.ShowDialog() = DialogResult.OK) fillList();
listView.Focus();
}
Сначала обрабатывается текущий выделенный элемент. Если пользователь выделил папку, то для формы
nameForm
задается соответствующий заголовок Переименовать папку
. Также из этой формы передается в основную форму новое имя папки или файла с помощью метода Set Rename
, как это показано в листинге 7.17.Листинг 7.17///
/// Метод для переименования папки или файла
///
/// Имя папки или файла
public void SetRename(string name) {
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
string itemName = path + lvi.Text;
string destName =
Path.GetDirectoryName(itemName) +
Path.DirectorySeparatorChar.ToString() + name;
if (isFolder)
Directory.Move(itemName, destName);
else
File.Move(itemName, destName);
}
После того как будет получена информация о выделенном элементе, он переименовывается. Для реализации команды
Удалить
используется код, приведенный в листинге 7.18.Листинг 7.18private void deleteMenuItem_Click(object sender,
System.EventArgs e) {
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
string s = "Are you sure you want to delete " + lvi.Text;
if (isFolder)
s += " and all its content";
s += "?";
if (MessageBox.Show(s, this.Text, MessageBoxButtons.YesNo,
MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) ==
DialogResult.Yes) {
if (isFolder)
Directory.Delete(path + lvi.Text, true);
else
File.Delete(path + lvi.Text);
fillList();
}
Перед удалением папки или файла запрашивается подтверждение действий пользователя. Для создания новой папки используется следующий код, приведенный в листинге 7.19.
Листинг 7.19private void newFolderMenuItem_Click(object sender, System.EventArgs e) {
Cursor.Current = Cursors.WaitCursor;
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
NameForm nameForm = new NameForm(this, "Новая папка", "",
new SetNameDelegate(SetNewName));
if (nameForm.ShowDialog() == DialogResult.OK) fillList();
listView.Focus();
}
В результате действия этой функции отображается форма
NameForm
с заголовком Новая папка
. Эта форма также передает информацию в главную форму при помощи метода SetNewName
, который приведен в листинге 7.20.Листинг 7.20///
/// Устанавливает новое имя для папки
///
/// Имя для папки
public void SetNewName(string name) {
Directory.CreateDirectory(path + name);
}
Метод создает папку с заданным именем. Как видно, код его чрезвычайно прост.
Код для выполнения команды
Свойства
приведен в листинге 7.21.Листинг 7.21private void propertiesMenuItem_Click(object sender, System.EventArgs e) {
Cursor.Current = Cursors.WaitCursor;
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
FileInfo fi = new FileInfo(path + lvi.Text);
PropertiesForm propertiesForm =
new PropertiesForm(this, fi, new SetNameDelegate(SetRename),
new SetAttributesDelegate(SetAttributes));
if (propertiesForm.ShowDialog() == DialogResult.OK) fillList();
listView.Focus();
}
Этот код вызывает форму
PropertiesForm
, которая отображает атрибуты выбранного файла или папки. Также в этой форме пользователь может изменять атрибуты файла при помощи метода SetAttributes
, код которого приведен в листинге 7.22.Листинг 7.22public void SetAttributes(FileAttributes fileAttributes) {
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex = 0;
if (isFolder) {
DirectoryInfo di = new DirectoryInfo(path + lvi.Text);
di.Attributes = fileAttributes;
} else {
FileInfo fi = new FileInfo(path + lvi.Text);
fi.Attributes = fileAttributes;
}
}
Для создания градиентной заливки соответствующего элемента интерфейса применяется метод, код которого приведен в листинге 7.23.
Листинг 7.23public static void SetGradient(System.Windows.Forms.ListView listView) {
// Новый вариант
// Для .NET Compact Framework 2.0
SendMessage(listView.Handle, LVM_SETEXTENDEDLISTVIEWSTYLE,
LVS_EX_GRADIENT);
listView.Refresh();
}
Итак, основные трудности реализации программы рассмотрены. Кроме того, в примере присутствуют вызовы функций Windows API для работы с табличным списком
ListView
. Эти примеры рассматривались в главе 4, поэтому не стоит повторять их разбор. На самом деле эту программу можно улучшать до бесконечности, добавляя новые функциональные возможности. Надеюсь, у вас это получится. Буду рад, если вы пришлете свои варианты примеров, которые, на ваш взгляд, украсят программу.Диспетчер задач