Академический Документы
Профессиональный Документы
Культура Документы
Кафедра: САПР
УЧЕБНОЕ ПОСОБИЕ
ЛАБОРАТОРНЫЙ ПРАКТИКУМ
студента-дипломника Ермакова Р. Г.
Москва 2012 г.
СОДЕРЖАНИЕ
11. Лабораторная работа № 11: Динамическая связь приложений через библиотеку классов
....................................................................................................................................................324
2
22. Лабораторная работа № 22: Различные примеры на F#..................................................717
3
1. Лабораторная работа № 1: Знакомство с Visual Studio 2010 на примере
консольного приложения
Содержание
1. Вводная часть
2. Знакомство со средой разработки на примере создания простого
консольного приложения
3. Модификация консольного приложения
4. Публикация
5. О приложении к Лабораторной работе № 1
1. Вводная часть
4
ориентированных языках программирования или инструментов для прочих аспектов
цикла разработки программного обеспечения (например, клиент Team Explorer для
работы с Team Foundation Server).
5
Рис. 1. 1. Получение среды разработки на сайте DreamSpark.com
(http://www.dreamspark.com)
6
Рис. 2. 1. Начальная страница Visual Studio 2010 Professional (русская версия)
Для начала, надо создать пустой проект, для этого выполним последовательно:
Файл -> Создать -> Проект… (также можно просто нажать сочетание клавиш
Ctrl+Shift+N или пункт «Создать проект…» на Начальной странице):
7
Проект (Project) используется в Visual Studio для логической группировки
нескольких файлов, содержащих исходный код, на одном из поддерживаемых языков
программирования, а также любых вспомогательных файлов (ресурсы для проекта: это
могут быть и дополнительные библиотеки, файлы изображений, иконок и прочее).
Обычно после сборки проекта (которая включает компиляцию всех входящих в проект
файлов исходного кода) создается один исполняемый модуль (исполняемый файл),
либо несколько, если это веб-приложение.
Фактически, Проект это структурированная директория под конкретную
программу написанную программистом и содержащая версии сборки
скомпилированной программы (по конфигурациям по умолчанию: Release или Debug).
8
Параметры… -> Проекты и решения -> меняем путь в поле Размещение
проектов). Выберем расположение удобное для быстрого поиска. В поле Имя
решения вводится либо название программы «по умолчанию» из поля Имя
автоматически, либо можно ввести своё собственное. Под этим именем будет создана
конечная папка проекта (если Имя и Имя решения разные).
9
Рис. 2. 5. Исходный код консольного приложения сформированного средой разработки
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LWP01Console
{
class Program
{
static void Main(string[] args)
{
}
}
}
10
Рис. 2. 5. Запуск консольного приложения по конфигурации Debug
11
Рис. 2. 7. Окно Добавить ссылку
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет
видимым для COM
[assembly: Guid("eb30278d-af90-4a61-a3e4-d64ba45f6b7c")]
12
// используя "*", как показано ниже:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Содержание пунктов говорит само за себя, здесь можно менять все атрибуты
нашего приложения, включая версию приложения и описание.
Управлять напрямую (то есть менять значения в самом файле) не обязательно.
Для этого есть инструмент, который можно активировать так: правой кнопкой мыши
жмём на название проекта LWP01Console, далее выбираем Свойства (Alt+Enter).
Откроется окно свойств текущего проекта (в решении):
13
Рис. 2. 9. Окно Сведения о сборке консольного приложения
14
Рис. 2. 10. Свойства консольного приложения (скомпилированного)
При выделении какого либо объекта в обозревателе решений можно увидеть его
текущие свойства и установки для данного проекта. Для пространство имён
Microsoft.Csharp это выглядит так:
15
Рис. 2. 11. Свойства объекта
16
Рис. 3. 1. Отображение номера строки
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LWP01Console
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!\nИ по-русски можно? Конечно, привет мир!\
nThis is my first application using C#!");
Console.ReadKey();
}
}
}
17
Рис. 3. 2. Модифицированное консольное приложение
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LWP01Console
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!\nИ по-русски можно? Конечно, привет мир!\
nThis is my first application using C#!");
Console.ReadKey();
Console.Clear();
//
Console.WriteLine("Пожалуйста, введите символы:\n");
String a;
a = Console.ReadLine();
Console.WriteLine("\n\tВы ввели символ: " + a + ".");
Console.ReadKey();
Console.Clear();
//
Console.WriteLine("Пожалуйста, введите число элементов массива:\n");
int x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("\nПожалуйста, введите элемент массива:\n");
string[] m1 = new string[x];
int i;
18
Console.Write("\t" + m1[i]);
Console.WriteLine();
}
Console.ReadKey();
}
}
}
using System;
19
Ввод такого текста несложен, а представим, что у нас большое приложение с
обширным набором типов и классов. Директива using позволяет компилятору найти
непонятный ему элемент, просматривая указанное пространство имен. В данной работе
директива using используется так:
using System;
…
namespace LWP01Console
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!\nИ по-русски можно? Конечно, привет мир!\
nThis is my first application using C#!");
Как видим, также как и в C и C++ язык унаследовал символ «;» (точка с
запятой) как символ разделения операторов, однако можно писать код и без этого
разделителя (частично).
20
using <псевдоним> = <класс>;
namespace LWP01Console
{
class Program
namespace Name01.Name02
{
class A { }
}
namespace Name03
{
using Name01.Name02;
class B : A { }
}
21
static void Main(string[] args)
//
Console.WriteLine("Пожалуйста, введите символы:\n");
String a;
a = Console.ReadLine();
Console.WriteLine("\n\tВы ввели символ: " + a + ".");
Console.ReadKey();
Console.Clear();
/* Текст комментария */
22
В остальном, здесь всё просто. Объявляем переменную «a» (строковая — String
или string), выполняем считывание символов с клавиатуры (ReadLine) и выводим
введённый символ в потоке с переводом строки. Конструкция «+ a +» служит для
подстановки переменной «a» в поток символов для вывода в окне консольного
приложения.
\n определяется как перевод курсора на следующую строку.
\t определяется как символ табуляции в окне консоли.
//
Console.WriteLine("Пожалуйста, введите число элементов массива:\n");
int x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("\nПожалуйста, введите элемент массива:\n");
string[] m1 = new string[x];
int i;
Немного поясним несколько простых правил при написании кода, так как
зачастую подобные ошибки наиболее часто встречаемы и как следствие наиболее
неприятны.
23
ПРИМЕЧАНИЕ № 2: Однако, не всегда точка с запятой обязана быть.
Например, конструкция без точек с запятой ниже, выдаст сообщение с тремя кнопками.
Вставим этот код, в наше консольное приложение предварительно добавив ссылку
System.Windows.Forms (Рис. 3. 4.):
Рис. 3. 5. Результат работы кода без точек с запятой (нужно нажать «Да»)
4. Публикация
24
Presentation). В частности вкладка свойств проекта LWP01Console Безопасность
выглядит так:
25
Рис. 4. 2. Публикация проекта
26
Рис. 4. 3. Мастер публикаций: Место публикации приложения
27
Рис. 4. 4. Мастер публикаций: Способ установки приложения пользователями
28
Рис. 4. 5. Мастер публикаций: Место, где приложение будет искать обновления
Последний шаг:
29
Рис. 4. 6. Готов к публикации
30
Запускаем Setup.exe:
31
Рис. 4. 10. Удаление установленного приложения (Панель управления ->
Программы и компоненты)
Содержание
1. Вводная часть
2. Создание простого приложения Windows Forms
3. Модификация приложения Windows Forms
4. Завершающая часть
5. О приложении к Лабораторной работе № 2
1. Вводная часть
32
создана для быстрой разработки приложений, в которых обширный графический
пользовательский интерфейс не является приоритетом. Для создания
пользовательского интерфейса используется конструктор Windows Forms, и
пользователь получает доступ к другим возможностям времени разработки и времени
выполнения, в число которых входят следующие:
Развёртывание ClickOnce.
Обширная поддержка баз данных, благодаря элементу управления
DataGridView (Windows Forms).
Панели инструментов и другие элементы пользовательского интерфейса,
которые могут иметь внешний вид и поведение Microsoft Windows XP/7, Microsoft
Office или Microsoft Internet Explorer.
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
33
Рис. 2. 2. Окно создания нового проекта
34
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
35
Рис. 2. 4. Исходный код приложения Windows Forms сформированного средой
разработки (файл Program.cs)
36
Рис. 2. 6. Запуск приложения Windows Forms по конфигурации Debug
37
Первое что необходимо отметить по сравнению с консольным приложением это
добавление новых инструментов в окне среды разработки. Теперь у нас есть
«визуальная форма» (Рис. 3. 1), на которой можно размещать любые доступные
элементы из специальной панели объектов которая называется Панель элементов (по
умолчанию находится слева сбоку на границе среды разработки):
38
Рис. 3. 2. Закреплённая панель элементов
39
Рис. 3. 3. Добавленное текстовое поле редактирования (сверху) и процесс добавления
(снизу)
40
Рис. 3. 5. Выбор свойств для элементов: пока у нас всего два элемента — сама форма
(с именем: Form1) и текстовое поле редактирования (с именем: textBox1)
41
Рис. 6. 2. Ошибочное задание поля (Name)
И так, для начала изменим свойства самой формы. Для этого перейдём в
свойства Form1.cs. Нам нужны следующие поля (информация о значении поля можно
получить на панели свойств ниже на тёмно сером поле):
42
ПРИМЕЧАНИЕ № 3: Для того, чтобы поменять имя файла нашей формы,
необходимо выполнить следующее: выделить в обозревателе решений значок формы (
В нашем случае нам нужна иконка. Выбираем Файл значка, вводим внизу его
Имя и жмём Добавить.
43
Рис. 3. 7. Создание нового типа изображения для значка
44
Рис. 3. 8. Создание типа изображения для значка приложения
Рисуем значок. Лучше всего сделать два значка (32х32, 24-разрядное и 16х16,
24-разрядное), чтобы отображался и маленький значок и более крупный (маленький к
примеру будет отображаться в шапке приложения, а также в режиме Маленькие
значки у Проводника Windows). Разумеется, старые версии значка необходимо
удалить (правая кнопка мыши по значку -> Удалить тип изображений).
Остался последний штрих. Нужно проассоциировать нашу программу форму со
значком. Для этого переходим в свойства формы, ищем пункт Icon и нажимаем на
«…». Ищем через Проводник нужный файл значка в корневом каталоге проекта и
нажимаем Открыть.
В итоге получим примерно следующее:
45
Рис. 3. 9. Выбор значка для приложения
46
TextAlign5: изменим с Right на Center
^ Изменим положение курсора и вывода текста в поле.
47
Рис. 3. 12. Готовый шаблон приложения Windows Forms
Кнопки цифр:
(Name): B0…B9 Text: 0…9
Кнопка «запятая»:
(Name): BD. Text: ,
Кнопка «очистить»:
(Name): BC Text: C
Кнопка «=»:
(Name): BResult Text: =
Кнопки действий:
(Name): BOperation1 Text: +
(Name): BOperation2 Text: -
(Name): BOperation3 Text: *
(Name): BOperation4 Text: /
Кнопка «Округлить»:
(Name): BSpecial Text: Округлить
Элемент NumericUpDown:
(Name): NumericSpecial Maximum: 5 Minimum: 0 Increment: 1
48
Рис. 3. 13. Переключение со страницы свойств кнопки на страницу событий
Нас интересует событие Click. Дважды нажимаем мышкой на слово Click (Рис. 3.
14):
49
}
if (Clear == true)
{
ResultBox.Clear();
Clear = false;
Dot = false;
}
Добавим по аналогии (с кнопкой «1» код для всех цифровых кнопок). Затем
добавим код для кнопки «=»:
if (Operation1 == true)
Result = A + B;
if (Operation2 == true)
Result = A - B;
if (Operation3 == true)
Result = A * B;
50
if (Operation4 == true)
Result = A / B;
ResultBox.Text = Result.ToString();
Operation1 = false;
Operation2 = false;
Operation3 = false;
Operation4 = false;
Clear = true;
}
Кнопка «очистить»:
Кнопка «вычитание»:
«Умножение» и «деление»:
51
private void BOperation4_Click(object sender, EventArgs e)
{
Operation1 = false;
Operation2 = false;
Operation3 = false;
Operation4 = true;
Dot = true;
ResultBox.Clear();
}
Кнопка для ввода дробных чисел (с точкой). Код этой кнопки такой:
if (Clear == true)
{
ResultBox.Clear();
Clear = false;
Dot = false;
}
if (Dot == false)
{
ResultBox.AppendText(",");
Dot = true;
}
}
else
{
if (Dot == true)
{
ResultBox.AppendText(",");
Dot = false;
}
}
}
Кнопка Округлить:
4. Завершающая часть
52
Рис. 4. 1. Модифицированное приложение Windows Forms
ПРИМЕЧАНИЕ № 6: Отметим ещё раз, что разницы между string и String нет. В
первом случае это обозначение переменной как ключевого слова (это alias для String).
Во-вторых мы имеем дело с общепринятым типом CLR для разных языков .NET.
namespace LWP02WindowsForms01
{
public partial class LWP02Main : Form
{
Boolean Operation1 = false;
Boolean Operation2 = false;
Boolean Operation3 = false;
Boolean Operation4 = false;
Boolean Clear = false;
Double A;
Double B;
Double Result;
Boolean Dot = false;
public LWP02Main()
{
InitializeComponent();
}
53
файлами. Каждый исходный файл содержит свою часть определения класса и все такие
части собираются во время компиляции.
Есть несколько ситуаций, когда удачно разбить определение класса на
несколько файлов:
if (Clear == true)
{
ResultBox.Clear();
Clear = false;
Dot = false;
}
Это метод нажатия кнопки «0». В самом начале во время события нажатия, идёт
проверка на переменную Clear. Если она true, происходит очистка текстового поля,
54
после чего состояние «очистки» отключается вместе с состоянием нажатия кнопки
«точка».
Дальше идёт проверка на нажатие кнопок математических операций. Если
кнопки не нажаты, то вводимое в поле число будет отправлено в переменную A. Точнее
при нажатии кнопки «0» в этом случае к тексту хранимому в данный момент в
ResultBox.Text добавляется ещё один нуль. После чего идёт проверка на символы «.»
(точка) и замена его символом «,» (запятая). В завершении вся строка конвертируется
из String в Double и полученное значение отправляется в переменную A. Если же
нажата кнопка какой-либо операции, все введённые цифры присваивается переменной
B.
if (Clear == true)
{
ResultBox.Clear();
Clear = false;
Dot = false;
}
if (Dot == false)
{
ResultBox.AppendText(",");
Dot = true;
}
}
else
{
if (Dot == true)
{
ResultBox.AppendText(",");
Dot = false;
}
}
}
55
Если же математическая операция уже выбрана, то Dot переведена в состояние
true и сработает последняя часть метода. Опять же нажать «точку» можно лишь один
раз.
56
/// <summary>
/// Требуется переменная конструктора.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Освободить все используемые ресурсы.
/// </summary>
/// <param name="disposing">истинно, если управляемый ресурс должен быть удален;
иначе ложно.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// Обязательный метод для поддержки конструктора - не изменяйте
/// содержимое данного метода при помощи редактора кода.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new
System.ComponentModel.ComponentResourceManager(typeof(LWP02Main));
this.ResultBox = new System.Windows.Forms.TextBox();
this.B1 = new System.Windows.Forms.Button();
…
#endregion
Как уже было сказано, этот код генерируется автоматически. По большей части
это создание экземпляров определённых ресурсов и изменение их свойств, такие как
позиция, имена, размеры и прочее. Это «конструктор» всех компонентов формы,
которые пользователь установил при помощи визуального редактора. Разумеется,
добавлять новый компоненты (кнопки, текстовые поля, DataGridView’ы и другое)
можно добавлять «руками», прописывая всё то что делает конструктор автоматически.
Зачастую, ручное добавлением элементов управления и настройку их можно увидеть в
«одно файловых» проектах (где код помещён в один файл *.cs). Единственное что
можно сказать о преимуществе визуального конструктора формы — это его наглядность
и несомненное удобство.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace LWP02WindowsForms01
{
57
static class Program
{
/// <summary>
/// Главная точка входа для приложения.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LWP02Main());
}
}
}
Application.Run(new LWP02Main());
Содержание
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms
4. Модификация приложения Windows Forms: элемент управления
MenuStrip
5. Модификация приложения Windows Forms: элемент управления Button
6. Модификация приложения Windows Forms: элемент управления ToolTip
7. Модификация приложения Windows Forms: элемент управления
ComboBox
8. Модификация приложения Windows Forms: элемент управления
WebBrowser
9. Модификация приложения Windows Forms: добавляем исходный код
58
10.Завершающая часть
11.О приложении к Лабораторной работе № 3
1. Вводная часть
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
59
Рис. 2. 2. Окно создания нового проекта
60
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
61
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
62
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
63
^ Поменяем размер формы.
64
Рис. 4. 1. Панель элементов: поиск элемента MenuStrip
65
Рис. 4. 3. Отображение добавления специального элемента управления под формой
Заполним меню. В поле с текстом Вводить здесь введём имя меню, в данном
случае Навигация. После нажатия клавиши Enter появятся новые поля для создания
других меню и пунктов меню. В поле, расположенном ниже, вводим: Домашняя
страница. Нажимаем клавишу Enter, после чего появятся дополнительные поля.
Введём: Назад. Нажмём Enter, введём: Вперёд. В итоге получим следующую картину:
При компиляции теперь у нас есть полноценное меню. При помощи инструмента
MenuStrip. Можно создавать сколько угодно большие вложенные меню (с выпадением
списка вниз и вправо).
Обратим внимание ещё вот на что. Если посмотреть поле (Name) свойства
элемента меню Домашняя страница, то оно будет следующим:
домашняяСтарницаToolStripMenuItem
Поэтому события, которые можно подставлять для этого элемента тоже будут «по
умолчанию» содержать символы кириллицы, ссылаясь на внутренне имя объекта.
Страшного ничего нет, так как это не вызовет ошибки. Однако, чтобы не смущать
наличием кириллицы в имени переменной, изменим поля (Name) для получившегося
меню. Вместо домашняяСтраница поставим Home, вместо назад: Back,. Вместо:
вперёд: Forward. Вместо навигацияToolStripMenuItem для первоначального
элемнта меню поставим: NavigateToolStripMenuItem.
66
Разместим справа в углу внутри меню элемент управления Button. Параметры
таковы:
(Name): GoButton
Text: Перейти
Получим следующее:
67
Рис. 6. 2. Новое поле для элемента управления GoButton: ToolTip на toolTip1
68
Рис. 7. 1. Добавленный элемент управления ComboBox
Параметры ComboBox:
(Name): AddressBox
69
перекрытия элементов управления MenuStrip, ComboBox и Button. Если изменение
размеров элемента управления WebBrowser затруднено, откроем его свойства, найдём
параметр Dock и убедимся, что ему задано значение none, после этого установим
желаемый размер. Задание параметру Anchor значения Top, Bottom, Left, Right
заставит элемент управления WebBrowser корректно изменять свой размер при
изменении размера окна приложения.
70
Для добавления события нажатия для кнопки, необходимо дважды кликнуть на
соответствующую кнопку, либо перейти в свойства кнопки и нажать на зачёк «молнии»
(События):
71
Оператор try-catch состоит из блока try, за которым следует одно или несколько
предложений catch, в которых определяются обработчики для различных исключений.
Как следует из вышесказанного у метода может быть несколько исключений. О том
какие это исключения, а также о том сколько их, можно узнать да-да, наведя курсор
мыши на используемый метод). При возникновении исключения среда CLR ищет
оператор catch, который обрабатывает это исключение. Если выполняющийся в данный
момент метод не содержит такого блока catch, то среда CLR рассматривает метод,
который вызвал текущий метод, и так далее по стеку вызовов (тоесть до самого
начального метода). Если блок catch не найден, то среда CLR отображает пользователю
сообщение о необработанном исключении и останавливает выполнение программы
(вызывая критическую ошибку и как следствие, потерю всех данных).
Блок try содержит защищаемый код, в котором могут происходить исключения.
Этот блок выполняется до момента возникновения исключения или до своего
успешного завершения.
Хотя предложение catch можно использовать без аргументов для перехвата
любого типа исключения, такой подход не рекомендуется. В общем случае следует
перехватывать только те исключения, устранение причин которых известно.
В нашем случае, у метода Uri() есть два исключения. Одно из них мы «поймали»
и обработали, выведя пользователю сообщение в MessageBox.Show. А поймали мы
пустую строку или неправильный формат URL-адреса. Само сообщение об ошибке,
сгенерированное методом можно увидеть в переменной Error.Message.
Кнопка Назад:
Кнопка Вперёд:
72
{
WebBrowser.GoForward();
}
Код события:
73
Рис. 9. 3. Событие Navigate для элемента управления WebBrowser
Код события:
74
Рис. 9. 4. Событие Key Down для элемента управления ComboBox
75
}
catch (UriFormatException)
{
return;
}
}
Для начала нам нужно пространство имён для работы с потоками данных между
приложением и файлами. Добавим строчку в самое начало файла LWP03Main.cs:
}
catch (UriFormatException)
{
return;
}
}
76
ArrayBox: переменная для массива значений ComboBox. FilePath: конечный
путь куда записывать файл и его название с расширением.
// Метод Navigate()
// Метод является альтернативой нажатия кнопки Перейти для наглядности
private void Navigate(String Address)
{
if (String.IsNullOrEmpty(Address)) return; // Если адрес пуст, завершаем
функцию
if (Address.Equals("about:blank")) return; // Если получаем адрес пустой
страницы, завершаем функцию (чтобы не делать лишнюю работу)
// Проверяем как передан адрес: если передаётся без http:// или https://
подставляем
if (!Address.StartsWith("http://") &&
!Address.StartsWith("https://"))
{
Address = "http://" + Address;
}
try
{
WebBrowser.Navigate(new Uri(Address)); // Выполняем переход по новому
сформированному адресу
SaveFile(FilePath, null, Address, false); // Вызываем метод записи в
текстовый файл
}
catch (UriFormatException)
{
return;
}
}
77
Рис. 10. 1. Модифицированное приложение Windows Forms
Рис. 10. 2. Содержание файла созданного приложением Windows Forms после перехода
по новому адресу в ComboBox
78
Варианты заданий: Варианты для выполнения самостоятельных заданий с
использованием материала данной работы приведены по ссылке в конце этого
материала (сслыка доступна в программном продукте).
Содержание
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms
4. Модификация приложения Windows Forms: добавление новой формы
5. Модификация приложения Windows Forms: динамическое связывание
параметров двух форм и передача параметра через прямой доступ к
элементу формы
6. Модификация приложения Windows Forms: динамическое связывание
параметров двух форм и передача параметра через передачу метода в
конструктор формы
7. Модификация приложения Windows Forms: динамическое связывание
параметров двух форм и передача параметра через класс делегата
8. Модификация приложения Windows Forms: динамическое связывание
параметров двух форм и передача параметра через свойства
9. Завершающая часть
10.О приложении к Лабораторной работе № 4
1. Вводная часть
79
10. Передача параметра из одной формы в другую. Применим четыре различных
способа (наиболее интересных).
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
80
Рис. 2. 2. Окно создания нового проекта
81
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
82
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
83
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
84
ПРИМЕЧАНИЕ № 2: Для того, чтобы поменять имя файла нашей формы,
необходимо выполнить следующее: выделить в обозревателе решений значёк формы (
(Name): Hint
85
Для добавления новой формы нам необходимо следующее: выделим правой
кнопкой мыши название нашего проекта в обозревателе решений (
Настроим её:
Теперь надо определиться, как именно быдет вызываться новая форма. Как
модальное окно, или как подчинённая форма (родительская форма будет контейнером
для обращение к дочерней форме). Наиболее интересен второй вариант, так как он
позволяет напрямую обращаться к элементам дочерней формы, а не ждать завершения
формы. Реализуем сначала этот способ.
Первое что необходимо сделать для вызова второй формы из первой это
объявить конструктор класса новой формы:
После добавляем:
86
LWP04Children dlg = new LWP04Children();
(Name): ButtonShowChildren
Text: Подчинённая форма
Size: 150; 23
ToolTip на Hint: Передача параметра через прямой доступ
к элементам управления
87
private void LWP04Children_FormClosing(object sender, FormClosingEventArgs e)
{
// Определяем кто закрывает приложение
if (e.CloseReason == CloseReason.UserClosing)
{
Hide();
e.Cancel = true;
}
}
Добавим для двух форм приложения два текстовых поля. Для формы LWP04Main
добавим TextBox ( ):
(Name): TextBoxMain
Modifiers: Public
88
Рис. 5. 1. Расстановка элементов на добавленной форме LWP04Children
(Name): TextBoxChildren
Modifiers: Public
(Name): ButtonClose
89
Text: Закрыть
Size: 75; 23
Единственны «минус» такого варианта работы с формами, это атрибут public для
поля Modifiers элементов управления. То есть этот вариант можно использовать, но
безопасность данных в элементе управления в этом случае ставится под сомнение
изначально. Потому что этот элемент и все его свойства, поля и данные становятся
«видимыми» из любого места приложения. Под «безопасностью» понимаем не взлом
приложения, а доступность данных внутри приложения и возможное нарушение
согласования типов при манипуляции с переменными. То есть нарушается принцип
инкапсуляции. Если приходится открывать то что должно приватным, значит что то не
правильно в архитектуре приложения.
90
Создаём новую форму LWP04ChildrenDelegate1 со следующими элементами:
(Name): LWP04ChildrenDelegate1
Text: Работа с окнами (C#) :: Подчинённая
форма для делегата № 1
Size: 500; 100
FormBorderStyle: Fixed Dialog
MaximizeBox: False
MinimizeBox: False
ShowInTaskbar: False
91
Рис. 6. 2. Свойства формы LWP04ChildrenDelegate1: значения поля свойства
FormBorderStyle
(Name): TextBoxChildrenDelegate1
92
}
(Name): ButtonClose
Text: Закрыть
Size: 75; 23
(Name): ButtonShowChildrenDelegate1
Text: Для делегата № 1
Size: 150; 23
ToolTip на Hint: Передача параметра через метод в
конструкторе
93
/* Создаём экземпляр класса формы LWP04ChildrenDelegate1, вызывает
конструктор
* Вместе с формой создаём экземпляр делегата с вызовом метода
TextBoxNewDelegate1() */
LWP04ChildrenDelegate1 D1 = new LWP04ChildrenDelegate1(new
NewDelegate1(TextBoxNewDelegate1));
D1.ShowDialog(); // Вызываем модальный диалог нашей формы
MessageBox.Show("Текст который был введён в форме:\n\n" + D1.Text1, D1.Text +
" :: Результат выполнения"); // Возвращаем с формы переменную Text1 И показываем её в
окошке MessageBox.Show
TextBoxMain.Text = D1.Text1; // Отправляем переменную в поле TextBoxMan
}
string TextBoxNewDelegate1()
{
// Метод возвращает значение TextBoxMain, нужен для работы делегата
return TextBoxMain.Text;
}
Application.Run(new LWP04Main());
}
}
Добавляем после:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace LWP04WindowsForms03
{
public partial class LWP04ChildrenDelegate1 : Form
{
private NewDelegate1 D1; // Объявляем экземпляр делегата NewDelegate1
public String Text1; // Объявляем переменную для передачи параметра обратно в
главную форму
// Меняем конструктор формы, чтобы он мог узнать какой метод ему был отправлен...
public LWP04ChildrenDelegate1(NewDelegate1 sender)
{
InitializeComponent();
D1 = sender; // ...и кем
}
94
private void LWP04ChildrenDelegate_Load(object sender, EventArgs e)
{
/* Вызываем созданный экземпляр класса NewDelegate1
* После вызова экземпляр вытаскивает из главной формы метод, а точнее
переменную TextBoxNewDelegate1
* Далее делегат становится переменной, которую мы отправляем в
TextBoxChildren этой формы
* Сама переменная делегата является возвращаемой строкой от TextBoxMain */
TextBoxChildrenDelegate1.Text = D1();
}
Рис. 6. 4. Статическое изменение значений текстовых полей двух форм (до открытия и
закрытия формы)
95
Рис. 7. 1. Расстановка элементов на добавленной форме LWP04ChildrenDelegate1
(Name): LWP04ChildrenDelegate1
Text: Работа с окнами (C#) :: Подчинённая
форма для делегата № 2
Size: 500; 100
FormBorderStyle: Fixed Dialog
MaximizeBox: False
MinimizeBox: False
ShowInTaskbar: False
(Name): TextBoxChildrenDelegate2
(Name): ButtonClose
Text: Закрыть
Size: 75; 23
96
Рис. 7. 2. Расстановка элементов на добавленной форме LWP04Main: кнопка Для
делегата № 2
(Name): ButtonShowChildrenDelegate2
Text: Для делегата № 2
Size: 150; 23
ToolTip на Hint: Передача параметра через класс делегата
string TextBoxNewDelegate2()
{
// Метод возвращает значение TextBoxMain, нужен для работы делегата
return TextBoxMain.Text;
}
Основной метод главной формы отредактируем так (ВАЖНО, чтобы весь код
находился после строчки (InitializeComponent();), так как здесь должен сработать
сначала конструктор, а затем всё остальное.
public LWP04Main()
{
InitializeComponent();
NewDelegate2Out.EventHandler = new
NewDelegate2Out.NewEventOut(TextBoxNewDelegate2Out);
}
97
// Метод отлавливающий изменения текстового поля является здесь основным
private void TextBoxChildrenDelegate2_TextChanged(object sender, EventArgs e)
{
/* Срабатывает делегат NewDelegate2Out при получения события изменений текста
этого поля
* и в метод (на главную форму) отправляется значение этого поля
* То есть изменение текста приводит к ВЫЗОВУ события EventHandler и отправку
через делегат данных текстового поля */
NewDelegate2Out.EventHandler(TextBoxChildrenDelegate2.Text);
}
/* Создаём делегата № 2 */
public static class NewDelegate2Out
{
public delegate void NewEventOut(String Data);
public static NewEventOut EventHandler;
}
// Делаем по аналогии
public delegate string NewDelegate2In();
98
универсального типа. Чтобы связать событие с методом, который будет обрабатывать
событие, и добавить в событие экземпляр делегата. Обработчик событий вызывается
при каждом происхождении этого события, пока делегат не будет удален.
Сначала идёт ключевое слово, затем возвращаем тип метода (void), и потом
имя делегата. В нашем случае, делегат не возвращает зачения и не получает
параметров, то есть это относится к любому методу этого делегата.
Мы видим, что в конструктор передалось имя целевого метода. Тип этого метода
должен быть обязательно void (раз мы решили так сделать в самом начале). И
конструктором создаётся экземпляр под именем NewDelegate.
Целевой метод:
Для запуска делегата вызываем объект нашего делегата (будет выполнен метод
MyMethod()):
NewDelegate();
99
Рис. 7. 3. Динамическое изменение значений текстового полей главной формы (во
время ввода данных в текстовом поле дочерней формы)
ComboBox ( ) и ListBox.
100
Рис. 7. 1. Расстановка элементов на форме LWP04Children
(Name): CB
Size: 225; 21
(Name): LB
Size: 225; 69
{
Close();
}
Добавим после:
Смысл тут ясен. Вызывается метод set если ComboText слева. И get если… да,
справа (операция получения значения). При нажатии на кнопку вызова формы, в
ComboBox добавляется значение (если значение которое надо добавить не пустое).
101
Теперь реализуем получение значения из ComboBox например при изменении
значения. Добавим на нашу главную форму ещё один ListBox с именем:
(Name): LBMain
И разберёмся с ListBox для дочерней формы. Код для формы такой (добавим
после ComboText):
102
Теперь при нажатии на кнопку Подчинённая форму, в ComboBox И ListBox будут
добавлять записи типа ComboBox: [<текст из поля главной формы>]. Выбираем
значение в ComboBox или ListBox, после чего дважды нажимаем на поле ListBox
главной формы и туда автоматически добавятся новые значения (выбранные курсором
в дочернем окне). При переполнения какого-либо элемента управления (более четырёх
записей), все значения удаляются переполненного из элемента.
Теперь если закрыть форму, вызванную кнопкой Для делегата № 1 через кнопку
Закрыть (не крестик в заголовке или Alt+F4), то данные дочеренего окна будут
уничтожены (после передачи в главную форму) и к ним не буде доступа до следующего
вызова формы. Это можно увидеть по всплывающему окошку, а точнее его
обрезанному заголовку после уничтожения формы:
9. Завершающая часть
103
Рис. 9. 1. Модифицированное приложение Windows Forms
Содержание
104
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms
4. Модификация приложения Windows Forms: динамическое добавление и
уничтожение элемента управления
5. Модификация приложения Windows Forms: стандартные диалоговые
окна
6. Модификация приложения Windows Forms: открытие файла, сохранение
файла и работа с текстом
7. Модификация приложения Windows Forms: прочее
8. Завершающая часть
9. О приложении к Лабораторной работе № 5
1. Вводная часть
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
105
Рис. 2. 1. Создание нового проекта
106
В поле Имя вводим LWP05WindowsForms04 — это название программы
(выбрано по названию лабораторного практикума, номеру и названию работы). В поле
Расположение указана конечная директория, где будет находиться весь проект.
Выберем расположение удобное для быстрого поиска. В поле Имя решения вводится
либо название программы «по умолчанию» из поля Имя автоматически, либо можно
ввести своё собственное. Под этим именем будет создана конечная папка проекта (если
Имя и Имя решения разные).
107
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
108
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
109
ПРИМЕЧАНИЕ № 2: Для того, чтобы поменять имя файла нашей формы,
необходимо выполнить следующее: выделить в обозревателе решений значок формы (
(Name): Hint
110
Расставим первую группу элементов. Наша цель, по нажатию кнопки, в
определённом месте получить новую (созданную) кнопку и добавить для неё событие
нажатия, которое выполнит изменение текста кнопки. Также добавим кнопку по
уничтожению этой добавленной кнопки. И, наконец, добавим кнопку, которая
автоматически добавит массив однотипных текстовых полей.
Здесь у нас есть такие элементы как MenuStrip (меню сверху), GroupBox
(рамка с текстом), кнопки Button, NumericUpDown (элемент «ползунка») и элемент
StatusStrip (отдельное поле внизу).
111
Рис. 4. 3. Панель элементов: StatusStrip
MenuStrip:
(Name): MainMenu
ToolTip на Hint: Меню
Первый главный пункт: Файл
Первый всплывающий пункт: вводим произвольное имя, далее
выделяем этот пункт меню, жмём по нему
правую кнопку мыши и выбираем
Преобразовать в -> Separator
Второй всплывающий пункт: Выход
112
Событие Click пункта меню Выход:
Добавим «горячую» клавишу. Для этого выделим элемент меню и войдём в его
свойства. Ищем там пункт ShortcutKeys. Выставляем галочку в Ctrl
(Модификаторы), и ищем в списке Клавиши нужную: E:
StatusStrip:
(Name): MainStatus
ToolTip на Hint: Строка состояния
StatusLabel:
113
(Name): StatusLabel
Text: <пусто>
PrgoressBar:
(Name): StatusProgressBar
Организуем вывод подсказки в строке состояния для кнопки Выход. Для этого
инициализируем событие MouseEnter (ожидание получения фокуса). Код события
модифицируем:
Timer:
(Name): TimerOneSecond
Interval: 1000
^ Интервал между событиями Elapsed в миллисекундах.
114
Единственное событие таймера инициализируем двойным нажатием на иконку
таймера в конструкторе форм (Tick) и модифицируем:
Button (Добавить):
(Name): button1
Text: Добавить
ToolTip на Hint: Добавить элемент управления
115
Получаем доступ к элементу управления по его имени, для того чтобы поменять
текст на добавленной кнопке по её нажатию. Добавляем следующий код после кода
события Click:
Кнопка Удалить:
Button (Удалить):
(Name): button2
Text: Удалить
ToolTip на Hint: Удалить элемент управления
Enabled: False
^ Выключаем кнопку изначально.
Кнопка Массив:
116
Button (Массив):
(Name): button3
Text: Массив
ToolTip на Hint: Добавить массив элементов управления
(число выбираем справа)
NumericUpDown:
(Name): NumericButtons
Maximum: 3
^ Максимальное число.
Mininum: 1
^Минимальное число.
Increment: 1
^Шаг.
117
Пример реализации поиска текста в однотипных элементах управления типа
TextBox (можно сделать отдельной кнопкой):
118
5. Модификация приложения Windows Forms: стандартные диалоговые окна
Кнопка Отмена:
(Name): button6
119
Text: Отмена.
ToolTip на Hint: Возвращает фон формы по умолчанию
Кнопка Отмена:
(Name): button7
Text: Отмена
ToolTip на Hint: Возвращает шрифт формы по умолчанию
NumericUpDown:
(Name): NumericAplha
Maximum: 100
^ Максимальное число.
Mininum: 1
^ Минимальное число.
Increment: 1
^ Шаг.
Timer:
(Name): TimerOneMinute
Enabled: True
^ Запускаем таймер при инициализации приложения.
120
ColorDialog:
(Name): ColorSelect
FontDialog:
(Name): FontSelect
FolderBrowserDialog:
(Name): SelectBrowser
121
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
GraphicsPath G = new GraphicsPath(); // Создаём новый графический элемент
Rectangle Rect = new Rectangle(0, 0, this.Width, this.Height); // Создаём
прямоугольник
public LWP05Main()
{
InitializeComponent();
/* Инициализируем массив элементов для checkedListBox1 */
String[] CheckItems = { "Непрозрачность окна в %", "Овальное окно" };
checkedListBox1.Items.AddRange(CheckItems);
/* Устанавливаем режим выбора элемента с двойного нажатия на одинарный */
checkedListBox1.CheckOnClick = true;
NumericAplha.Value = 100; // Начальное значение
progressBar1.Maximum = 60; // Граница индикаторы выполнения
}
122
using System.IO;
123
Рис. 5. 3. Окончательная работа блока стандартных диалоговых окон
(25% прозрачности на фоне рабочего стола, овальность и диалог выбора шрифта)
124
файлов, а также добавим контекстное меню по щелчку правой кнопки мыши на любой
пустой позиции формы.
125
Рис. 6. 1. Редактор коллекции элементов (для MouseRight)
126
Рис. 6. 2. Ошибочное задание поля (Name)
(Name): button10
Text: Цвет
ToolTip на Hint: Цвет выделенного текста
(Name): button11
Text: Шрифт
ToolTip на Hint: Шрифт выделенного текста
(Name): button14
Text: Фон
ToolTip на Hint: Фон выделенного текста
(Name): button15
Text: Копировать
ToolTip на Hint: Копировать выделенный текст в буфер
127
обмена
(Name): button13
Text: Выделить всё
(Name): button12
Text: Очистить
128
private void button13_Click(object sender, EventArgs e)
{
richTextBox1.Focus(); // Фокус на элемент
richTextBox1.SelectAll(); // Выделяем весь текст
}
129
{
SaveQuestion(); // Вызов метода обработки "правильного" закрытия предложения
с выводом диалога сохранения данных
}
void SaveQuestion()
{
DialogResult Result = MessageBox.Show("Сохранить изменения в документе?",
Title + " :: Сохранение изменений", MessageBoxButtons.YesNo);
public LWP05Main()
{
InitializeComponent();
/* Инициализируем массив элементов для checkedListBox1 */
String[] CheckItems = { "Непрозрачность окна в %", "Овальное окно" };
checkedListBox1.Items.AddRange(CheckItems);
/* Устанавливаем режим выбора 'ktvtynf с двойного нажатия на одинарный */
checkedListBox1.CheckOnClick = true;
NumericAplha.Value = 100; // Начальное значение
progressBar1.Maximum = 60; // Граница индикаторы выполнения
Title = this.Text; // Сохраняем первоначальный заголовок
}
130
{
SaveQuestion();
}
Расставим последнюю группу элементов. Пусть у нас есть элемент для работы со
вкладками. При выборе одной вкладки у нас есть всего одна кнопка. Нажав на неё,
открываем диалог выбора файла, выбираем файл изображения, и часть этого
изображения вставляется в диалоговое окно (в определённое место). Вторая вкладка
131
содержит один флаг, нажатие на который отображает два элемента RadioButton.
Выбор одного из них высвечивает определённый элемент. Один из них: текстового
поля с форматированием по маске, а второй: выбор даты и времени по календарю.
132
аналогично ComboBox и подобным элементам. Через TabPages переходим в Редактор
коллекции TabPages:
Расположение CheckBox:
133
Параметры для этой кнопки:
(Name): button16
Text: Выбрать изображение
Элемент MasketTextBox:
134
(Name): masketTextBox1
PromptChar: *
Visible: False
Элемент DateTimePicker:
(Name): dateTimePicker1
Visible: False
(Name): radioButton1
Text: Специальное поле
Visible: False
(Name): radioButton2
Text: Дата и время
Visible: False
135
Рис. 7. 3. Все возможные маски ввода
136
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked == true)
{
radioButton1.Visible = true;
radioButton2.Visible = true;
}
else
{
radioButton1.Visible = false;
radioButton2.Visible = false;
maskedTextBox1.Visible = false;
dateTimePicker1.Visible = false;
}
}
137
150
^ Поменяем размер формы.
FormBorderStyle изменим с Sizable на FixedDialog
^ Сделаем окно «неизменяем» по размерам.
Основу часов должен составлять всё тот же простой таймер (Timer). Но, к
сожалению, точность таймера ОС оставляет желать лучшего. Сообщения таймера,
создающие события Tick, проходят через очередь приложения. К тому же, другие
приложения могут блокировать на некоторое время работу Вашего приложения.
Поэтому события таймера возникают в общем случае нерегулярно. Кроме того,
несмотря на возможность указания интервалов времени в миллисекундах, реальная
дискретность таймера определяется периодом прерываний, посылаемых таймером.
(Name): Clock
Interval: 1000
138
По умолчанию таймер создается в заблокированном состоянии. Чтобы его
разблокировать, необходимо записать значение true в свойство Enabled. Сделаем это в
коде инициализации формы. В файле LWP04Clock.cs найдём:
public LWP05Clock()
{
InitializeComponent();
}
public LWP05Clock()
{
InitializeComponent();
Clock.Enabled = true;
}
(Name): Now
ReadOnly: True
(Name): button1
Text: Старт
(Name): Button2
Text: Стоп
Найти его можно просто раскрыв список Все формы Windows Forms панели
элементов.
TrackBar:
(Name): Seconds
139
Maximum: 60
Minimum: 1
140
Рис. 7. 5. Расстановка элементов последней формы LWP05Clock
public LWP05Clock()
{
InitializeComponent();
Clock.Enabled = true;
label1.Text = "";
}
if (Clock.Enabled == true)
{
button1.FlatStyle = FlatStyle.Standard;
button1.BackColor = Color.LawnGreen;
}
label1.Text = "";
Seconds.Value = dt.Second;
}
141
button2.BackColor = Color.IndianRed;
}
8. Завершающая часть
142
Рис. 8. 2. Модифицированное приложение Windows Forms : результат работы
переключателей на фоне уже выбранного изображения
143
Рис. 8. 3. Модифицированное приложение Windows Forms: результат работы формы
Простые часы (ползунок двигается с каждой секундой при работающих часах)
Содержание
10.Вводная часть
11.Создание приложения Windows Forms
12.Модификация приложения Windows Forms: eDrawings 2012
13.Модификация приложения Windows Forms: организация работы с
SolidWorks через приложение
144
14.Завершающая часть
15.О приложении к Лабораторной работе № 6
1. Вводная часть
145
расширения. Эти возможности могут использоваться конечными пользователями в
рамках управляемых расширений.
Visual Studio Tools for Applications был объявлен Microsoft с выпуском Visual
Studio 2005. Первый Community Technology Preview (CTP) из Visual Studio для
приложений был выпущен в апреле 2006 года. Он входит в состав Office 2007 для
использования конечными пользователями и разработчиками бизнес-приложений, и
SDK доступна отдельно для независимых поставщиков ПО.
Текущей версией является Visual Studio Tools for Applications 2.0. Вторая версия
Visual Studio Tools for Applications включает в себя такие функции, как динамическое
программирование модели и поддержка WPF, WCF, WF, LINQ и NET 3.5.
Независимые поставщики программных продуктов желающих интегрировать
Visual Studio Tools for Applications в свои приложения должны платить за лицензию
Microsoft.
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
146
Рис. 2. 1. Создание нового проекта
147
В поле Имя вводим LWP06SW01 — это название программы (выбрано по
названию лабораторного практикума, номеру и названию работы). В поле
Расположение указана конечная директория, где будет находиться весь проект.
Выберем расположение удобное для быстрого поиска. В поле Имя решения вводится
либо название программы «по умолчанию» из поля Имя автоматически, либо можно
ввести своё собственное. Под этим именем будет создана конечная папка проекта (если
Имя и Имя решения разные).
148
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
149
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
150
ПРИМЕЧАНИЕ № 2: Для того, чтобы поменять имя файла нашей формы,
необходимо выполнить следующее: выделить в обозревателе решений значок формы (
(Name): Hint
(Name): eDButton
151
Text: Выбрать деталь, чертёж или сборку
Size: 200; 23
152
Рис. 3. 3. Начальная расстановка элементов
(Name): eDView
ToolTip на Hint: Деталь, чертёж или сборка
153
Рис. 3. 4. Работа COM-компонента eDrawings 2012 Control
(Name): eDPath
ReadOnly: True
(Name): eDFind
FileName: Деталь, чертёж или сборка
154
}
Теперь, попытаемся сделать вот что. Допустим нам нужно, чтобы наше
приложение запускалось отдельно, но при необходимости оно могло открывать
SolidWorks и что-то делать (например, строить элемент по размерам из приложения).
Такой способ работы противоположен концепции использования макросов после
запуска SolidWorks и гораздо медленнее (необходимость запускать SolidWorks пусть
даже и в фоновом режиме). Однако бывают ситуации, когда такой подход необходим.
155
Рис. 4. 1. Добавить ссылку: выбор нужных компонентов для добавления
Теперь нужно, чтобы наше приложение выполняло запуск лишь в том случае,
если не запущено других экземпляров SolidWorks. Сделаем проверку на загрузку
процесса SLDWORKS.exe в коде. Добавим в самом низу формы три кнопки: Запустить
SolidWorks и Послать команду.
156
Size: 300; 23
Послать команду, выгрузить (Button):
(Name): BSend
Text: Послать команду, выгрузить
Size: 300; 23
Фоновый режим (запуск, действие, выгрузка) (Button):
(Name): BHide
Text: Фоновый режим (запуск, действие,
выгрузка)
Size: 300; 23
Впишем ещё одну ссылку для работы со списком запущенных процессов в самое
начало файла LWP06Main.cs:
using System.Diagnostics;
using SldWorks;
using SwConst;
157
// Выгрузка фонового процесса не произойдёт из-за некорректного
объявления экземпляра класса;
// это было допущено сознательно для демонстрации возможностей работы
открытого приложения и самого Solidworks.
// Правильная работа ExitApp() будет реализована так:
// некая_функция() {
// SldWorks.SldWorks swApp = new SldWorks.SldWorks();
// /* действия */
// swApp.ExitApp();
// swApp = null;
// }
swApp.ExitApp();
/* Выгрузим процесс */
foreach (Process PSW in Process.GetProcessesByName(SW))
PSW.Kill();
}
catch { MessageBox.Show("Невозможно послать команду экземпляру SolidWorks
2012: экземпляр приложения не найден!", "Работа с SolidWorks (C#) :: Отсылка команды в
SolidWorks"); }
}
Смысл его в том, чтобы при наведении мышки на любое пустое место
приложения запускалась проверка на запущенный процесс SolidWorks. И если процесс
выгружен, то происходит разблокировка двух кнопок.
Впишем:
String SW = "SLDWORKS";
Process[] PList;
SldWorks.SldWorks swApp;
public LWP06Main()
{
InitializeComponent();
BStart.Enabled = false;
158
BHide.Enabled = false;
}
159
Копировать необходимо архив без распаковки. Также стоит проверить пути,
которые использует VSTA для работы с SolidWorks. Для этого выполним в VSTA: Tools -
> Options, нажать на Project and Solutions.
Главные пути:
160
Рис. 4. 4. Настройка SolidWorks для работы с VSTA
if (swAppHide == null)
{ MessageBox.Show("Невозможно запустить экземпляр SolidWorks 2012!", "Работа
с SolidWorks (C#) :: Запуск SolidWorks"); }
else
{
161
BStart.Enabled = false;
BHide.Enabled = false;
}
swAppHide.Visible = false;
swAppHide.NewPart();
// Начало макроса
ModelDoc2 swDoc = null;
PartDoc swPart = null;
DrawingDoc swDrawing = null;
AssemblyDoc swAssembly = null;
bool boolstatus = false;
int longstatus = 0;
int longwarnings = 0;
swDoc = ((ModelDoc2)(swAppHide.ActiveDoc)); // В макросе было использовано
swApp
swDoc = ((ModelDoc2)(swAppHide.ActiveDoc)); // В макросе было использовано
swApp
boolstatus = swDoc.Extension.SelectByID2("Спереди", "PLANE", 0, 0, 0, true,
0, null, 0);
RefPlane myRefPlane = null;
myRefPlane = ((RefPlane)(swDoc.FeatureManager.InsertRefPlane(8, 0.01, 0, 0,
0, 0)));
swDoc.ClearSelection2(true);
swDoc.ClearSelection2(true);
Array vSkLines = null;
vSkLines = ((Array)(swDoc.SketchManager.CreateCornerRectangle(0, 0, 0,
0.070797287636592099, 0.045512542052094929, 0)));
swDoc.ClearSelection2(true);
SketchSegment skSegment = null;
skSegment = ((SketchSegment)(swDoc.SketchManager.CreateCircle(0.035399,
0.022756, 0.000000, 0.048625, 0.029175, 0.000000)));
swDoc.ClearSelection2(true);
swDoc.SketchManager.InsertSketch(true);
boolstatus = swDoc.Extension.SelectByID2("Line2@Эскиз1", "EXTSKETCHSEGMENT",
0, 0.017504823866190358, 0, false, 0, null, 0);
swDoc.ShowNamedView2("*Триметрия", 8);
swDoc.ClearSelection2(true);
boolstatus = swDoc.Extension.SelectByID2("Line2@Эскиз1", "EXTSKETCHSEGMENT",
0, 0.017504823866190358, 0, false, 0, null, 0);
Feature myFeature = null;
myFeature = ((Feature)(swDoc.FeatureManager.FeatureExtrusion2(true, false,
false, 0, 0, 0.020000000000000004, 0.01, false, false, false, false,
0.017453292519943334, 0.017453292519943334, false, false, false, false, true, true, true,
0, 0, false)));
swDoc.ISelectionManager.EnableContourSelection = false;
swDoc.SetPickMode();
swDoc.ClearSelection2(true);
// Конец макроса
swDoc.SaveAsSilent("D:\\LWP06SW01-Деталь.sldprt", false);
swAppHide.ExitApp();
swAppHide = null;
MessageBox.Show("Деталь отрисована и сохранена на диск, работа SolidWorks
2012успещно завершена!", "Работа с SolidWorks (C#) :: Фоновый режим");
}
162
Макрос выше, сначала на виде Спереди вставляет справочную плоскость
(расстояние от вида Спереди: 10 мм). Затем рисует Эскиз (который находится
непосредственно на виде Спереди). Эскиз состоит из двух фигур: прямоугольника и
окружности. Затем происходит вытягивание на 20 мм фигуры заключённой между
внешнем прямоугольником и внутренней окружностью. Поясним код, который отвечает
за размеры фигур:
Здесь по порядку:
1. Выбор вида Спереди.
2. Задание экземпляра объекта Плоскость.
3. Отрисовка плоскости, где 8 это параметр первого ограничения (первая
привязка), а второй (0.01) и есть отступ в 10 мм по первой привязке (вид Спереди).\
4. Дальше идёт сбор выделения.
Это построение прямоугольника (эскиз как массив линий). Строит четыре линии
от начальной точки (три первых нуля), до конечной. В качестве начальной выбрана
Исходная точка. Конечная точка это:
double X2 (ось X): ~70.7 мм.
double Y2 (ось Y): ~45.5 мм.
double Z2: 0.
163
создание экземпляра объекта для вытягивания и собственно последняя строчка
выполняет вытягивание в одну сторону (Направление 1) на 20 мм (0.02000...4).
Итак. «Читать» код SolidWorks достаточно просто. К тому же, при наведении
курсора на функции, сам VSTA также как и Visual Studio 2010 высвечивает подсказки
Достаточно скудные подсказки: лишь тип функций, их аргументы и тип аргументов,
однако в названии, как функций, так и аргументов уже заложено определение того,
для чего всё это можно использовать. Проще всего изучать API на многочисленных
примерах как в справочной системе SolidWorks (на локальной машине с установленным
SolidWorks), так и на официальном сайте (www.solidworks.com). Также можно
записывать макросы с различными действиями.
5. Завершающая часть
Рис. 5. 1. Работа кнопки Фоновый режим: на просмотр было открыто то что SolidWorks
собрал (деталь) и сохранил на диск D (LWP06SW01-Деталь.sldprt)
164
Рис. 5. 2. Работа кнопки Послать команду, выгрузить: окно SolidWorks с пустой деталью
и всплывающее окошко
Содержание
16.Вводная часть
17.Создание приложения SwCSharpAddin
18.Модификация приложения SwCSharpAddin: макрос отрисовки болта
19.Модификация приложения SwCSharpAddin: построение через шаблон
детали
20.Завершающая часть
21.О приложении к Лабораторной работе № 6
165
1. Вводная часть
166
Рис. 1. 1. Сервиc –> Параметры...: Размещения пользовательских шаблонов
проектов
167
Рис. 2. 1. Расположение исполняемого файла devenv.exe
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
168
Рис. 2. 3. Окно создания нового проекта
169
Рис. 2. 4. Вводим данные нового проекта приложений SwCSharpAddin
170
Рис. 2. 5. Обозреватель решений: состав проекта приложения SwCSharpAddin
сформированного средой разработки
171
Рис. 2. 7. Создание новой детали для проверки работы добавления
172
Нажмём на C# Addin и сразу же перейдём на эту панель, в результате увидим
следующее:
173
Рис. 2. 8. Запуск приложения SwCSharpAddin по конфигурации Debug: демонстрация
всех возможностей
Разбирать весь код добавления не имеет смысла. Код достаточно прост для
понимания и внесения изменений. Весь проект изначально состоит из четырёх
основных файлов кода *.cs, главных из которых два.
1. UserPMPage.cs отвечает за панель пользовательских элементов управления
Solidworks (на рис. 2. 8 слева, панель Sample PMP). Этот файл отвечает за
оформление панели (функция AddControls() реализует инициализацию конструктора).
Разумеется, таких панелей может быть много. Событийные инструменты (функции для
работы с пользовательскими элементами и событиями от них) такой панели описаны в
PMPHandler.cs. Для каждой панели может быть своя событийная модель и свой
обработчик событий.
2. SwAddin.cs отвечает за построение всего добавления. По сути является
главным файлов с которым придётся работать большую часть времени. Содержит
инициализацию всего меню и всех функций, которое это меню реализует. И файл
который является фундаментом для всего добавления это: EventHandling.cs.
Реализует событийную модель (обработку событий) для всего добавления.
Заменим на:
174
Найдём:
Заменим:
public UserPMPage2(SwAddin addin)
Заменяем на:
Заменяем на:
Находим:
Заменяем на:
Находим:
Заменяем на:
175
Находим:
Заменяем на:
Находим:
Заменяем на:
Находим:
cmdIDs[0] = cmdGroup.get_CommandID(cmdIndex0);
TextType[0] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[1] = cmdGroup.get_CommandID(cmdIndex1);
TextType[1] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[2] = cmdGroup.ToolbarId;
TextType[2] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal |
(int)swCommandTabButtonFlyoutStyle_e.swCommandTabButton_ActionFlyout;
Заменяем на:
cmdIDs[0] = cmdGroup.get_CommandID(cmdIndex0);
176
TextType[0] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[1] = cmdGroup.get_CommandID(cmdIndex1);
TextType[1] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[2] = cmdGroup.ToolbarId;
TextType[2] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal |
(int)swCommandTabButtonFlyoutStyle_e.swCommandTabButton_ActionFlyout;
cmdIDs[3] = cmdGroup.get_CommandID(cmdIndex2);
TextType[3] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
Находим:
Добавляем после:
// Клонируем панель элементов PMP
public Boolean AddPMP2()
{
ppage2 = new UserPMPage2(this);
return true;
}
Находим:
Добавляем после:
177
public int EnablePMP2()
{
if (iSwApp.ActiveDoc != null)
return 1;
else
return 0;
}
178
В ToolbarLarge.bmp нарисуем:
В ToolbarSmall.bmp нарисуем:
Добавим после:
Найдём:
Изменим на:
Найдём:
179
int menuToolbarOption = (int)(swCommandItemType_e.swMenuItem |
swCommandItemType_e.swToolbarItem);
cmdIndex0 = cmdGroup.AddCommandItem2("CreateCube", -1, "Create a cube",
"Create cube", 0, "CreateCube", "", mainItemID1, menuToolbarOption);
cmdIndex1 = cmdGroup.AddCommandItem2("Show PMP", -1, "Display sample property
manager", "Show PMP", 1, "ShowPMP", "EnablePMP", mainItemID2, menuToolbarOption);
// Клонируем панель элементов PMP
cmdIndex2 = cmdGroup.AddCommandItem2("Показать ещё одну PMP", -1, "Открыть
ещё одну страницу с элементами", "Показать ещё одну PMP", 2, "ShowPMP2", "EnablePMP2",
mainItemID3, menuToolbarOption);
Добавим после:
Найдём:
Изменим на:
Найдём:
cmdIDs[0] = cmdGroup.get_CommandID(cmdIndex0);
TextType[0] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[1] = cmdGroup.get_CommandID(cmdIndex1);
TextType[1] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[2] = cmdGroup.ToolbarId;
TextType[2] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal |
(int)swCommandTabButtonFlyoutStyle_e.swCommandTabButton_ActionFlyout;
cmdIDs[3] = cmdGroup.get_CommandID(cmdIndex2);
TextType[3] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
Изменим на:
180
int[] TextType = new int[6];
cmdIDs[0] = cmdGroup.get_CommandID(cmdIndex0);
TextType[0] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[1] = cmdGroup.get_CommandID(cmdIndex1);
TextType[1] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[2] = cmdGroup.ToolbarId;
TextType[2] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal |
(int)swCommandTabButtonFlyoutStyle_e.swCommandTabButton_ActionFlyout;
cmdIDs[3] = cmdGroup.get_CommandID(cmdIndex2);
TextType[3] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[4] = cmdGroup.get_CommandID(cmdIndex3);
TextType[4] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
cmdIDs[5] = cmdGroup.get_CommandID(cmdIndex4);
TextType[5] =
(int)swCommandTabButtonTextDisplay_e.swCommandTabButton_TextHorizontal;
Найдём:
Добавим до:
// Спираль: часть 1
double C = 1 / 1000.0;
double Height, Pitch, Radius;
181
Height = Macro.H * C;
Pitch = Macro.P * C;
Radius = Macro.R * C;
Macro.Dispose();
// Спираль: часть 2
182
skSegment = ((SketchSegment)(swDoc.SketchManager.CreateLine(x + dx, y +
dy, z, x, y, z)));
swDoc.ClearSelection2(true);
//boolstatus = swDoc.Extension.SelectByID2("", "SKETCHSEGMENT", x+dx/2.0,
y+dy, z, false, 0, null, 0);
//swDoc.SketchAddConstraints("sgHORIZONTAL2D"); // Больше не нужно
//swDoc.ClearSelection2(true);
//swDoc.ViewRotateminusx(); // Тоже не нужно
swDoc.SetPickMode();
boolstatus = swDoc.Extension.SelectByID2("Точка1", "SKETCHPOINT", x, y,
z, false, 0, null, 0);
boolstatus = swDoc.Extension.SelectByID2("Спираль1", "REFERENCECURVES",
0, 0, 0, true, 1, null, 0);
swDoc.SketchAddConstraints("sgATPIERCE");
swDoc.ClearSelection2(true);
boolstatus = swDoc.Extension.SelectByID2("Точка1", "SKETCHPOINT", x, y,
z, false, 0, null, 0);
swDoc.SketchConstraintsDel(0, "sgCOINCIDENT"); // Автоматически
"совпадающие"
swDoc.SketchManager.InsertSketch(true);
// Спираль: часть 3
// Вытягивание
double Height2 = Height + Radius / 4.0 + dx * 1.5;
boolstatus = swDoc.Extension.SelectByID2("Эскиз1", "SKETCH", 0, 0, 0,
false, 0, null, 0);
183
myFeature = ((Feature)(swDoc.FeatureManager.FeatureExtrusion2(true,
false, false, 0, 0, Height2, 0.00254, false, false, false, false, 0.01745329251994,
0.01745329251994, false, false, false, false, true, true, true, 0, 0, false)));
swDoc.ISelectionManager.EnableContourSelection = false;
swDoc.ClearSelection2(true);
// Шляпка болта
double extrHeight = Radius + c / 4;
boolstatus = swDoc.Extension.SelectByID2("", "FACE", 0, Height2, 0,
false, 0, null, 0);
swDoc.SketchManager.InsertSketch(true);
//skSegment = ((SketchSegment)
(swDoc.SketchManager.CreateCircleByRadius(0, 0, 0, Radius*1.5))); // Созданём полигон (6
граней) вместо окружности
Array vSkLines = null;
vSkLines = ((Array)(swDoc.SketchManager.CreatePolygon(0, 0, 0, -Radius *
2, 0, 0, 6, true)));
swDoc.SketchManager.InsertSketch(true);
swDoc.ClearSelection2(true);
boolstatus = swDoc.Extension.SelectByID2("Эскиз3", "SKETCH", 0, Height2,
0, false, 0, null, 0);
myFeature = ((Feature)(swDoc.FeatureManager.FeatureExtrusion2(true,
false, false, 0, 0, extrHeight, 0.00254, false, false, false, false, 0.01745329251994,
0.01745329251994, false, false, false, false, true, true, true, 0, 0, false)));
swDoc.ISelectionManager.EnableContourSelection = false;
swDoc.ClearSelection2(true);
// Кромки
boolstatus = swDoc.Extension.SelectByID2("", "FACE", 0, Height2 +
extrHeight, 0, false, 0, null, 0);
swDoc.ViewRotateplusx();
swDoc.ViewRotateplusx();
swDoc.ViewRotateplusx();
swDoc.ViewRotateplusx();
boolstatus = swDoc.Extension.SelectByID2("", "FACE", Radius * 1.5,
Height2, 0, true, 1, null, 0);
//boolstatus = swDoc.Extension.SelectByID2("", "FACE", 0, 0, 0, true, 2,
null, 0);
Array radiiArray3 = null;
double[] radiis3 = new double[1];
Array setBackArray3 = null;
double[] setBacks3 = new double[0];
Array pointArray3 = null;
double[] points3 = new double[0];
radiiArray3 = radiis3;
setBackArray3 = setBacks3;
pointArray3 = points3;
184
myFeature = ((Feature)(swDoc.FeatureManager.FeatureFillet(195, Radius *
2.0 / 25.0, 0, 0, radiiArray3, setBackArray3, pointArray3)));
swDoc.ClearSelection2(true);
boolstatus = swDoc.Extension.SelectByID2("", "FACE", 0, 0, 0, false, 0,
null, 0);
myFeature = ((Feature)(swDoc.FeatureManager.FeatureFillet(195, Radius *
2.0 / 25.0, 0, 0, radiiArray3, setBackArray3, pointArray3)));
(Name): Hint
(Name): TBHeight
185
Text: 75
ToolTip на Hint: Высота спирали
(Name): TBRadius
Text: 10
ToolTip на Hint: Радиус стержня
(Name): TBPitch
Text: 3
ToolTip на Hint: Шаг спирали
Добавим над каждым TextBox слева направо по элементу Label. Свойства Text
каждого текстового элемента будут соответственно H, R и P.
(Name): B_OK
Text: Размер выбран
Size: 100; 23
Код:
public double H, R, P;
186
Компилируем приложение (Debug) и запускаем. Загружаем новую деталь (или
непосредственно панель инструментов) и нажимаем на кнопку в нашем добавлении с
буквой «М». Результат работы показан ниже (Рис. 3. 3):
187
каждым годом претерпевает существенные изменения и многое, что было написано для
старых версий SolidWorks уже не запускается на новых, то есть требуется
вмешательство и устранение проблем совместимости.
Вывод напрашивается сам. Эффективность макросов резко падает в тех случаях,
когда деталей в модели становится очень много. Здесь на передний план выходит даже
не сама запись и объём кода, а тот объём работы что потребуется затратить на
привидение макроса к редактируемому виду (расстановка переменных в ключевых
местах, чистка от лишних записей). Да и к тому же, код сложнее читать. А когда перед
тобой пара тысяч строк...
Итак. Пускай теперь у нас есть шаблон модели шестигранного болта с резьбой с
расставленными привязками и сформированными зависимостями размеров друг от
друга. В шаблоне выделено три ключевых размера. Назовём его
Bolt_Default_Template.sldprt. Шаблон выглядит так:
188
Рис. 4. 1. Заготовка шаблона для добавления SwCSharpAddin
189
Рис. 4. 2. Модифицированная форма LWP07Temp
(Name): Hint
Немного поясним, что будет делать наша форма для работы с шаблоном. На
форме будет присутствовать две дополнительные кнопки. Одна кнопка будет выбирать
шаблон на диск, вторая кнопка будет выбирать путь и имя, по которому будет сохранён
болт на основе изменённого по размерам шаблона. В остальном, форма также будет
обеспечивать ввод трёх размеров: высота болта до шляпки, радиус стержня и шаг
спирали (между витками).
OpenFileDialog:
(Name): OFD_Template
FileName: Bolt_Default_Template.sldprt
SaveFileDialog:
(Name): SFD_Template
FileName: Bolt_Final.sldprt
190
Button:
(Name): B_Template
Text: Выбор файла шаблона
TextBox:
(Name): TB_Template
ReadOnly: True
Button:
(Name): B_Final
Text: Выбор файла для сохранения
TextBox:
(Name): TB_Final
ReadOnly: True
TextBox:
(Name): TB_H
ToolTip на Hint: Высота болта
TextBox:
(Name): TB_R
ToolTip на Hint: Радиус стержня
TextBox:
(Name): TB_C
ToolTip на Hint: Шаг спирали
Button:
(Name): B_OK
Text: Размер выбран
Добавим после:
191
Найдём:
public LWP07Temp()
{
InitializeComponent();
Добавим после:
// Инициализируем переменные при старте формы
P_Template = "D:\\Bolt_Default_Template.sldprt";
P_Final = "D:\\Bolt_Final.sldprt";
B1 = false;
B2 = false;
TB_Template.Text = P_Template;
TB_Final.Text = P_Final;
TB_H.Text = "100";
TB_R.Text = "10";
TB_P.Text = "3";
192
MessageBox.Show("Ошибка ввода размеров: нужно ввести все неотрицательные
числа в поля формы.\n\nРазмеры по умолчанию:\nH: 100 мм.\nR: 10 мм.\nP: 3 мм.",
"Использование SwCSharpAddin (C#) :: Ввод размеров болта для шаблона");
}
}
5. Завершающая часть
193
Нажимаем на кнопку с буквой «Ш»:
194
Рис. 5. 1. Работа кнопки Создать болт (шаблон): на просмотр было открыто то что
SolidWorks собрал (шестигранный болт на основе шаблона) и сохранил на диск по
указанному в форме пути и с именем заданным пользователем (Bolt_Final.sldprt)
Содержание
1. Вводная часть
2. Создание приложения Windows Foundation Presentation
3. Модификация приложения Windows Foundation Presentation
4. Модификация приложения Windows Foundation Presentation:
добавление нового элемента из библиотеки компонентов WPF
5. Модификация приложения Windows Forms: расширение
функциональности приложения и работа с оформление
195
6. Модификация приложения Windows Foundation Presentation: различные
возможности WPF
7. Модификация приложения Windows Foundation Presentation: немного о
стилях и шаблонах
8. Завершающая часть
9. О приложении к Лабораторной работе № 8
1. Вводная часть
«Гзамл», «ви-эф-пи». Эти слова идут неразрывно. Оба слова обозначают одну
достаточно интересную технологию, которой можно найти великое множество полезных
применений... Эта технология, можно сказать ― прямой конкурент Windows Forms.
элементы управления;
двухмерную (2D) и трёхмерную (3D) графику;
анимацию в приложении;
шаблоны и стили;
работа с мультимедиа и оформлением;
привязка данных;
язык eXtensible Application Markup Language (далее XAML).
196
Набор свойств, методов и событий объекта позволяет объединить веб-документы в
связанное приложение. Этот набор свойств и описывается при помощи XAML.
Также XAML используется в Windows Workflow Foundation (WF) и Silverlight.
При помощи XAML в WFP можно определять элементы пользовательского интерфейса,
привязку данных, поддержку событий и прочее. В WF XAML определяет
последовательности выполняемых действия (workflows).
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
197
Рис. 2. 2. Окно создания нового проекта
198
Рис. 2. 3. Вводим данные нового проекта приложения WPF
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace LWP08WPF01
{
/// <summary>
/// Логика взаимодействия для App.xaml
/// </summary>
public partial class App : Application
{
}
}
Но, что же это? В файле отсутствует точка входа: публичный и статичный метод
Main(). Don’t panic! Приложение (объект Application) вызывается конструкцией файла
App.xaml:
<Application x:Class="LWP08WPF01.App"
199
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LWP08WPF01
{
/// <summary>
/// Логика взаимодействия для MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
<Window x:Class="LWP08WPF01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
Сама разметка, как уже было рассказано в вводной части данной лабораторной
работы очень напоминает другой формат для разметки текстовых файлов, а именно
небезызвестный расширяемый язык разметки XML. Собственно XAML основан на XML. И
это неудивительно. Ведь XML при ближайшем рассмотрении очень удобный язык
разметки текстовых файлов (например весьма эффективен для организации баз данных
типа «поле-значение» без необходимости использования того же SQL).
200
Рис. 2. 4. Обозреватель решений: состав проекта приложения WPF сформированного
средой разработки
201
Рис. 2. 5. Макет формы MainWindows.xaml: отображение конструктора формы (сверху)
и представление этой формы в качестве разметки XAML (внизу)
202
Рис. 2. 6. Запуск приложения WPF по конфигурации Debug
Пока что у нас есть пустое приложение и всего один элемент: сетка (Grid).
Исправим это.
203
Поменяем заголовок нашей формы, иконку, сделаем окно «неизменяемым» по
размерам. Выделим окно MainWindow в конструкторе (двойное нажатие мышки по
MainWindow.xaml в обозревателе решений). Окно свойств формы находится справа
внизу окна среды разработки:
Первое что бросается в глаза: у нашего окна нет имени. Исправим это так:
выдели серое поле <без имени> и введём имя Main:
204
Установка иконки тоже занятие простое: ищем поле Icon, нажимаем на .
Откроется окно:
205
Рис. 3. 3. Добавленное изображение
206
Рис. 3. 4. Модифицированная форма приложения
Обратим, что слева в верхнем углу конструктора для формы находится элемент
«лупы». Увеличивает или уменьшает отображения формы, если например нужно чёткое
позиционирование элемента (по пикселям):
<Window x:Class="LWP08WPF01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Простое приложение WPF (C#)" mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="350"
Width="525" WindowStyle="SingleBorderWindow" ToolTip="Главное окно" ResizeMode="NoResize"
Name="Main" ForceCursor="False" Icon="/LWP08WPF01;component/Images/LWP08WPF01.ico">
<Grid></Grid>
</Window>
207
4. Модификация приложения Windows Foundation Presentation: добавление
нового элемента из библиотеки компонентов WPF
208
Рис. 4. 2. Окно Выбор элементов панели элементов
209
Рис. 4. 4. Выравнивание элемента по левому верхнему углу
210
класса со странным именем Thickness, который используется при задании свойства
Margin в коде программной части.
MinHeight: впишем 100
^ Минимальная высота элемента (попробуйте уменьшить размер элемента
меньше этого значения в конструкторе мышкой, среда не даст этого сделать).
Background: LightYellow
^ Выбор цвета может также зависеть от числа, например: #[Aplha|Red|Green|
Blue] является маской для ввода цвета с альфа-каналом прозрачности или:
#FFFFFFE0 для светло-жёлтого.
Обратим также внимание, что нашему элементу InkCanvas было дано имя
inkCanvas1.
211
Рис. 4. 5. Добавление кнопки Очистить
Теперь вернём нашей форме свободу и изменим для неё значения для
свободного изменения размера1:
ResizeMode: CanResize
Добавить событие можно разными способами. Наиболее простой (для Click) это
дваждый нажать на кнопку в конструкторе формы. Также можно перейти на вкладку
события для кнопки Button, найти там Click и дваждый щёлкнуть по надписи:
212
Также можно добавить событие вручную, редактированием кода XAML для
формы:
213
Рис. 4. 6. Модифицированное приложение WPF
214
5. Модификация приложения Windows Forms: расширение функциональности
приложения и работа с оформление
<Window x:Class="LWP08WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Простое приложение WPF (C#)" mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="350"
Width="525" WindowStyle="SingleBorderWindow" ToolTip="Главное окно"
ResizeMode="CanResizeWithGrip" Name="Main" ForceCursor="False"
Icon="/LWP08WPF;component/Images/LWP08WPF.ico">
<Grid>
<InkCanvas HorizontalAlignment="Stretch" Margin="12,12,12,199" Name="inkCanvas1"
VerticalAlignment="Stretch" MinHeight="100" EditingMode="Ink" Background="LightYellow" />
<Button Content="Очистить" HorizontalAlignment="Right" Margin="0,0,12,170"
Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click" Height="23" />
</Grid>
<Window.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFD08E8E" Offset="0" />
<GradientStop Color="#FF881E1E" Offset="0.99" />
</LinearGradientBrush>
</Window.Background>
</Window>
215
Рис. 5. 2. Градиент для фона окна приложения
216
Рис. 6. 1. Новые элементы в окне формы MainWindow.xaml
217
Content: Всё
Теперь добавим две новые формы (в качестве окна Window). Для этого выделим
правой кнопкой мыши название проекта в обозревателе решений ( ),
далее выполним Добавить -> Создать элемент… (Ctrl+Shift+A). Выберем Окно
(WPF), введём Имя: Special.xaml:
Теперь заполним окна элементами. Для этого можно просто вставить код XAML
для определённого окна. Код для окна Special.xaml:
<Window x:Class="LWP08WPF01.Special"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
218
Title="Простое приложение WPF (C#) :: Специальное окно" Height="480" Width="480"
Name="SpecialWindow" Icon="/LWP08WPF;component/Images/LWP08WPF01.ico">
<DockPanel Width="Auto" Height="Auto" LastChildFill="True">
<!--Верхняя область меню-->
<Menu Width="Auto" Height="20" Background="#FFA9D1F4" DockPanel.Dock="Top">
<!--Меню Файл -->
<MenuItem Header="Файл">
<MenuItem Header="Сохранить как..." />
<Separator/>
<MenuItem Header="Выход" />
</MenuItem>
<!-- Меню Помощь -->
<MenuItem Header="Помощь">
<MenuItem Header="О программе" />
</MenuItem>
</Menu>
<!-- Нижняя область строки состояния объявляется до средней области (чтобы
заполнить весь низ строкой состояния)
что не удалось бы сделать при наличии зафиксированной слева панели -->
<StackPanel Width="Auto" Height="31" Background="#FFCAC5C5"
Orientation="Horizontal" DockPanel.Dock="Bottom">
<Label Width="155" Height="23" Content="Здесь находится строка состояния"
FontFamily="Arial" FontSize="10" />
</StackPanel>
<!-- Левая область основного содержимого -->
<StackPanel Width="136" Height="Auto" Background="White">
<Button Width="Auto" Height="26" Content="Кнопка № 1" Margin="5,5,5,5" />
<Button Width="126" Height="26" Content="Кнопка № 2" Margin="5,5,5,5" />
<Button Width="126" Height="26" Content="Кнопка № 2" Margin="5,5,5,5" />
</StackPanel>
<!-- Правая область основного содержимого. Обратим внимания, что элемент Grid —
последний дочерний элемент, поэтому он занимает всё оставшееся место -->
<Grid Width="Auto" Height="Auto" Background="#FFCC9393">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Рисуем квадраты -->
<Rectangle Fill="LightCyan" Margin="10,10,10,10" Grid.Row="0" Grid.Column="0"
/>
<Rectangle Fill="LightCyan" Margin="10,10,10,10" Grid.Row="0" Grid.Column="1"
/>
<Rectangle Fill="LightCyan" Margin="10,10,10,10" Grid.Row="1" Grid.Column="0"
/>
<Rectangle Fill="LightCyan" Margin="10,10,10,10" Grid.Row="1" Grid.Column="1"
/>
</Grid>
</DockPanel>
</Window>
219
Рис. 6. 3. Конструктор окна Special.xaml
<Window x:Class="LWP08WPF01.NoEventsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Приложение WPF (C#) :: Работа с командами без событий" Height="179"
Width="500"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" Name="SimpleEditor"
Icon="/LWP08WPF;component/Images/LWP08WPF01.ico">
<StackPanel Orientation="Vertical" Width="auto">
<StackPanel Orientation="Horizontal" Background="Gainsboro" Margin="10"
Height="40">
<Button Command="Cut" CommandTarget="{Binding ElementName=textBox1}"
Margin="5,5,5,5" Content ="Вырезать"/>
<Button Command="Copy" CommandTarget="{Binding ElementName=textBox1}"
Margin="5,5,5,5" Content="Копировать"/>
<Button Command="Paste" CommandTarget="{Binding ElementName=textBox1}"
Margin="5,5,5,5" Content="Вставить"/>
<Button Command="Undo" CommandTarget="{Binding ElementName=textBox1}"
Margin="5,5,5,5" Content="Откат"/>
<Button Command="Redo" CommandTarget="{Binding ElementName=textBox1}"
Margin="5,5,5,5" Content="Вернуть"/>
</StackPanel>
<TextBlock HorizontalAlignment="Left" Margin="5,5,5,5" Text="Введите текст,
попробуйте воспользоваться командами. Выделите текст, посмотрите какие кнопки (команды)
станут активными" TextWrapping="Wrap" Height="Auto" Width="Auto" />
220
<TextBox x:Name="textBox1" Margin="5,5,5,5" MaxLines="60" Height="23" Width="470"
Background="#FFF9EBA9" VerticalContentAlignment="Bottom" />
</StackPanel>
</Window>
).
Canvas.Left
Canvas.Right
Canvas.Top
Canvas.Bottom
221
Нажатие кнопки (для radioButton1: Canvas) будет выполнять создание этого
элемента через код C#. XAML-представление (аналог) кода будет таким:
<Window x:Class="LWP08WPF01.CanvasSpecial"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Приложение WPF (C#) :: Canvas" Width="640" Height="480">
<Canvas Margin="0,0,0,0" Background="White">
<Rectangle Fill="Red"
Stroke="Red"
Width="145"
Height="126"
Canvas.Left="124" Canvas.Top="122"/>
<Ellipse Fill="Blue"
Stroke="Blue"
Width="121" Height="100"
Panel.ZIndex="1"
Canvas.Left="195" Canvas.Top="191"/>
</Canvas>
</Window>
<Window x:Class="LWP08WPF01.StackPanelSpecial"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Приложение WPF (C#) :: StackPanel" Width="640" Height="480">
<StackPanel Margin="0,0,0,0" Background="White" Orientation="Vertical">
<Button Content="Кнопка сверху"/>
<Button Content="Кнопка снизу"/>
</StackPanel>
</Window>
<Window x:Class="LWP08WPF01.WrapPanelSpecial"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Приложение WPF (C#) :: WrapPanel" Width="640" Height="480">
<WrapPanel Margin="0,0,0,0" Background="White">
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
<Rectangle Margin="10,10,10,10" Fill ="Blue" Width="60" Height="60"/>
</WrapPanel>
</Window>
222
По сути, с помощью DockPanel (или двух таких элементов) можно реализовать
основной макет большинства современных приложений. Можно закрепить строку меню
сверху, затем левую и правую области основного содержимого, а строку состояния
снизу. И всё это благодаря паре свойств элемента управления DockPanel. Как правило,
закрепление любого дочернего элемента в элементе DockPanel управляется следующим
присоединенным свойством зависимости:
DockPanel.Dock
Этому свойству можно присвоить значения Left, Right, Top или Bottom. Есть ещё
одно полезное свойство (обычное свойство CLR) элемента управления DockPanel,
называемое LastChildFill. Если этому свойству присвоено значение true, последний
добавленный дочерний элемент будет занимать всё оставшееся свободное
пространство. Оно переопределяет свойство DockPanel.Dock, которое может быть уже
задано дочерним элементом.
<Window x:Class="LWP08WPF01.DockPanelSpecial"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Приложение WPF (C#) :: DockPanel" Width="640" Height="480">
<DockPanel Width="Auto" Height="Auto" LastChildFill="True">
<Rectangle Fill="CornflowerBlue" Stroke="CornflowerBlue" Height="20"
DockPanel.Dock="Top"/>
<Rectangle Fill="Orange" Stroke="Orange" />
</DockPanel>
</Window>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
Grid.Column
Grid.Row
223
Grid.ColumnSpan
Grid.RowSpan
<Window x:Class="LWP08WPF01.GridSpecial"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Приложение WPF (C#) :: Grid" Width="640" Height="480">
<Grid Width="Auto" Height="Auto" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Aqua" Grid.Column="0" Grid.Row="0"/>
<Rectangle Fill="Plum" Grid.Column="1" Grid.ColumnSpan="2"/>
</Grid>
</Window>
224
поместить выделенный текст в буфер обмена. В графическом объекте можно вырезать
выделенное изображение. Однако для вызова команды в обоих классах может
использоваться один и тот же источник команды, например объект KeyGesture или
кнопка на панели инструментов. В платформах .NET3.0/3.5/4.0 предусмотрено
множество готовых команд для выполнения типичных задач.
if (radioButton1.IsChecked == true)
{
Canvas Canv = new Canvas(); // Создаём экземпляр объекта Canvas
// Добавить элемент Canvas в качестве единственного дочернего элемента
Window
this.Content = Canv;
Canv.Margin = new Thickness(0, 0, 0, 0); // Создаём поле расстояния до
ближайшего элемента. Так как ближайшего элемента нет, Canvas заполняет всю форму
Canv.Background = new SolidColorBrush(Colors.White); // Задаём фон
// Прямоугольник
Rectangle r = new Rectangle();
r.Fill = new SolidColorBrush(Colors.Blue); // Заливка прямоугольник
r.Stroke = new SolidColorBrush(Colors.Red); // Граница прямоугольника
r.Width = 145; // Ширина прямоугольника
r.Height = 126; // Высота прямоугольника
r.SetValue(Canvas.LeftProperty, (double)124); // Устанавливаем левую
верхнюю границу прямоугольника
r.SetValue(Canvas.TopProperty, (double)122); // Устанавливаем верхнюю
границу первого элемента
Canv.Children.Add(r); // Добавляем прямоугольник в качестве дочернего для
Canvas
// Эллипс
Ellipse el = new Ellipse();
el.Fill = new SolidColorBrush(Colors.Green);
225
el.Stroke = new SolidColorBrush(Colors.Green);
el.Width = 121;
el.Height = 100;
el.SetValue(Canvas.ZIndexProperty, 1);
el.SetValue(Canvas.LeftProperty, (double)195);
el.SetValue(Canvas.TopProperty, (double)191);
Canv.Children.Add(el);
// Кнопка Вернуть оформление
Button button3 = new Button();
button3.Content = "Вернуть оформление";
button3.Width = 131;
button3.Height = 23;
button3.HorizontalAlignment = HorizontalAlignment.Right;
button3.VerticalAlignment = VerticalAlignment.Bottom;
button3.Margin = new Thickness(12, 12, 12, 12); // Кнопка будет
распологаться в левом верхнем углу
button3.Click += new RoutedEventHandler(button3_Click); // Переопределяем
события для кнопки
Canv.Children.Add(button3);
}
if (radioButton2.IsChecked == true)
{
StackPanel SP = new StackPanel();
// Добавить элемент StackPanel в качестве единственного дочернего
элемента Window
this.Content = SP;
SP.Margin = new Thickness(0, 0, 0, 0);
SP.Background = new SolidColorBrush(Colors.White);
SP.Orientation = Orientation.Vertical;
// Кнопка 1
Button b1 = new Button();
b1.Content = "Кнопка сверху";
SP.Children.Add(b1);
// Кнопка 2
Button b2 = new Button();
b2.Content = "Кнопка снизу";
SP.Children.Add(b2);
// Кнопка Вернуть оформление
Button button4 = new Button();
button4.Content = "Вернуть оформление";
button4.Width = 131;
button4.Height = 23;
button4.HorizontalAlignment = HorizontalAlignment.Right;
button4.VerticalAlignment = VerticalAlignment.Bottom;
button4.Margin = new Thickness(12, 12, 12, 12); // Кнопка будет
располагаться справа вверху под "Кнопка внизу"
button4.Click += new RoutedEventHandler(button3_Click);
SP.Children.Add(button4);
}
if (radioButton3.IsChecked == true)
{
WrapPanel WP = new WrapPanel();
// Добавить элемент WrapPanel в качестве единственного дочернего элемента
Window
this.Content = WP;
WP.Margin = new Thickness(0, 0, 0, 0);
WP.Background = new SolidColorBrush(Colors.White);
// Добавить прямоугольники (создание одинаковых объектов)
Rectangle r;
226
r.Fill = new SolidColorBrush(Colors.Blue);
r.Margin = new Thickness(10, 10, 10, 10);
r.Width = 60;
r.Height = 60;
WP.Children.Add(r);
}
// Кнопка Вернуть оформление
Button button5 = new Button();
button5.Content = "Вернуть оформление";
button5.Width = 131;
button5.Height = 23;
button5.HorizontalAlignment = HorizontalAlignment.Right;
button5.VerticalAlignment = VerticalAlignment.Bottom;
button5.Margin = new Thickness(12, 12, 12, 12); // Кнопка будет
рсполагаться слева вверху под всеми элементами
button5.Click += new RoutedEventHandler(button3_Click);
WP.Children.Add(button5);
}
if (radioButton4.IsChecked == true)
{
DockPanel DP = new DockPanel();
DP.LastChildFill = true;
// Это эквивалентно Width = "Auto" в XAML, кроме элементов GridColumn
Width/Height и GridRow Width/Height
DP.Width = Double.NaN;
DP.Height = Double.NaN;
// Добавить элемент WrapPanel в качестве единственного дочернего элемента
Window
this.Content = DP;
// Добавить прямоугольник (верхний)
Rectangle rTop = new Rectangle();
rTop.Fill = new SolidColorBrush(Colors.CornflowerBlue);
rTop.Stroke = new SolidColorBrush(Colors.CornflowerBlue);
rTop.Height = 20;
DP.Children.Add(rTop);
// Добавить прямоугольник (нижний)
rTop.SetValue(DockPanel.DockProperty, Dock.Top);
Rectangle rFill = new Rectangle();
rFill.Fill = new SolidColorBrush(Colors.Orange);
rFill.Stroke = new SolidColorBrush(Colors.Orange);
rFill.Height = 20;
DP.Children.Add(rFill);
rFill.SetValue(DockPanel.DockProperty, Dock.Bottom);
// Кнопка Вернуть оформление
Button button6 = new Button();
button6.Content = "Вернуть оформление";
button6.Width = 131;
button6.Height = 23;
button6.HorizontalAlignment = HorizontalAlignment.Right;
button6.VerticalAlignment = VerticalAlignment.Bottom;
button6.Margin = new Thickness(12, 12, 12, 12); // Кнопка будет
располагаться между верхним и нижним прямоугольником
button6.Click += new RoutedEventHandler(button3_Click);
DP.Children.Add(button6);
}
if (radioButton5.IsChecked == true)
{
Special Window = new Special();
Window.Show();
}
}
227
Теперь организуем событие нажатия виртуальной кнопки Вернуть
оформление:
public MainWindow()
{
InitializeComponent();
Default = this.Content; // Объекту Default отдаём текущий набор элементов
оформления
}
<Style x:Key="Style1">
...
</Style>
228
<Style x:Key="Style2" BasedOn="{StaticResource Style1}">
...
</Style>
<Window x:Class="LWP08WPF01.ButtonStylesWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LWP08WPF01"
229
Title="Приложение WPF (C#) :: Изменённые кнопки" SizeToContent="WidthAndHeight"
Name="lol">
<Window.Resources>
<Style TargetType="Button" x:Key="BaseButtonStyle">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="10" />
</Style>
<Grid Margin="30">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
230
<Button Style="{local:MultiStyle SmallButtonStyle GreenButtonStyle
BoldButtonStyle}" Content="М., зелёная, жирная" Grid.Row="3" />
<Button Style="{local:MultiStyle SmallButtonStyle RedButtonStyle
BoldButtonStyle}" Content="М., красная, жирная" Grid.Row="3" Grid.Column="1"/>
</Grid>
</Window>
Добавим два следующих файла с кодом (добавляем как Код -> Класс). Имя
первого файла: ButtonStylesExtension.cs (расширение стилей кнопок). Содержимое
файла делаем таким (комментарии включены):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows;
namespace LWP08WPF01
{
[MarkupExtensionReturnType(typeof(Style))] // Сообщаем что класс Style может
возвращать расширение разметки (typeof используется для получение объекта типа, является
альтернативой создания экземпляра класса new и получение типа методом GetType() )
public class MultiStyleExtension : MarkupExtension // Создаём класс как базовый
класс для расширенной разметки XAML
{
private string[] resourceKeys;
/// <summary>
/// Публичный конструктор.
/// </summary>
/// <param name="inputResourceKeys">Конструктор ввода должен быть строкой,
состоящей из одного или нескольких имен стилей, разделенных пробелами.</param>
public MultiStyleExtension(string inputResourceKeys) // получаем входящий ключ на
ресурс
{
if (inputResourceKeys == null) // Если ничего не получаем, генерируем (throw)
новое (new) исключение ArgumentNullException (пустой аргумент) для ключа
{
throw new ArgumentNullException("inputResourceKeys");
}
this.resourceKeys = inputResourceKeys.Split(new char[] { ' ' },
StringSplitOptions.RemoveEmptyEntries); // Выполняем слияние строк для ключа, удаляя
лишни символы пробелов
/// <summary>
/// Возвращает стиль, который объединяет все стили с ключами, указанными в
конструкторе.
/// </summary>
/// <param name="serviceProvider">Поставщиков услуг для данного расширения
разметки.</param>
/// <returns>Стиль, который объединяет все стили с ключами, указанными в
конструкторе.</returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
Style resultStyle = new Style(); // Создаём объект класса Style
231
try
{
foreach (string currentResourceKey in resourceKeys) // Пробегаем в цикле
по всем ключам ресорусов (foreach повторяет группу вложенных операторов для каждого
элемента массива или коллекции объектов)
{
Style currentStyle = new
StaticResourceExtension(currentResourceKey).ProvideValue(serviceProvider) as Style; //
Передаём через конструктор исходный ключ конкретного текущего стиля
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace LWP08WPF01
{
public static class StyleExtensionMethods
{
/// <summary>
/// Слияние двух стилей передается в качестве параметров. Первый стиль будет
изменен, чтобы получить всю информацию, присутствующую во втором.
/// Если имеет место совпадение стилей, второй стиль имеет больший приоритет.
/// </summary>
/// <param name="style1">Первый стиль для слияния, который будет изменен, чтобы
получить информацию из второго.</param>
/// <param name="style2">Второй стиль для слияния, который передают информацию
для первого стиля.</param>
public static void Merge(this Style style1, Style style2)
{
if (style1 == null) { throw new ArgumentNullException("style1"); }
if (style2 == null) { throw new ArgumentNullException("style2"); }
232
{
Merge(style1, style2.BasedOn); // Сливаем базовый стиль style2.VaseOn в
style1
}
233
Откроется список всех доступных стилей для этой кнопки. Выбор конкретного
стиля осуществляется например для кнокпи Большая, зелёная так. Объявляем
базовый стиль от кнопки Button, даём ему имя BaseButtonStyle:
Далее создаём на основе кнопки базового стиль стиль «большой» кнопки и даём
стилю имя BigButtonStyle:
Как видим, такую кнопку можно создать и без всех этих стилей, просто задав
необходимые свойства. Однако шаблонизация стилей хороша когда меняется не один
два параметра для элемента, а ОЧЕНЬ много и кардинально. И на настройку каждой
кнопки просто уйдёт много времени, тогда как можно воспользоваться заранее
приготовленным шаблоном.
234
Обработчик кнопки вызова последнего окна такой:
8. Завершающая часть
235
Рис. 8. 2. Работа окна с изменёнными кнопками с новыми стилями
236
Рис. 8. 4. Работа кнопки Сменить оформление в режиме Всё (справа), изменённый
размер окна и Canvas (слева), изменённый размер окна
Содержание
1. Вводная часть
2. Создание приложения Windows Foundation Presentation
3. Модификация приложения Windows Foundation Presentation
4. Модификация приложения Windows Foundation Presentation: работа с
решением в Expression Blend
5. Завершающая часть
6. О приложении к Лабораторной работе № 9
1. Вводная часть
237
примеру Windows Forms). И разумеется создать такое приложение на WPF только в
среде разработки Visual Studio и при этом применить уникальный стиль можно, но для
этого нужно хорошо знать XAML в его первозданном виде. Целью этого практикума не
ставится ознакомить читателя с XAML на уровне кода, потому можно пойти другими
путями к конечной цели.
Стандартные инструменты редактирования элементов управления и
формирования разметки для среды разработки хороши, но не более. Специально для
иных, более расширенных оформлений и дизайнов создан другой мощнейший
инструмент: Microsoft Expression Blend (текущая версия 4).
238
Как показано на рисунке выше, пакет Expresson Studio доступен бесплатно. В
пакет включены четыре отдельных приложения (Web, blend, Desing, Encoder), а также
набор документации. Другие компоненты кроме Blend на не интересуют (опять же цель
данного практикума не ознакомить читателя с работой иных сторонних продуктов, но
основы работы в Expression Blend будут изложены в кратком виде).
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
239
Рис. 2. 2. Окно создания нового проекта
240
Рис. 2. 3. Вводим данные нового проекта приложения WPF
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace LWP08WPF02
{
/// <summary>
/// Логика взаимодействия для App.xaml
/// </summary>
public partial class App : Application
{
}
}
<Application x:Class="LWP08WPF01.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
241
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LWP08WPF01
{
/// <summary>
/// Логика взаимодействия для MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
<Window x:Class="LWP08WPF01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
242
Рис. 2. 4. Обозреватель решений: состав проекта приложения WPF сформированного
средой разработки
243
Рис. 2. 5. Макет формы MainWindows.xaml: отображение конструктора формы (сверху)
и представление этой формы в качестве рамзетки XAML (внизу)
Начнём сразу с нового класса. Добавим новый класс в проект. Правая кнопка
244
Рис. 3. 1. Добавление нового элемента – LWP09WPF02
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LWP09WPF02
{
class Presentation
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
245
private bool currentTimer = false;
public int CurrentIndex // Свойство для получения текущего выбранного индекса для
слайда или установки нового индекса и генерации PropertyChanged
{
get { return this.currentIndex; }
set
{
if (this.currentIndex != value)
{
this.currentIndex = value;
this.OnPropertyChanged("CurrentSlide");
this.OnPropertyChanged("CanGoBack");
this.OnPropertyChanged("CanGoNext");
}
}
}
if (this.CanGoBack)
{
this.CurrentIndex--;
}
}
if (this.CanGoNext)
{
this.CurrentIndex++;
}
}
246
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
if (this.currentTimer == true)
{
this.currentTimer = false;
}
else
{
this.currentTimer = true;
}
}
}
}
}
Класс есть, теперь нужны слайды с содержимым, события и макет для того чтобы
эти слайды выводить.
Добавим три слайда (потом ещё один, четвёртый в Expression Blend). Для этого
нам нужно выбрать в окне добавления нового элемента (Установленные шаблоны ->
WPF) выберем элемент Страница (WPF). Для первой страницы Имя укажем как
01Title.xaml:
247
Рис. 3. 2. Добавление новой страницы: Страница (WPF), тип Page
<Page x:Class="LWP09WPF02._01Title"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="_01Title">
<Grid>
</Grid>
</Page>
Код добавленных страниц трогать мы не будем (это просто не нужно). Всё будет
выполнять главное окно и наш класс. Поэтом добавление новых страниц слайда
максимально упрощено и превращается в работу по заполнению их содержимым (как в
PowerPoint). Естественно, чтобы добавить для страницы уникальную функциональность,
код придётся редактировать. Но лучше всю функциональность организовывать в уже
готовом классе, тогда будет максимально просто добавлять новые функции для новых
слайдов не заботясь о правильном перенесении кода. Для слайдов же останется лишь
добавлять обработчики новых методов и расставлять новые элементы с привязкой.
248
<Window x:Class="LWP09WPF02.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:LWP09WPF02="clr-namespace:LWP09WPF02"
Title="Презентация WPF (C#)" Height="600" Width="800" ResizeMode="NoResize"
WindowState="Maximized" WindowStyle="None" Name="Window" KeyDown="Window_KeyDown"
PreviewMouseMove="Window_PreviewMouseMove"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown">
<Window.Resources>
<LWP09WPF02:Presentation x:Key="presentation"/>
</Window.Resources>
<Grid>
<Viewbox Margin="10,20,10,40" Stretch="Uniform">
<StackPanel>
<!-- Подставляем всегда: заголовок страницы -->
<TextBlock VerticalAlignment="Top" Height="84" FontFamily="Calibri"
FontSize="65" FontWeight="Bold" Text="{Binding Content.Title, ElementName=Frame,
Mode=Default}" TextAlignment="Center" TextWrapping="Wrap">
</TextBlock>
<!-- Подставляем всегда: содержимое слайда -->
<Frame Width="1000" Height="600" Source="{Binding CurrentSlide,
Source={StaticResource presentation}}" x:Name="Frame" NavigationUIVisibility="Hidden"
Background="{x:Null}" Focusable="False"/>
</StackPanel>
</Viewbox>
<LWP09WPF02:Presentation x:Key="presentation"/>
249
Stretch: Uniform
TextBox:
Text="{Binding Content.Title, ElementName=Frame, Mode=Default}"
Frame:
Source="{Binding CurrentSlide, Source={StaticResource presentation}}"
Кнопка Вперёд:
IsEnabled="{Binding CanGoNext, Source={StaticResource presentation}}"
public MainWindow()
{
InitializeComponent();
NewPresentation = (Presentation)this.FindResource("presentation");
AutoClick = false;
250
// После перезапуска приложения, стартует слайд, дата изменения которого
наиболее поздняя (дата изменения страницы XAML)
int IndexLastWritten = 0;
DateTime latestDateTimeWritten = DateTime.MinValue;
if (DateLastWritten.CompareTo(latestDateTimeWritten) > 0)
{
latestDateTimeWritten = DateLastWritten;
IndexLastWritten = i;
}
}
NewPresentation.CurrentIndex = IndexLastWritten;
//presentation.CurrentIndex = 0; // Если нам нужно, можем всегда стартовать с
первой страницы
Метод для показа курсора мышки ShowCursor() после того, как её скрыли
(вызов метода находится в ином месте: в методе нажатия левой клавиши мышки).
После вызова, снова запускает Timer1 отвечающий за событие скрытия курсора.
251
private void Window_PreviewMouseLeftButtonDown(object sender,
MouseButtonEventArgs e)
{
ShowCursor();
Point CurrentPoint = e.GetPosition(this);
TranslateTransform Transform = new TranslateTransform(CurrentPoint.X,
CurrentPoint.Y);
//this.ClickCanvas.RenderTransform = Transform;
}
if (AutoClick == false)
{
AutoClick = true;
NewPresentation.GoAuto = true;
Timer2.Start();
}
else
{
AutoClick = false;
NewPresentation.GoAuto = false;
Timer2.Stop();
}
if (!NewPresentation.GoAuto)
{
LAuto.Visibility = Visibility.Hidden;
EAuto.Visibility = Visibility.Hidden;
252
}
else
{
LAuto.Visibility = Visibility.Visible;
EAuto.Visibility = Visibility.Visible;
}
}
253
Рис. 3. 3. Модифицированная форма приложения
Всё. Код теперь можно не менять, у нас есть готовый каркас для презентации.
Нам осталось только облагородить кнопки и добавить немного «красоты» в наш каркас.
Когда будет готово и это, можно приступать к заполнению приложения слайдами.
Процесс заполнения слайдов практически напоминает аналогичную работу в Microsoft
Office PowerPoint. Если создавать слайды в Visual Studio то проблем с заполнением не
возникнет. Элементов достаточно, возможностей много. Для добавления слайдов
используем добавление новых страниц (Page), а занесения их в порядок вывода,
добавляем полное имя XAML-файла в переменную slides класса Presentation.
254
Важно отметить, что Expression Blend 4 поддерживает редактирование кода С# и
добавление новых страниц. Фактически это замена среды разработки Visual Studio (в
ней даже можно компилировать приложение).
Этого нам будет достаточно.
255
Рис. 4. 2. Загруженное решение презентации (вид на файл 02Slide.xaml)
Как видим, всё напоминает среды разработки Visual Studio. Аналог обозревателя
решений находится слева вверху, справа всю область занял местный аналог свойств
выбранного элемента. Слева внизу окно доступных в файле XAML объектов и «линия
времени» для анимации. В центре внизу, привычное окно вывода (со вкладками Errors
и Output). Вверху над окном представления (конструктора по сути) находятся вкладки
для переключения между выбранным файлов XAML.
Нажмём F5 (Project -> Run Project), увидим сообщения выводы в нижнем окне
И запустится наше приложение (если не будет ошибок).
256
Рис. 4. 3. Окно New Item
257
Рис. 4. 5. Расстановка элементов на первом слайде
«Рисовать» можно при помощи боковой панели слева. Иноки понятные. Если
нужно добавить элемент, которого нет изначально на боковой панели, можно поискать
в контекстном меню кнопки (нужно правой кнопкой мышки нажать на кнопку боковой
панели). Например, выбор инструмента рисования Pencil (Y):
258
Рис. 4. 6. Выбор элемента из библиотеки элементов
В данной работы для кнопок были «собраны» новые стили. Для создания стиля
на подобие элемента можно нажать правой кнопкой мыши объект формы в Objects and
Timeline -> Edit Template -> Create Empty…. Если нужно скопировать конкретный
стиль на основе выбранного элемента (например Button), жмём на Create Copy….
Далее даём стилю имя, после чего новый стиль должен появиться в окне
ресурсов справа (Resources). Выбираем тот файл, куда добавили ресурс, далее новый
стиль и жмём на Edit resource.
259
Далее размещаем в месте под элемент (чёрная граница) сетку Grid:
И после этого внутри этой сетки рисуем что угодно. Например такой шаблон
(Rectangle и Label):
260
Рис. 4. 8. Добавление Property для стиля кнопки, элемента Rectangle
261
Рис. 4. 9. Запись триггера
262
Рис. 4. 10. Записываем изменение цвета заполнения прямоугольника для события
наведения мышки на элемент
263
Рис. 4. 11. Записываем изменение цвета заполнения прямоугольника для события
нажатия на элемент
Всё. Наш элемент можно вставлять на форму. Для этого можно просто
перетащить его как новую кнопку из окна ресурсов справа или если был создан стиль
(Style), то из окна доступных стилей хранящихся в файлах решения (на рисунке ниже
отображены все доступные стили):
Для добавления нашей кнопки из она ресурсов справа, сначало надо вернуть на
форму. Для этого в окне Objects and Timiline ждмём на символ «стрелочки вверх»:
264
Рис. 4. 12. Применение стиля к новой кнопке
Если всё сделано правильно, кнопка будет корректно менять цвет при наведении
мышки и нажатии на эту кнопку. Обработчик события добавляется либо в среде
разработки (двойным нажатием), либо ручным редактированием кнопки в редакторе
XAML. Если редактор не вызван, выбираем любой элемент на любой форме, далее
нажимаем правую кнопку мышки на этом элементе и выбираем в раскрывающемся
списке View XAML.
265
доступных элементов формы MainWindow.xaml, далее в строке с текстом (No
Storyboard open) нажмём на двойнуу стрелку вниз:
266
Рис. 4. 15. Выполнение анимационного элемента (текущий кадр указан жёлтой
полосой)
267
Откроется окно добавления новой «истории» для записи. Впишем имя
ButtonMove:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO; // Для работы с классом File
using System.Windows.Threading; // Для создания таймеров
namespace LWP09WPF02
{
/// <summary>
/// Логика взаимодействия для MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
268
{
private Presentation NewPresentation; // Создаём экземпляр нашего класса
Presenation
private DispatcherTimer Timer1;
private DispatcherTimer Timer2;
private bool AutoClick;
public MainWindow()
{
InitializeComponent();
NewPresentation = (Presentation)this.FindResource("presentation");
AutoClick = false;
if (DateLastWritten.CompareTo(latestDateTimeWritten) > 0)
{
latestDateTimeWritten = DateLastWritten;
IndexLastWritten = i;
}
}
NewPresentation.CurrentIndex = IndexLastWritten;
//presentation.CurrentIndex = 0; // Если нам нужно, можем всегда стартовать с
первой страницы
269
{
NewPresentation.GoNext();
e.Handled = true;
}
else if (e.Key == Key.Escape)
{
Application.Current.Shutdown();
e.Handled = true;
}
}
if (AutoClick == false)
{
AutoClick = true;
NewPresentation.GoAuto = true;
Timer2.Start();
}
else
{
AutoClick = false;
NewPresentation.GoAuto = false;
Timer2.Stop();
}
if (!NewPresentation.GoAuto)
{
270
LAuto.Visibility = Visibility.Hidden;
EAuto.Visibility = Visibility.Hidden;
}
else
{
LAuto.Visibility = Visibility.Visible;
EAuto.Visibility = Visibility.Visible;
}
}
}
}
Раскомментированная строка:
this.ClickCanvas.RenderTransform = Transform;
<Application x:Class="LWP09WPF02.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:LWP09WPF02="clr-namespace:LWP09WPF02"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="LWP09WPF02.MainWindow"
Title="Презентация WPF (C#)" Height="600" Width="800" ResizeMode="NoResize"
KeyDown="Window_KeyDown" PreviewMouseMove="Window_PreviewMouseMove"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown">
<Window.Resources>
<LWP09WPF02:Presentation x:Key="presentation"/>
271
<Setter Property="Fill" TargetName="path"
Value="#4C176074"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="path"
Value="#FF0C303A"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Fill" TargetName="path"
Value="{x:Null}"/>
<Setter Property="Stroke" TargetName="path"
Value="#FF72A1AE"/>
<Setter Property="StrokeThickness" TargetName="path"
Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="AutoButtonStyle" BasedOn="{x:Null}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Label x:Name="label" Content="Авто" Height="Auto"
Width="Auto" BorderBrush="{x:Null}" Background="#02FF0000" Foreground="#FFCC5252"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Stretch" FontWeight="Bold"
FontFamily="UkrainianBrushScript" FontSize="42.667" ToolTip="Автоматический показ
слайдов">
<Label.Effect>
<BlurEffect Radius="2"/>
</Label.Effect>
</Label>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground"
TargetName="label" Value="#4C176074"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground"
TargetName="label" Value="#FF0C303A"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground"
TargetName="label" Value="#FF72A1AE"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="HomeButtonStyle" BasedOn="{x:Null}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Path Stretch="Fill" Stroke="Black" x:Name="path"
Width="44.15" Height="41" Data="M589.13955,33.208404 L598.54392,33.208406
C598.54392,33.208406 598.54392,69.969662 598.54392,69.969662 L630.46071,69.969662
630.46071,33.208404 639.86497,33.208404 614.37289,8.7009021 C614.37289,8.7009021
589.13955,33.208404 589.13955,33.208404 z" Fill="#FFCC5252" OpacityMask="Black"
VerticalAlignment="Stretch"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
272
<Setter Property="Fill" TargetName="path"
Value="#4C176074"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="path"
Value="#FF0C303A"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Fill" TargetName="path"
Value="{x:Null}"/>
<Setter Property="Stroke"
TargetName="path" Value="#FF72A1AE"/>
<Setter Property="StrokeThickness"
TargetName="path" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Storyboard x:Key="ClickAnimation">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ClickCanvas" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static
Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:00.7000000">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="4.749"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[3].(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-5.623"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[3].(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-0.082"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle3"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].
(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="4.996"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle3"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="5.328"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle3"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-2.748"/>
</DoubleAnimationUsingKeyFrames>
273
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle4" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle1" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle2" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle3" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle4"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].
(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="4.685"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle4"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-3.261"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle4"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="4.463"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].
(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="5.573"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-3.388"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-5.964"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle2"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].
(ScaleTransform.ScaleX)">
274
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="5.555"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle2"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="2.621"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="rectangle2"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="-6.309"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
<Grid Height="29.188" Width="82.416">
<Rectangle x:Name="rectangle" Fill="#FFB85858"
Margin="22.958,0,27.458,0" Stroke="Black" StrokeThickness="3"/>
<TextBlock Margin="34.708,5.094,35.042,8" TextWrapping="Wrap"
Text="T" FontFamily="Poplar Std" FontSize="18.667"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter
Property="IsEnabled" TargetName="rectangle"
Value="True"/>
<Setter
Property="Fill" TargetName="rectangle"
Value="#4C176074"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="rectangle"
Value="#FF0C303A"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Storyboard x:Key="ButtonMove">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.Y)" Storyboard.TargetName="button">
<EasingDoubleKeyFrame KeyTime="0" Value="-4.413"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-3.638"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="-2.863"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="-2.088"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="-1.313"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="-0.538"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].
(TranslateTransform.X)" Storyboard.TargetName="button">
<EasingDoubleKeyFrame KeyTime="0" Value="5"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-35"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="-75"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="-115"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="-150"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="-198"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<!-- Подключаем RoutedEvent и вытаскиваем событие нажатия левой кнопки мышки -->
<EventTrigger RoutedEvent="UIElement.PreviewMouseLeftButtonDown">
<BeginStoryboard Storyboard="{StaticResource ClickAnimation}"/>
</EventTrigger>
275
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource ButtonMove}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Viewbox Margin="10,20,10,40" Stretch="Uniform">
<StackPanel>
<!-- Подставляем всегда: заголовок страницы -->
<TextBlock VerticalAlignment="Top" Height="84" FontFamily="Calibri"
FontSize="65" FontWeight="Bold" Text="{Binding Content.Title, ElementName=Frame,
Mode=Default}" TextAlignment="Center" TextWrapping="Wrap">
<TextBlock.Effect>
<BlurEffect/>
</TextBlock.Effect>
</TextBlock>
<!-- Подставляем всегда: содержимое слайда -->
<Frame Width="1000" Height="600" Source="{Binding CurrentSlide,
Source={StaticResource presentation}}" x:Name="Frame" NavigationUIVisibility="Hidden"
Background="{x:Null}" Focusable="False"/>
</StackPanel>
</Viewbox>
276
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button>
<Ellipse x:Name="EAuto" Fill="#FFE0C0C0" HorizontalAlignment="Right" Height="28"
Margin="0,0,122.316,12.775" Stroke="Black" VerticalAlignment="Bottom" Width="28"
Visibility="Hidden">
<Ellipse.Effect>
<DropShadowEffect ShadowDepth="3" RenderingBias="Quality"/>
</Ellipse.Effect>
</Ellipse>
<Label x:Name="LAuto" Content="A" HorizontalAlignment="Right" Height="30"
Margin="0,0,107.378,14.709" VerticalAlignment="Bottom" Width="40" Background="#00E07878"
FontFamily="Trajan Pro" FontSize="18.667" Foreground="#FFB82913" Visibility="Hidden">
<Label.Effect>
<BlurEffect Radius="2"/>
</Label.Effect>
</Label>
277
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="-67.444"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Width="3" Height="7" Fill="{DynamicResource OrangeClick}"
Stroke="#FF000000" StrokeThickness="0" RenderTransformOrigin="0.5,0.5"
x:Name="rectangle3" Canvas.Left="15.875" Canvas.Top="-4.875">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="-10.339"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Grid>
</Window>
<Page x:Class="LWP09WPF02._01Title"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FlowDirection="LeftToRight"
Width="1000" Height="600">
<Grid x:Name="LayoutRoot">
278
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<TextBlock HorizontalAlignment="Center" Margin="0,123,0,0"
VerticalAlignment="Top" Height="113" FontSize="72" FontWeight="Bold" Text="Название
презентации" TextAlignment="Center" TextWrapping="Wrap" Foreground="#FF9B7979">
<TextBlock.Effect>
<DropShadowEffect/>
</TextBlock.Effect>
</TextBlock>
<TextBlock Margin="499,0,150.213,170" VerticalAlignment="Bottom" Height="65"
FontSize="36" Text="Ваше имя" TextAlignment="Right" TextWrapping="Wrap"/>
<TextBlock Margin="499,0,150.213,127" VerticalAlignment="Bottom" Height="63"
FontSize="36" Text="Другие данные" TextAlignment="Right" TextWrapping="Wrap"/>
<Path Fill="{x:Null}" Stretch="Fill" Stroke="#FF895D80" StrokeThickness="3"
HorizontalAlignment="Right" Margin="0,251.5,125.5,123.5" Width="311" Data="M875,253
L875,477 565,477">
<Path.Effect>
<BlurEffect/>
</Path.Effect>
</Path>
<Path Fill="{x:Null}" Stretch="Fill" Stroke="#FF895D80" StrokeThickness="3"
Width="309" Data="M872.98701,253 L872.98701,477 565,477" HorizontalAlignment="Right"
Margin="0,259.5,117.5,115.5">
<Path.Effect>
<BlurEffect/>
</Path.Effect>
</Path>
<Path Data="M80.5,237.5 L81.5,87.5 246.5,86.5" Fill="{x:Null}"
HorizontalAlignment="Left" Height="154" Margin="82.5,123,0,0" Stretch="Fill"
Stroke="#FF683F52" StrokeThickness="3" VerticalAlignment="Top" Width="169">
<Path.Effect>
<BlurEffect/>
</Path.Effect>
</Path>
<Path Data="M72.499038,227.50034 L73.653041,79.509324 263.49905,78.500323"
Fill="{x:Null}" HorizontalAlignment="Left" Height="152" Margin="73.5,113,0,0"
Stretch="Fill" Stroke="#FF683F52" StrokeThickness="3" VerticalAlignment="Top"
Width="194">
<Path.Effect>
<BlurEffect/>
</Path.Effect>
</Path>
</Grid>
</Page>
5. Завершающая часть
279
Рис. 5. 1. Модифицированное приложение Windows Foundation Presentation (Зелёным
анимирован щелчок по движущейся кнопки, включена автопрокрутка, отображается
первый слайд 01Title.xaml)
280
Варианты заданий: Варианты для выполнения самостоятельных заданий с
использованием материала данной работы приведены по ссылке в конце этого
материала (сслыка доступна в программном продукте).
Содержание
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms: ODBC
4. Модификация приложения Windows Forms: OLE
5. Модификация приложения Windows Forms: XML
6. Завершающая часть
7. О приложении к Лабораторной работе № 10
1. Вводная часть
281
Источник данных (Data Provider) – это набор взаимосвязанных компонентов,
обеспечивающих доступ к данным. Функциональность и само существование
провайдера обеспечивается набором классов, специально для этой цели
разработанных.
ADO.NET поддерживает следующие типа источников данных:
Имя API-
Описание источника данных
провайдера префикс
ODBC Data
Odbc Источники данных с ODBC-интерфейсом. Устаревший провайдер.
Provider
OleDb Data
OleDb Источники данных с OleDb-интерфейсом, для Access или Excel.
Provider
Oracle Data
Oracle Для баз данных Oracle.
Provider
SQL Data
Sql Для работы с Microsoft SQL Server.
Provider
Borland Data Общий доступ к множеству баз данных, таких как Interbase, SQL
Bdp
Provider Server, IBM DB2, и Oracle.
282
API разработчики MFC создали набор абстрактных классов, представляющих
логические сущности в базе данных.
283
Запускаем Visual Studio 2010, откроется Начальная страница:
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
284
Рис. 2. 2. Окно создания нового проекта
285
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
286
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
287
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
288
ПРИМЕЧАНИЕ № 2: Для того, чтобы поменять имя файла нашей формы,
необходимо выполнить следующее: выделить в обозревателе решений значок формы (
(Name): Hint
289
Рис. 3. 2. Расстановка элементов первой группы (ODBC)
Здесь у нас четыре кнопки Button, один RichTextBox (слева внизу), и простой
погашенный TextBox.
Button:
(Name): B_ODBC_Search
Text: Выбрать базу данных (*.mdb)
Size: 200; 23
Button:
(Name): B_ODBC_Connect
Text: Открыть соединение
Size: 200; 23
Button:
(Name): B_ODBC_Add
Text: Добавить запись
Size: 200; 23
Button:
(Name): B_ODBC_Disconnect
Text: Закрыть соединение
Size: 200; 23
TextBox:
(Name): TB_ODBC_Path
ReadOnly: True
RicTextBox:
(Name): RTB_ODBC
ReadOnly: True
GroupBox:
(Name): GB_ODBC
Text: ODBC
OpenFileDialog:
(Name): OFD_ODBC
FileName: LWP10-DB-ODBC
InitialDirectory: D:\
Filter База данных *.mdb|*.mdb
Теперь отправляемся в код формы (правая кнопка мыши на значке формы, далее
Перейти к коду или нажмём на клавишу F7):
290
В самое начало кода добавим:
Найдём:
Добавим после:
Double Counter = 4;
OdbcConnection ConnectionOBDC;
public LWP10Main()
{
InitializeComponent();
B_ODBC_Add.Enabled = false;
B_ODBC_Connect.Enabled = false;
B_ODBC_Disconnect.Enabled = false;
TB_ODBC_Path.Text = "D:\\LWP10-DB-ODBC.mdb";
}
if (OFD_ODBC.ShowDialog() == DialogResult.OK)
{
B_ODBC_Add.Enabled = true;
B_ODBC_Connect.Enabled = true;
B_ODBC_Disconnect.Enabled = true;
Directory.CreateDirectory(Path.GetDirectoryName(OFD_ODBC.FileName) + @"\
Копии"); // Создаём директорию под изменённые БД
File.Copy(OFD_ODBC.FileName, Path.GetDirectoryName(OFD_ODBC.FileName) +
@"\Копии\" + OFD_ODBC.SafeFileName, true); // Копируем туда выбранную БД (перезаписываем,
в случае обнаружения похожего файла)
291
if (Path.GetDirectoryName(OFD_ODBC.FileName) ==
Directory.GetDirectoryRoot(OFD_ODBC.FileName)) // Проверяем путь, если находимся в
корневой директории диска, режем один слеш
TB_ODBC_Path.Text = Path.GetDirectoryName(OFD_ODBC.FileName) +
@"Копии\" + OFD_ODBC.SafeFileName;
else
TB_ODBC_Path.Text = Path.GetDirectoryName(OFD_ODBC.FileName) + @"\
Копии\" + OFD_ODBC.SafeFileName;
}
}
try
{
ConnectionOBDC.Open(); // Открываем соединение
MessageBox.Show("Соединение с базой данных " + TB_ODBC_Path.Text + "
успешно открыто!", "Работа с базами данных (C#) :: ODBC");
}
catch (Exception ex)
{
MessageBox.Show("Невозможно открыть соединение с базой данных " +
TB_ODBC_Path.Text + " (" + ex.Message + ")!", "Работа с базами данных (C#) :: ODBC");
}
}
try
{
Command.ExecuteNonQuery(); // Выполняем команду
RTB_ODBC.Clear(); // Очищаем RichTextBox
RTB_ODBC.AppendText(Command.CommandText); // Вставляем результат
выполнения команды с нашей базой
}
catch (Exception ex)
{
RTB_ODBC.Clear();
RTB_ODBC.AppendText(ex.Message);
292
}
}
try
{
Command.ExecuteNonQuery(); // Выполняем команду
RTB_ODBC.Clear(); // Очищаем RichTextBox
RTB_ODBC.AppendText(Command.CommandText); // Вставляем результат
выполнения команды с нашей базой
ConnectionOBDC.Close(); // Закрываем соединение
MessageBox.Show("Соединение с базой данных " + TB_ODBC_Path.Text + "
успешно закрыто!", "Работа с базами данных (C#) :: ODBC");
}
catch (Exception ex)
{
RTB_ODBC.Clear();
RTB_ODBC.AppendText(ex.Message);
MessageBox.Show("Невозможно закрыть соединение с базой данных " +
TB_ODBC_Path.Text + " (" + ex.Message + ")!", "Работа с базами данных (C#) :: ODBC");
}
}
Последнее что нам нужно, это база данных в формате Microsoft Access 2000.
Сделаем её, например в Microsoft Office Access 2010. База будет содержать одну
таблицу (Главная таблица) и четыре столбца: Ключевое поле (являющее
ключевым, числовое), : Первое поле, Второе поле и Третье поле (все текстовые).
Заполним первые три записи (Ключевое поле: 1, 2 и 3):
293
Рис. 3. 4. Сохранение в формате Access 2000
Удаляем все новые записи кнопкой Закрыть соединение и тем самым также
закрываем соединение с базой.
294
Рис. 3. 5. Окончательная работа блока: ODBC
Для соединения с базой данных Microsoft Office Access 2003 и ниже (файл
*.mdb) в C# следует использовать класс OleDbConnection со следующими
параметрами соединения:
Provider=Microsoft.Jet.OLEDB.4.0; Data Source=DataBaseFile
Здесь DataBaseFile — абсолютный путь к файлу базы данных Access.
«Провайдер» соединения должен иметь значение Microsoft.Jet.OLEDB.4.0.
Для соединения с базой данных Microsoft Office Access 2007 и выше (файл
*.accdb) в C# следует использовать класс OleDbConnection со следующими
параметрами соединения:
Provider=Microsoft.ACE.OLEDB.12.0; Data Source=DataBaseFile
Здесь DataBaseFile — абсолютный путь к файлу базы данных Access.
«Провайдер» соединения должен иметь значение Microsoft.ACE.OLEDB.12.0 либо для
версии Access 2010: Microsoft.ACE.OLEDB.14.0.
Пример:
295
Для выполнения запросов на вставку, изменение, или удаление данных из базы
данных следует использовать метод класса OleDbCommand: ExecuteNonQuery(). Его
вызов выполняет указанный в свойстве CommandText класса OleDbCommand запроc и
возвращает int-число затронутых запросом полей.
Пример:
Если база данных из примеры выше пустая, то int будет равно единице.
Пример:
Здесь представлены три кнопки Button, один ListBox (по центру), простой
погашенный TextBox, как в предыдущей группе, и четыре TextBox’а для ввода с
клавиатуры данных, которые будут добавлять в базу данных. Слева от каждого
размещены по одному текстовому элементу: Label. Также с панели инструментов было
добавлен ещё один OpenFileDialog-элемент.
296
база данных для старых версий Access (*.mdb) и для новых (*.accdb). После этого
будет выдано сообщение об успехе или неудаче выбора с описанием причины ошибки в
случае неудачи. Далее при нажатии Прочитать все записи, в центральный ListBox
будут занесены все записи из базы данных. Ввод значений в TextBox’ы слева и нажатие
кнопки Добавить записи сохранит в нашей базе новый записи. Будет использована та
же самая база данных, что и для предыдущего блока: LWP10-DB-ODBC.mdb, а также
копия этой же базы, сохранённая как файл *.accdb, для версии Access 2007 или Access
2010. База: LWP10-DB-OLE.accdb.
Button:
(Name): B_OLE_1_Search
Text: Выбрать базу данных
Size: 200; 23
Button:
(Name): B_OLE_1_Read
Text: Прочитать все записи
Size: 200; 23
Button:
(Name): B_OLE_1_Add
Text: Добавить записи
Size: 200; 23
TextBox:
(Name): TB_OLE_1_Path
ReadOnly: True
ListBox:
(Name): LB_OLE_1
GroupBox:
(Name): GB_OLE_1
Text: OLE # 1
OpenFileDialog:
(Name): OFD_OLE_1
InitialDirectory: D:\
Filter База данных *.mdb|*.mdb|База данных
*.accdb|*.accdb
TextBox:
(Name): TB_OLE_1_1
TextBox:
(Name): TB_OLE_1_2
TextBox:
(Name): TB_OLE_1_3
TextBox:
(Name): TB_OLE_1_4
Найдём:
297
public partial class LWP10Main : Form
{
Double Counter = 4;
OdbcConnection ConnectionOBDC;
Добавим после:
Найдём:
public LWP10Main()
{
InitializeComponent();
TB_ODBC_Path.Text = "D:\\LWP10-DB-ODBC.mdb";
Добавим после:
TB_OLE_1_Path.Text = "D:\\LWP10-DB-OLE.accdb";
B_OLE_1_Read.Enabled = false;
B_OLE_1_Add.Enabled = false;
TB_OLE_1_1.Text = Counter.ToString();
SQL_OLE = "SELECT * FROM [Главная таблица]";
if (OFD_OLE_1.ShowDialog() == DialogResult.OK)
{
B_OLE_1_Read.Enabled = true; // Активируем кнопку "Прочитать все записи"
B_OLE_1_Add.Enabled = true;
//TB_OLE_1_Path.Text = OFD_OLE_1.FileName;
Directory.CreateDirectory(Path.GetDirectoryName(OFD_OLE_1.FileName) + @"\
Копии"); // Создаём директорию под изменённые БД
File.Copy(OFD_OLE_1.FileName, Path.GetDirectoryName(OFD_OLE_1.FileName) +
@"\Копии\" + OFD_OLE_1.SafeFileName, true); // Копируем туда выбранную БД
(перезаписываем, в случае обнаружения похожего файла)
if (Path.GetDirectoryName(OFD_OLE_1.FileName) ==
Directory.GetDirectoryRoot(OFD_OLE_1.FileName)) // Проверяем путь, если находимся в
корневой директории диска, режем один слеш
TB_OLE_1_Path.Text = Path.GetDirectoryName(OFD_OLE_1.FileName) +
@"Копии\" + OFD_OLE_1.SafeFileName;
else
TB_OLE_1_Path.Text = Path.GetDirectoryName(OFD_OLE_1.FileName) + @"\
Копии\" + OFD_OLE_1.SafeFileName;
298
if (Path.GetExtension(OFD_OLE_1.FileName) == ".accdb")
{
ConnetionStringOLE1 = "Provider=Microsoft.ACE.OLEDB.12.0;" +
@"Data Source=" + TB_OLE_1_Path.Text + "";
MessageBox.Show("Выбрана база данных " + TB_OLE_1_Path.Text + "
формата: *" + Path.GetExtension(TB_OLE_1_Path.Text) + "!", "Работа с базами данных
(C#) :: OLE # 1");
}
}
}
try
{
ConnectionOLE1.Open(); // Открываем соединение
MessageBox.Show("Соединение с базой данных " + TB_OLE_1_Path.Text + "
успешно открыто!", "Работа с базами данных (C#) :: OLE # 1");
}
catch (Exception ex) // Ловим исключение и вытаскиваем ошибку через
ex.Message
{
MessageBox.Show("Невозможно открыть соединение с базой данных " +
TB_OLE_1_Path.Text + " (" + ex.Message + ")!", "Работа с базами данных (C#) :: OLE # 1");
}
OleDbCommand Command = new OleDbCommand(SQL_OLE, ConnectionOLE1); //
Формируем SQL-команду для текущего подключения
OleDbDataReader DataReader = Command.ExecuteReader(); // Формируем объект для
чтения данных из базы данных
LB_OLE_1.Items.Add(Command.CommandText); // Посылаем текст команды в ListBox
// Организуем циклический перебор полученных записей
while (DataReader.Read())
{
LB_OLE_1.Items.Add(DataReader["Ключевое поле"].ToString() + " | " +
DataReader["Первое поле"].ToString() + " | " + DataReader["Второе поле"].ToString() + " |
" + DataReader["Третье поле"].ToString());
}
// Закрываем потоки чтения и соединения
DataReader.Close();
ConnectionOLE1.Close();
}
try
{
ConnectionOLE1.Open(); // Открываем соединение
MessageBox.Show("Соединение с базой данных " + TB_OLE_1_Path.Text + "
успешно открыто!", "Работа с базами данных (C#) :: OLE # 1");
}
catch (Exception ex)
{
299
MessageBox.Show("Невозможно открыть соединение с базой данных " +
TB_OLE_1_Path.Text + " (" + ex.Message + ")!", "Работа с базами данных (C#) :: OLE # 1");
}
SQL_OLE_ADD = "INSERT INTO [Главная таблица] VALUES('" + Counter.ToString() +
"', '" + this.TB_OLE_1_2.Text + "', '" + this.TB_OLE_1_3.Text + "', '" +
this.TB_OLE_1_4.Text + "');";
OleDbCommand Command = new OleDbCommand(SQL_OLE_ADD, ConnectionOLE1);
try
{
Command.ExecuteNonQuery(); // Выполняем команду
}
catch (Exception ex)
{
MessageBox.Show("Невозможно выполнить команду с базой данных " +
TB_OLE_1_Path.Text + " (" + ex.Message + ")!", "Работа с базами данных (C#) :: OLE # 1");
}
LB_OLE_1.Items.Add(Command.CommandText); // Отправляем текст команды в
ListBox
Counter++;
TB_OLE_1_1.Text = Counter.ToString();
}
300
приложении и малые трудозатраты лучше нужно использовать элемент управления
DataGridView и связанные с ним другие элементы, такие как например DataSet.
301
Здесь представлены три кнопки Button, один, простой погашенный TextBox, как
в предыдущей группе и два DataGridView. Также с панели инструментов было добавлен
ещё один OpenFileDialog-элемент и один DataSet. Сразу оговоримся, для работы с
добавлением данных через DataGridView база данных была переделана и название
таблиц и столбцов были изменены (убраны символы кириллицы). Это связано с тем, что
при работе с базой, в которой есть не английские символы в названиях таблиц (и
столбцов) можно столкнуться с ошибками (не всегда). Сама же база осталась без
изменений, Название: LWP10-DB-OLE-Special.accdb. Содержание таблицы
Main_Table:
Button:
(Name): B_OLE_2_Search
Text: Выбрать базу данных
Size: 200; 23
Button:
(Name): B_OLE_2_Read
Text: Прочитать все записи
Size: 200; 23
Button:
(Name): B_OLE_2_Save
Text: Сохранить записи
Size: 200; 23
TextBox:
(Name): TB_OLE_2_Path
ReadOnly: True
GroupBox:
(Name): GB_OLE_2
Text: OLE # 2
OpenFileDialog:
(Name): OFD_OLE_2
InitialDirectory: D:\
Filter База данных *.mdb|*.mdb|База данных
302
*.accdb|*.accdb
DataGridView:
(Name): DataGridViewOLE
DataGridView:
(Name): DataGridViewOLE_S
DataSetw:
(Name): DataSetOLE
Добавим после:
Найдём:
Добавим после:
TB_OLE_2_Path.Text = "D:\\LWP10-DB-OLE.accdb";
B_OLE_2_Read.Enabled = false;
DataGridViewOLE.DataMember = "Table"; // Указываем на тип подсписка для
DataGridView
if (OFD_OLE_2.ShowDialog() == DialogResult.OK)
{
B_OLE_2_Read.Enabled = true;
B_OLE_2_Save.Enabled = true;
//TB_OLE_2_Path.Text = OFD_OLE_2.FileName;
Directory.CreateDirectory(Path.GetDirectoryName(OFD_OLE_2.FileName) + @"\
Копии"); // Создаём директорию под изменённые БД
File.Copy(OFD_OLE_2.FileName, Path.GetDirectoryName(OFD_OLE_2.FileName) +
@"\Копии\" + OFD_OLE_2.SafeFileName, true); // Копируем туда выбранную БД
(перезаписываем, в случае обнаружения похожего файла)
if (Path.GetDirectoryName(OFD_OLE_2.FileName) ==
Directory.GetDirectoryRoot(OFD_OLE_2.FileName)) // Проверяем путь, если находимся в
корневой директории диска, режем один слеш
TB_OLE_2_Path.Text = Path.GetDirectoryName(OFD_OLE_2.FileName) +
@"Копии\" + OFD_OLE_2.SafeFileName;
else
TB_OLE_2_Path.Text = Path.GetDirectoryName(OFD_OLE_2.FileName) + @"\
Копии\" + OFD_OLE_2.SafeFileName;
if (Path.GetExtension(OFD_OLE_2.FileName) == ".mdb")
{
ConnetionStringOLE2 = "Provider=Microsoft.Jet.OLEDB.4.0;" +
@"Data Source=" + TB_OLE_2_Path.Text + "";
303
MessageBox.Show("Выбрана база данных " + TB_OLE_2_Path.Text + "
формата: *" + Path.GetExtension(TB_OLE_2_Path.Text) + "!", "Работа с базами данных
(C#) :: OLE # 2");
}
if (Path.GetExtension(OFD_OLE_2.FileName) == ".accdb")
{
ConnetionStringOLE2 = "Provider=Microsoft.ACE.OLEDB.12.0;" +
@"Data Source=" + TB_OLE_2_Path.Text + "";
MessageBox.Show("Выбрана база данных " + TB_OLE_2_Path.Text + "
формата: *" + Path.GetExtension(TB_OLE_2_Path.Text) + "!", "Работа с базами данных
(C#) :: OLE # 2");
}
}
}
try
{
ConnectionOLE2.Open(); // Открываем соединение
MessageBox.Show("Соединение с базой данных " + TB_OLE_2_Path.Text + "
успешно открыто!", "Работа с базами данных (C#) :: OLE # 2");
}
catch (Exception ex)
{
MessageBox.Show("Невозможно открыть соединение с базой данных " +
TB_OLE_2_Path.Text + " (" + ex.Message + ")!", "Работа с базами данных (C#) :: OLE # 2");
}
// Создаем объект DataAdapter и передаём ему данные запроса
OleDbDataAdapter DataAdapter = new OleDbDataAdapter(); // DataAdapter -
посредник между базой данных и DataSet
DataAdapter.SelectCommand = new OleDbCommand(SQL_OLE, ConnectionOLE2);
DataAdapter.Fill(DataSetOLE); // Данные из адаптера поступают в DataSet
DataGridViewOLE.DataSource = DataSetOLE; // Связываем данные с элементом
DataGridView
// Закрываем соединение
ConnectionOLE2.Close();
}
try
{
DataAdapter_S.Update((DataTable)DataGridViewOLE_S.DataSource);
MessageBox.Show("Изменения в базе данных D:\\Копии\\LWP10-DB-OLE-
Special.accdb успешно внесены!", "Работа с базами данных (C#) :: OLE # 2");
}
catch (Exception ex)
{
MessageBox.Show("Невозможно сохранить изменения в базе данных D:\\Копии\\
LWP10-DB-OLE-Special.accdb (" + ex.Message + ")!", "Работа с базами данных (C#) :: OLE #
2");
}
304
}
ConnetionStringOLE_S = "Provider=Microsoft.ACE.OLEDB.12.0;" +
@"Data Source=D:\Копии\LWP10-DB-OLE-Special.accdb";
ConnectionOLE_S = new OleDbConnection(ConnetionStringOLE_S);
try
{
ConnectionOLE_S.Open(); // Открываем соединение
MessageBox.Show("Соединение с базой данных D:\\Копии\\LWP10-DB-OLE-
Special.accdb успешно открыто!", "Работа с базами данных (C#) :: OLE # 2");
}
catch (Exception ex)
{
MessageBox.Show("Невозможно открыть соединение с базой данных D:\\Копии\\
LWP10-DB-OLE-Special.accdb (" + ex.Message + ")!", "Работа с базами данных (C#) :: OLE #
2");
}
DataTable DataTable_S = new DataTable();
// Создаём команду
OleDbCommand Command = new OleDbCommand("SELECT * FROM Main_Table",
ConnectionOLE_S);
// Создаём адаптер DataAdapter_S: посредник между базой данных и DataSet
DataAdapter_S = new OleDbDataAdapter(Command);
// Создаём построитель команд
// Для адаптера становится доступной команда Update и другие команды
OleDbCommandBuilder CommandBuilder = new OleDbCommandBuilder(DataAdapter_S);
// Данные из адаптера поступают в DataTable_S
DataAdapter_S.Fill(DataTable_S);
// Связываем данные с элементом DataGridView
DataGridViewOLE_S.DataSource = DataTable_S;
// Закрываем соединение
ConnectionOLE_S.Close();
}
305
Рис. 4. 4. Окончательная работа блока: OLE # 2
306
<!-- <step>Почитать вчерашнюю газету.</step> - это сомнительный шаг... -->
<step>Замесить ещё раз, положить на противень и поставить в духовку.</step>
</instructions>
</recipe>
307
<ingredient amount="3" unit="стакан">Мука</ingredient>
Для данной работы вышеописанных свойств достаточно. Хотя XML обладает ещё
целым рядом особенностей. Например, применение тэгов HTML, нетерпимость к
перекрывающимся тэгам (один тэг, открывшийся раньше, закрывается раньше
другого), наличие специальных символов HTML, пустых элементов и прочее.
Найдём:
Добавим после:
308
Рис. 5. 1. Содержание файла LWP10-DB-XML.xml
Здесь у нас четыре кнопки, TextBox и большой ListBox. Также справа внизу
элемент SaveFileDialog. Свойства элементов таковы:
Button:
(Name): B_XML_Search
Text: Выбрать место сохранения
Size: 200; 23
Button:
(Name): B_XML_Create
309
Text: Создать простой документ
Size: 200; 23
Button:
(Name): B_XML_Read
Text: Прочитать простой документ
Size: 200; 23
Button:
(Name): B_XML_DB
Text: База данных обоев
Size: 200; 23
TextBox:
(Name): TB_XML_Path
ReadOnly: True
ListBox:
(Name): LB_XML
GroupBox:
(Name): GB_XML
Text: XML
SaveFileDialog:
(Name): SFD_XML
FileName: XML-Test
InitialDirectory: D:\
Filter XML-файл|*.xml
310
OutputXML.WriteElementString("Question", "Answer");
Random R = new Random();
OutputXML.WriteElementString("A", R.Next(0,1000).ToString());
OutputXML.WriteElementString("B", SQL_OLE);
OutputXML.WriteElementString("C", TB_XML_Path.Text);
OutputXML.WriteStartElement("Names");
OutputXML.WriteStartElement("Name");
OutputXML.WriteAttributeString("Type", "Male");
OutputXML.WriteString("John");
OutputXML.WriteEndElement();
OutputXML.WriteStartElement("Name");
OutputXML.WriteAttributeString("Type", "Male");
OutputXML.WriteString("Teo");
OutputXML.WriteEndElement();
OutputXML.WriteStartElement("Name");
OutputXML.WriteAttributeString("Type", "Famale");
OutputXML.WriteString("Miana");
OutputXML.WriteEndElement();
// Закрываем XML-Test
OutputXML.WriteEndElement();
// Сбрасываем буфферизированные данные
OutputXML.Flush();
// Закрываем фаил, с которым связан output
OutputXML.Close();
MessageBox.Show("Документ " + SFD_XML.FileName + " успешно создан!",
"Работа с базами данных (C#) :: XML");
}
}
311
// Вытаскиваем значения "руками"
XmlNodeList XMLTestAttributes = InputXML.SelectNodes("/XML-
Test[@Count_Parameters='4']");
312
Рис. 5. 3. Окончательная работа блока: XML
313
Для чтения XML-документа предусмотрен класс XMLReader, но он крайне не
удобен, потому что осуществляет последовательное считывание и не позволяет
«прыгать через»/обращаться сразу к нужному элементу.
// Создаём node
// book - имя узла
XmlNode node = xmlDoc.CreateElement("book");
// Добавляем его в качестве ребенка
parentNode.AppendChild(node);
Удаление узла?
// Удаление узла
parentNode.RemoveChild(node);
Работа с атрибутами:
Будем использовать загрузку данных из XML файла в DataSet, отметив, что после
загрузки данных, доступ к данным ничем не отличается от работы с данными при их
загрузке из таблиц БД.
314
Из сказанного можно сделать вывод: хранить двоичные данные можно, только
если они будут представлены как текстовые строки. Однако, прямое преобразование
всего массива байт (двоичных данных) в текстовую строку не будет решением в силу
того, что возможно присутствие в двоичном коде значений, совпадающих со
служебными символами кодировки строк и значений, равных завершению строки. Как
результат - мы сможем поместить в контент тэга только часть строки (до первого
совпадения двоичного символа со значением окончания строки) при возможном
отображении помещенного кода на нескольких строках XML файла для одного
содержимого контента. Следовательно, строка должна формироваться и присваиваться
значению контента как единое целое. Способы выполнить это могут быть разные, от
посимвольного преобразования байт в значение char и последовательного добавления
результата в содержимое контента (медленный способ), до использования
StringBuilder (быстрый способ).
Свойства формы:
(Name): LWP10Wallpapper
Text: Работа с базами данных (C#) :: База
данных обоев
315
Size: 500; 500
Свойства элементов:
Button:
(Name): B_OPEN
Text: Выбрать рисунок
316
Size: 200; 23
Button:
(Name): B_SEARCH
Text: Выбрать рисунок для добавления
Size: 200; 23
Button:
(Name): B_SAVE
Text: Сохранить рисунок в базе данных
Size: 200; 23
TextBox:
(Name): TB_NUMBER
TextBox:
(Name): TB_FORMAT
TextBox:
(Name): TB_SIZE
ReadOnly: True
TextBox:
(Name): TB_NAME
PictureBox:
(Name): PB_MAIN
OpenFileDialog:
(Name): OFD_FIND
InitialDirectory: D:\
Filter GIF-файлы|*.gif|BMP-файлы|*.bmp|JPEG-
файлы|*.jpg
Label:
(Name): L_NAME
Text: Имя файла:
Label:
(Name): L_SIZE
Text: Разрешение рисунка:
Обработчик события Click для кнопки База данных обоев главной формы
выглядит так:
using System.Xml;
using System.IO;
using System.Drawing.Imaging;
Добавим после:
317
// Директория выполнения приложения
private string String_Path = String.Empty;
XmlDocument XML;
// Классы для работв с XML-документом как с объектом базы данных
DataTable WallpapperDataTable = null;
DataSet WallpapperDataSet = null;
if (OFD_FIND.ShowDialog() == DialogResult.OK)
{
PB_MAIN.Image = Image.FromFile(OFD_FIND.FileName);
TB_FORMAT.Text = Path.GetExtension(OFD_FIND.FileName);
TB_NAME.Text = Path.GetFileNameWithoutExtension(OFD_FIND.FileName);
TB_SIZE.Text = PB_MAIN.Image.Width.ToString() + "x" +
PB_MAIN.Image.Height.ToString();
}
}
318
// Строки для метода SELECT в XML-документе
DataRow[] datarows = null;
// Ищем максимальное ID в DataSet (в DataTable)
string s = string.Empty;
try
{
datarows = WallpapperDataTable.Select("id=max(id)");
s = datarows[0]["id"].ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Работа с базами данных (C#) :: База данных
обоев");
}
319
}
PB_MAIN.Image = null;
TB_SIZE.Text = "";
TB_FORMAT.Text = "";
TB_NAME.Text = "";
// Сохраняем данные
WallpapperDataSet.WriteXml(String_Path + @"\Wallpapper-DB.xml",
XmlWriteMode.WriteSchema);
WallpapperDataSet = new DataSet();
// Вновь загружаем сохраненные данные
WallpapperDataSet.ReadXml(String_Path + @"\Wallpapper-DB.xml",
XmlReadMode.Auto);
WallpapperDataTable = WallpapperDataSet.Tables[0];
}
if (TB_NUMBER.Text.Trim() != "")
{
TB_NAME.Text = "";
TB_FORMAT.Text = "";
TB_SIZE.Text = "";
PB_MAIN.Image = null;
GetPicture(TB_NUMBER.Text.Trim()); // Отправляем номер функции, которая
вытащит из XML-документа все данные
}
try
{
320
datarows = WallpapperDataTable.Select("id=" + X); // Получаем все данные
DataTable по ключу <id>X</id>
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Работа с базами данных (C#) :: База данных
обоев");
return;
}
Все данные для одного рисунка по номеру вытаскиваются этим кодом из файла
базы данных в объект DataTable. Данные выводятся из файла через массив DataRow[]
с непосредственным выбором по именам узлов. Из строки символов
<pichash>...</pichash> формируются байты рисунка и затем передаются элементу
PictureBox.
6. Завершающая часть
321
Рис. 6. 1. Модифицированное приложение Windows Forms: результат работы
приложения по сохранению рисунка в базе данных (сохранение выбранного рисунка в
базе)
322
Рис. 6. 2. Модифицированное приложение Windows Forms: результат работы
приложения по сохранению рисунка в базе данных (выбор рисунка из базы по номеру)
323
Рис. 6. 3. Содержимое файла Wallpappaer-DB.xml: четыре рисунка, длинные строчки
<pichash>...</pichash> содержат символы рисунков (окончания строк не видны)
Найдём:
И заменим:
Найдём:
// Сохраняем данные
324
WallpapperDataSet.WriteXml(String_Path + @"\Wallpapper-DB.xml",
XmlWriteMode.WriteSchema);
WallpapperDataSet = new DataSet();
// Вновь загружаем сохраненные данные
WallpapperDataSet.ReadXml(String_Path + @"\Wallpapper-DB.xml",
XmlReadMode.Auto);
WallpapperDataTable = WallpapperDataSet.Tables[0];
Заменим:
Содержание
1. Вводная часть
2. Удалённый объект: создание библиотеки классов удалённого объекта
3. Клиент: создание приложения Windows Forms
4. Сервер: создание консольного приложения
5. Завершающая часть
6. О приложении к Лабораторной работе № 11
1. Вводная часть
325
приложения запущены в один и тот же момент и для передачи данных не используются
временные файла на жёстком диске. Фактически приложения будут общаться друг с
другом словно клиент и сервер. То есть это общение будет аналогично общению между
удалёнными машинами (через протоколы HTTP, FTP или TELNET). Для реализации
данной функциональности лучше всего использоваться так называемые «каналы».
Для чего может понадобится подобное? Динамическая связь приложений как удалённо,
так и на локальной машине удобна для передачи малых объёмов информации. При этом
данные передаются по защищённому каналу (данные можно предварительно
зашифровать тем же MD5-хэшем). Также, такая связь приложений не оставляет
лишнего «мусора» изначально. А также, возможно реализовать удалённое
взаимодействие клиента и сервера.
Первое приложение будет представлять собой окно пустое Windows Forms. Так
будет реализован клиент. Реализация сервера будет в виде консольного окна.
326
Удалённый объект будет сформирован в виде библиотеки классов. Всего три проекта и
три готовых объекта, два из которых — приложения.
Для начала, надо создать первый проект, для этого выполним последовательно:
Файл -> Создать -> Проект… (также можно просто нажать сочетание клавиш
Ctrl+Shift+N или пункт «Создать проект…» на Начальной странице):
327
Рис. 2. 2. Окно создания нового проекта
328
Рис. 2. 3. Вводим данные нового проекта библиотеки классов
329
Рис. 2. 4. Обозреватель решений: состав проекта библиотеки классов
сформированного средой разработки
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LWP11_RemoteObject
{
330
public interface IRemoteSend1
{
void Send(int x, int y);
}
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице).
Выберем слева в пункте Установленные шаблоны язык Visual C#, далее найдём в
списке Приложение Windows Forms. Также здесь можно выбрать какой
использовать «фреймворк» (набора компонентов для написания программ). В нашем
случае выберем .NET Framework 4.
331
Рис. 3. 1. Вводим данные нового проекта приложения Windows Forms
332
Рис. 3. 2. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
333
Рис. 3. 3. Запуск приложения Windows Forms по конфигурации Debug
334
) и нажать правую кнопку мыши, затем выбрать Переименовать. Ввести
необходимое новое имя СОХРАНЯЯ расширение *.cs. После смены имени,
автоматически поменяются имена проассоциированных непосредственно с формой
файлов:
335
Рис. 3. 5. Добавить ссылку: выбор нужного объекта, а именно ранее
скомпилированной библиотеки классов: LWP11-RemoteObject.dll
TextBox:
(Name): TB_1
Size: 30; 20
336
TextBox:
(Name): TB_2
Size: 30; 20
Теперь отправляемся в код формы (правая кнопка мыши на значке формы, далее
Перейти к коду или нажмём на клавишу F7):
Найдём:
Добавим после:
337
if (!Char.IsLetter(e.KeyChar))
{
e.Handled = true;
}
}
338
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице).
Выберем слева в пункте Установленные шаблоны язык Visual C#, далее найдём в
списке Консольное приложение. Также здесь можно выбрать какой использовать
«фреймворк» (набора компонентов для написания программ). В нашем случае выберем
.NET Framework 4.
339
Рис. 4. 2. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
340
Рис. 4. 3. Добавить ссылку: выбор нужного объекта, а именно ранее скомпилированной
библиотеки классов: LWP11-RemoteObject.dll
341
Рис. 4. 4. Добавить ссылку: добавление ссылки System.Runtime.Remoting
using LWP11_RemoteObject;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
342
Ctrl+Shift+A). В открывшемся окне выберем Класс, в качестве имени укажем
RemoteSend.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LWP11_RemoteObject;
namespace LWP11_Server
{
class RemoteSend1 : MarshalByRefObject, IRemoteSend1
{
public void Send(int x, int y)
{
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("\tX = {0}, Y = {1}", x, y);
}
}
343
Компилируем приложение (Release) и запускаем.
5. Завершающая часть
344
Рис. 5. 1. Динамическая связь приложений: результат работы приложения-клиента
(сверху) и приложения-сервера (снизу)
345
Содержание
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms: создание и заполнение
нового документа Microsoft Word
4. Модификация приложения Windows Forms: работа с шаблонами
5. Завершающая часть
6. О приложении к Лабораторной работе № 12
1. Вводная часть
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
346
Рис. 2. 2. Окно создания нового проекта
347
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
348
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
349
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
350
Text изменим с Form1 на Автоматизация
Microsoft Office Word (C#)
^ Поменяем заголовок формы (то что отображается в шапке приложения слева).
Icon изменим изображение (иконку)
приложения
^ Необходим файл значка *.ico.
Size изменим со значений 300; 300 на 500;
315
^ Поменяем размер формы.
Здесь у нас семь кнопок Button, один GroupBox, один OpenFileDialog и один
NumericUpDown.
Button:
(Name): B_Open
351
Text: Открыть Word, создать документ
Size: 200; 23
Button:
(Name): B_Page_1
Text: Страница № 1
Size: 100; 23
Button:
(Name): B_Page_2
Text: Страница № 2
Size: 200; 23
GroupBox:
(Name): GB_1
Text: Автоматическое создание и заполнение
нового документа
Button:
(Name): B_Print
Text: Печать
Size: 100; 23
Button:
(Name): B_Scale
Text: Масштаб
Size: 100; 23
Button:
(Name): B_Numeric
Text: Пронумеровать страницы
Size: 200; 23
Button:
(Name): B_Picture
Text: Вставить рисунок
Size: 200; 23
NumericUpDow:
(Name): NUD_Scale
Text: Increment
Size: 25
Minimum: 25
Maximum: 200
OpenFileDialog:
(Name): OFD_Picture
Filter: Файлы JPEG|*.jpg
DefaultExt: jpg
FileName: Рисунок для Word
352
Рис. 3. 2. Добавить ссылку: добавление библиотеки компонентов от Word 2010
Теперь отправляемся в код формы (правая кнопка мыши на значке формы, далее
Перейти к коду или нажмём на клавишу F7).
Найдём:
Добавим после:
public LWP12Main()
{
InitializeComponent();
B_Page_1.Enabled = false;
B_Page_2.Enabled = false;
B_Print.Enabled = false;
B_Number.Enabled = false;
B_Scale.Enabled = false;
B_Picture.Enabled = false;
}
353
private void B_Open_Click(object sender, EventArgs e)
{
B_Open.Enabled = false;
oWord = new Word.Application(); // Запускаем Word
oWord.Visible = true; // Делаем окно Word видимым
// Старый способ: здесь отражено наличие ключевого слова ref и параметра
oMissing, которые можно не использовать
oDoc = oWord.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref
oMissing); // Создаём новый документ
B_Page_1.Enabled = true;
B_Print.Enabled = true;
B_Number.Enabled = true;
B_Scale.Enabled = true;
B_Picture.Enabled = true;
}
// Вставка текста
Word.Paragraph oPara3;
oRng = oDoc.Bookmarks.get_Item(ref oEndOfDoc).Range;
oPara3 = oDoc.Content.Paragraphs.Add(ref oRng);
oPara3.Range.Text = "Обычный текст. Дальше идёт таблица:";
oPara3.Range.Font.Bold = 0;
oPara3.Format.SpaceAfter = 24;
oPara3.Range.InsertParagraphAfter();
354
for (c = 1; c <= 5; c++) // Заполняем столбцы
{
strText = "r" + r + "c" + c;
oTable.Cell(r, c).Range.Text = strText;
}
oTable.Rows[1].Range.Font.Bold = 1; // Меняем стиль первой строки:
"жирный"
oTable.Rows[1].Range.Font.Italic = 1; // Меняем стиль первой строки:
"курсив"
}
do
{
wrdRng = oDoc.Bookmarks.get_Item(ref oEndOfDoc).Range;
wrdRng.ParagraphFormat.SpaceAfter = 6;
wrdRng.InsertAfter("Текстовая строка");
wrdRng.InsertParagraphAfter();
oPos =
wrdRng.get_Information(Word.WdInformation.wdVerticalPositionRelativeToPage); // Вставляем
текст и получаем после каждой вставки информацию о положении на странице
}
while (dPos >= Convert.ToDouble(oPos));
// Вставка разрыва страницы
object oCollapseEnd = Word.WdCollapseDirection.wdCollapseEnd;
object oPageBreak = Word.WdBreakType.wdPageBreak;
wrdRng.Collapse(ref oCollapseEnd);
wrdRng.InsertBreak(ref oPageBreak);
355
wrdRng.Collapse(ref oCollapseEnd);
B_Page_2.Enabled = true;
}
356
Допустим, необходимо предоставить пользователю возможность отправить
документ на печать прямо из вашего приложения. Можно использовать функцию
Document.PrintOut, но у неё «устрашающее» количество параметров, как и у
большинства функций связи C# с COM объектами.
Решением этой проблемы может быть использование стандартных диалоговых
окон Word, привычных пользователю и позволяющих сократить разработчику
программный код.
Dialogs — это коллекция диалоговых окон Word (всего более 238 элементов
класса Microsoft.Office.Interop.Word.Dialog). Для того, что бы посмотреть все
доступные диалоговые окна, кликните правой кнопкой мышки на WdWordDialog и
выберите пункт меню Перейти к определению (F12):
[DispId(336)]
int Show(ref object TimeOut = Type.Missing);
357
Событие Click кнопки B_Scale («Масштаб»):
switch (viWhere)
{
case 1:
alignment = Word.WdPageNumberAlignment.wdAlignPageNumberRight;
break;
case 2:
alignment = Word.WdPageNumberAlignment.wdAlignPageNumberLeft;
break;
}
oWord.Selection.HeaderFooter.PageNumbers.Add(ref alignment, ref bFirstPage);
}
358
Рис. 3. 3. Окончательная работа первого блока: заполнение первой страницы
документа Word (в Word 2010)
359
Рис. 3. 4. Окончательная работа первого блока: заполнение второй страницы
документа Word (в Word 2010)
360
Рис. 3. 5. Окончательная работа первого блока: нумерация страниц, вставка рисунка
(на позицию курсора!) и масштаб в 50% в Word (в Word 2010)
361
// Вставит изображение на место выделения (если не перемещать выделение, то в
начало документа)
oWord.Selection.InlineShapes.AddPicture(OFD_Picture.FileName);
Image newImage = Image.FromFile(OFD_Picture.FileName);
Clipboard.SetImage(newImage);
Word.Range wrdRng = oDoc.Bookmarks.get_Item(ref oEndOfDoc).Range;
// Вставит изображение из буфера обмена в конец документа
wrdRng.Paste();
}
if (OFD_Picture.ShowDialog() == DialogResult.OK)
{
// Вставит изображение на место выделения (если не перемещать выделение,
то в начало документа)
oWord.Selection.InlineShapes.AddPicture(OFD_Picture.FileName);
Image newImage = Image.FromFile(OFD_Picture.FileName);
// Получаем данные из буфера обмена
IDataObject iData;
iData = Clipboard.GetDataObject();
//MessageBox.Show(iData.GetData(DataFormats.Text).ToString());
// Заносим туда изображение
Clipboard.SetImage(newImage);
Word.Range wrdRng = oDoc.Bookmarks.get_Item(ref oEndOfDoc).Range;
// Вставит изображение из буфера обмена в конец документа
wrdRng.Paste();
// Восстанавливаем буфер
Clipboard.SetDataObject(iData);
}
}
362
Рис. 3. 6. Окончательная работа первого блока: нумерация страниц, вставка рисунков в
начало (на позицию курсора) и в конец документа и масштаб в 50% в Word (в Word
2010)
Этот стиль обеспечит два поля. Одно для названия, другое для даты. Выберем
дату:
363
Теперь, необходимо связать два поля с приложения. Для этого будем
использовать «Закладки». По имени закладки на какой-либо можно будет отыскать
нужное текстовое поле в этом шаблоне. Сделаем закладку из текстового поля
Название. Выделим текст в фиолетовом поле:
364
Для даты правее сделаем закладку с именем Date_Now:
365
Закрываем колонтитул:
366
Удалим номер страницы, оставив только текст слева под чертой. Выделим этот
текст и сделаем из него новую закладку с именем Bottom_Name:
367
проще: выделим всю таблицу и добавим закладку на всё выделение сразу. Выберем
любое поле таблицы далее жмём правую кнопку мыши -> Выделить -> Таблица:
368
Рис. 4. 5. Выбор типа диаграммы
369
Закроем лист Excel. Теперь присвоим диаграмме в Word имя Graph (закладка на
всю таблицу).
Сохраняем шаблон в произвольном месте диска под именем Шаблон: Файл ->
Сохранить как -> Шаблон Word:
370
Изменим: Копировать в выходной каталог: Всегда копировать.
Здесь у нас два TextBox, два элемента Button, один GroupBox и DataGridView.
Свойства задаём такие:
Button:
(Name): B_Save
Text: Сохранить документ Word
Size: 200; 23
Button:
(Name): B_Exit
Text: Выгрузить Word
Size: 200; 23
371
GroupBox:
(Name): GB_2
Text: Автоматическое заполнение шаблона
документа
TextBox:
(Name): TB_Name_1
Text: Введите верхнее имя
Size: 200; 23
TextBox:
(Name): TB_Name_2
Text: Введите нижнее имя
Size: 200; 23
DataGridView:
(Name): DGV_Table
372
private void B_Exit_Click(object sender, EventArgs e)
{
// Выходим из всех открытых приложением окон Word
try
{
B_Save.Enabled = true;
oWordTemplate.Quit();
}
catch { }
try
{
B_Open.Enabled = true;
B_Page_1.Enabled = false;
B_Page_2.Enabled = false;
B_Print.Enabled = false;
B_Number.Enabled = false;
B_Scale.Enabled = false;
B_Picture.Enabled = false;
oWord.Quit();
}
catch { }
}
Добавим после:
Word._Application oWordTemplate;
DataTable T;
Найдём:
InitializeComponent();
...
B_Picture.Enabled = false;
Добавим после:
373
Необходимые для работы функции выполняющие действия по изменению двух
надписей, таблицы и диаграммы:
try
{
oDocTemplate.Bookmarks["Top_Name"].Range.Text = TB_Name_1.Text; //
Заполняем поле вверху документа
oDocTemplate.Bookmarks["Bottom_Name"].Range.Text = TB_Name_2.Text; //
Заполняем поле внизу документа
oDocTemplate.Bookmarks["Date_Now"].Range.Text =
DateTime.Now.Date.ToLocalTime();
SetTable(oDocTemplate, "Table", T); // Заполняем таблицу
SetChart(oDocTemplate, "Graph", T); // Заполняем график
}
catch { }
}
// Метод, заполняющий данными нашу таблицу в шаблоне
private void SetTable(Word._Document oDocTemplate, string bookmark, DataTable
dataContext)
{
dynamic tbl = oDocTemplate.Bookmarks[bookmark].Range.Tables[1];
int tblRow = 0;
int tblCell = 0;
try
{
SetCell(cell, (string)dataContext.Rows[tblRow][tblCell]); //
Вызываем метод SetCell() для проверки числе внутри ячеек и пометки их цветом
}
catch { } // Ловим пустые ячейки
tblRow++;
}
tblCell++;
tblRow = 0;
}
}
// Помечаем ячейки таблицы цветами. Красным цветом те ячейки, где значения меньше
10, зелёным те ячейки, где значения больше 100
private void SetCell(Word.Cell cell, string text)
{
int val;
374
cell.Range.Text = text;
}
// Метод, заполняющий данными график из таблицы в приложении
private void SetChart(Word._Document oDocTemplate, string bookmark, DataTable
dataContext)
{
Word.Chart chart =
oDocTemplate.Bookmarks[bookmark].Range.InlineShapes[1].Chart; // Получаем диаграмму
Word.ChartData chartData = chart.ChartData; // Переводим данные диаграммы
chartData.Activate();
5. Завершающая часть
Далее жмём на Сохранить документ Word. Результат работы показан ниже (Рис.
5. 2):
375
Рис. 5. 2. Модифицированное приложение Windows Forms: результат изменения
шаблона (Новый документ.docx)
376
Немного о работоспособности приложения:
Содержание
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms: создание и заполнение
нового документа Microsoft Excel
4. Модификация приложения Windows Forms: чтение данных с документа
Microsoft Excel
5. Модификация приложения Windows Forms: прочие возможности
Microsoft Excel
6. Завершающая часть
7. О приложении к Лабораторной работе № 13
1. Вводная часть
377
работы будет использована версия Excel 2010. В данной работе будет рассмотрено
следующее:
1. Автоматическое создание и заполнение нового документа.
2. Открытие и изменение документа.
3. Чтение данных из документа Excel.
4. Сохранение документа Excel.
5. Установка парольной защиты на документ Excel.
6. Автоматическое заполнение документа Excel (функции автоматического
заполнения ячеек Excel).
7. Объединение ячеек на листе документа Excel.
8. Создание графиков и диаграмм при помощи «Мастера диаграмм».
9. Работа с приложением, книгами и листами документа Excel.
10. Некоторые другие возможности.
На этих примерах будут продемонстрированы основные возможности работы с
Microsoft Excel при помощи сторонних приложений на C#.
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
378
Рис. 2. 2. Окно создания нового проекта
379
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
380
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
381
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
382
^ Поменяем внутреннее имя формы.
Text изменим с Form1 на Автоматизация
Microsoft Office Excel (C#)
^ Поменяем заголовок формы (то, что отображается в шапке приложения слева).
Icon изменим изображение (иконку)
приложения
^ Необходим файл значка *.ico.
Size изменим со значений 300; 300 на 500;
400
^ Поменяем размер формы.
383
Здесь у нас две кнопки Button, один GroupBox, три TextBox, два
NumericUpDown и ToolTip.
ToolTip:
(Name): Hint
Button:
(Name): B_Open
Text: Открыть Excel, создать документ
Size: 312; 23
Button:
(Name): B_Insert
Text: Вставить данные
Size: 130; 23
TextBox:
(Name): textBox1
TextBox:
(Name): textBox2
TextBox:
(Name): textBox3
GroupBox:
(Name): GB_Insert
Text: Вставка данных в Excel
NumericUpDown:
(Name): NUD_1
Minimum: 1
ToolTip на Hint: Строка
NumericUpDown:
(Name): NUD_2
Minimum: 1
ToolTip на Hint: Столбец
384
Рис. 3. 2. Добавить ссылку: добавление библиотеки компонентов от Excel 2010
Теперь отправляемся в код формы (правая кнопка мыши на значке формы, далее
Перейти к коду или нажмём на клавишу F7).
Найдём:
Добавим после:
Excel.Application ObjExcel1;
Excel.Workbook ObjWorkBook1;
Excel.Worksheet ObjWorkSheet1;
public LWP13Main()
{
InitializeComponent();
B_Insert.Enabled = false;
}
385
Событие Click кнопки B_Open («Открыть Excel, создать документ»):
386
4. Модификация приложения Windows Forms: чтение данных с документа
Microsoft Excel
Button:
(Name): B_Get
Text: Получить данные
Size: 130; 23
GroupBox:
(Name): GB_Get
Text: Получение данных из Excel
TextBox:
(Name): textBox4
ToolTip на Hint: Столбец
Size: 130; 23
RichTextBox:
(Name): richTextBox1
OpenFileDialog:
(Name): OFD_Get
FileName: Данные для извлечения
Filter: Файлы Excel или файлы Access|*.*
387
private void B_Get_Click(object sender, EventArgs e)
{
// Открываем файл Excel
if (OFD_Get.ShowDialog() == DialogResult.OK)
{
// Создаём приложение
Excel.Application ObjExcel2 = new Excel.Application();
// Открываем книгу
Excel.Workbook ObjWorkBook2 = ObjExcel2.Workbooks.Open(OFD_Get.FileName,
0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "", true, false, 0, true, false,
false);
// Выбираем таблицу (лист)
Excel.Worksheet ObjWorkSheet2;
ObjWorkSheet2 = (Excel.Worksheet)ObjWorkBook2.Sheets[1];
// Очищаем от старого текста окно вывода
richTextBox1.Clear();
InitializeComponent();
B_Insert.Enabled = false;
Добавим после:
textBox4.Text = "A";
388
Рис. 4. 2. Окончательная работа второго блока: открытие базы данных Access и запрос
на выбор таблицы или запроса для открытия в Excel (в Excel 2010)
Рис. 4. 3. Окончательная работа второго блока: открытие таблицы базы данных через
Excel (в Excel 2010)
Жмём Не сохранять.
389
Рис. 4. 4. Окончательная работа второго блока: вывод данных столбца таблицы через
Excel (столбец А) в приложение (в Excel 2010)
390
Идея реализации, вкратце, следующая. Для каждого COM-объекта
автоматизации прописывается отдельный интерфейс. В интерфейс включаются все
методы и свойства, которые необходимо использовать у этого COM-объекта. Обратим
внимание — только те, которые необходимо использовать, а вовсе не все, реализуемые
COM-объектом.
Каждый COM-объект автоматизации «заворачивается» в класс COMWrapper,
унаследованный от RealProxy. RealProxy — это стандартный класс, позволяющий
организовать перехват вызовов методов. При создании COM-объекта создаетётся
экземпляр COMWrapper и указывается требуемый интерфейс. Среда динамически
генерирует прокси-объект, реализующий этот интерфейс. С этим прокси-объектом в
дальнейшем и нужно работаетть как с объектом автоматизации. Вызов любого метода
прокси-объетка перехватывается и транслируется в вызов метода Invoke класса
RealProxy, перекрытый в классе COMWrapper. Здесь его можно обработать как угодно.
В реализации по умолчанию, вызовы свойств транслируются в вызовы
соответствующих методов get_ и set_, создаваемых .NET, возвращаемые объекты
автоматизации автоматически заворачиваются в COMWrapper и прочее.
391
свою очередь, набор свойств и методов для управления размерами, видом, масштабом
и упорядочиванием открытых окон, отображением заголовков, цветами и прочее. Эти
же возможности доступны и для свойств и методов объекта Excel.Application -
ActiveWindow (ссылка на активное окно).
Все эти объекты принято определять глобально для того, чтобы обеспечить
доступ к ним из любой функции проекта.
Создание двух рабочих книг из 3-х и 5-ти листов (на примере нашего
приложения):
Excel.Application ObjExcel;
public LWP13Main()
{
InitializeComponent();
ObjExcel = new Excel.Application();
ObjExcel.Visible = true;
ObjExcel.SheetsInNewWorkbook = 3;
ObjExcel.Workbooks.Add(Type.Missing);
ObjExcel.SheetsInNewWorkbook = 5;
ObjExcel.Workbooks.Add(Type.Missing);
}
В качестве параметра методу Add можно передать имя шаблона рабочей книги,
однако, в этом случае мы привязываемся к пути, по которому инсталлированы
приложения Microsoft Office. В примере использован другой способ: Type — класс
декларации типов, Type.Missing — отсутствие значения. Некоторые методы Excel
принимают необязательные параметры, которые не поддерживаются в C#. Для
решения этой проблемы в коде на C# требуется передавать поле Type.Missing вместо
каждого необязательного параметра, который является ссылочным типом (reference
type). Кроме того, (этого нет в документации) при задании в методе ADD чисел от 1 до
7 будет создана книга с одним листом (1, 6), диаграмма (2), макрос (3, 4) и книга с
четырьмя листами (5).
Из других свойств отметим свойство TemplatesPath. С его помощью, зная имя
файла шаблона, можно напрямую задавать имя шаблона (правда, в этом нет
необходимости, если мы не хотим использовать, например, свой собственный шаблон).
Свойство StartupPath возвращает путь к папке, которая содержит надстройки,
выполняемые при запуске Excel и, хотя свойство для отображения информации нам
ничего не дает, все же порой бывает необходимо найти имя файла настроек и удалить
его для того, чтобы приложение работало только с собственными настройками.
392
ObjExcel.Workbooks.Close();
ObjExcel.Windows[1].Close(false, Type.Missing, Type.Missing);
Подробнее:
ObjExcel.Windows[1].Close(
SaveChanges, // Если в книге нет никаких изменений в документе, то
параметр игнорируется.
// Иначе, если есть изменения, но есть ссылки на закрываемую
книгу
// в других открытых окнах - этот параметр также
игнорируется.
// При отсутствии ссылок и наличии изменений - этот параметр
// определяет, должны ли быть сохранены изменения.
// При true и определенном параметре Filename - изменения
// сохраняются, иначе запрашиваетcя имя файла. При false
сохранения
// нет. Если Type.Missing - вызывается диалоговое окно Save
As
Filename, // Имя файла
RouteWorkbook // Если файл не должен быть перенаправлен другому получателю
// этот параметр игнорируется. Иначе при true файл
направляется
// следующему получателю. При false пересылки нет
);
Excel.Workbook ObjWorkBook;
Excel.Workbooks ObjWorkBooks;
public LWP13Main()
{
...
// Запрашивать сохранение
ObjExcel.DisplayAlerts = true;
// Получаем набор ссылок на объекты Workbook (на созданные книги)
ObjWorkBooks = ObjExcel.Workbooks;
// Получаем ссылку на книгу 1 - нумерация от 1
ObjWorkBook = ObjWorkBooks[1];
// Ссылку можно получить и так, но тогда надо знать имена книг,
// причём, после сохранения - знать расширение файла
// ObjWorkBook = ObjWorkBooks["Book 1"];
// Запроса на сохранение для книги не должно быть
ObjWorkBook.Saved = true;
// Используем свойство Count, число Workbook в Workbooks
if (ObjWorkBooks.Count > 1)
{
ObjWorkBook = ObjWorkBooks[2];
// Запрос на сохранение книги 2 должен быть
ObjWorkBook.Saved = false;
}
}
393
Теперь, если выйти на конкретную книгу, как показано в примере, приведенном
выше, и присвоить свойству Saved объекта Workbook значение true, Excel согласно
документации не должен предлагать сохранение независимо от того, были или нет
изменения в данной книге.
Например:
ObjExcel.DefaultSaveFormat = Excel.XlFileFormat.xlHtml;
// Устанавливаем формат
ObjExcel.DefaultSaveFormat = Excel.XlFileFormat.xlExcel9795;
// Будем спрашивать разрешение на запись поверх существующего документа
ObjExcel.DisplayAlerts = true;
ObjWorkBook = ObjWorkBooks[1];
// Сохраняем книгу 1
ObjWorkBook.Save();
ObjWorkBook = ObjWorkBooks[2];
// Сохраняем книгу 2
ObjWorkBook.Save();
ObjWorkBook.SaveAs(
Filename, // Имя сохраняемого файла
FileFormat, // Формат сохраняемого файла
Password, // Пароль доступа к файлу до 15 символов
WriteResPassword, // Пароль на доступ на запись
ReadOnlyRecommended, // При true режим только для чтения
CreateBackup, // Создать резервную копию файла при true
AccessMode, // Режим доступа к рабочей книге
ConflictResolution, // Способ разрешения конфликтов
AddToMru, // При true сохраненный документ добавляется
// в список ранее открытых файлов
394
TextCodePage, // Кодовая страница
TextVisualLayout, // Направление размещения текста
Local // Идентификатор Excel.Application
);
ObjExcel.Workbooks.Open(@"C:\Документ.html",
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
ObjExcel.Workbooks.Open(@"C:\Документ.xlsx",
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
Параметров как видим очень много. Метод Open и его свойства таковы:
ObjWorkBooks.Open(
FileName, // Имя открываемого файла
UpdateLinks, // Способ обновления ссылок в файле
ReadOnly, // При значении true открытие только для
чтения
Format, // Определение формата символа разделителя
Password, // Пароль доступа к файлу до 15 символов
WriteResPassword, // Пароль на сохранение файла
IgnoreReadOnlyRecommended, // При значении true отключается вывод
// запроса на работу без внесения изменений
Origin, // Тип текстового файла
Delimiter, // Разделитель при Format = 6
Editable, // Используется только для надстроек Excel 4.0
Notify, // При значении true имя файла добавляется в
// список нотификации файлов
395
Converter, // Используется для передачи индекса
конвертера файла
// используемого для открытия файла
AddToMRU, // При true имя файла добавляется в список
// открытых файлов
Local, // Языковые параметры загрузки (true или
false)
CorruptLoad // Режим загрузки файла (всего 3 возможных)
);
GroupBox:
(Name): GB_Other
Text: Прочие возможности работы с Excel
396
Button:
(Name): B_Create
Text: Сформировать готовый документ с
графиком
Size: 312; 23
Button:
(Name): B_Other
Text: Другие листы
Size: 100; 23
Button:
(Name): B_Action
Text: Выполнить различные действия
Size: 206; 23
Button:
(Name): B_Unfreeze
Text: Разморозить
Size: 100; 23
Button:
(Name): B_Merge
Text: Объединение
Size: 100; 23
Button:
(Name): B_Exit
Text: Выгрузить все открытые приложением
экземпляры Excel
Size: 312; 23
Button:
(Name): B_AutoFill
Text: Автозаполнение
Size: 100; 23
//
397
Excel.Application ObjExcel3;
//
Excel.Application ObjExcel4;
Excel.Workbook ObjWorkBook4;
Excel.Workbooks ObjWorkBooks4;
Excel.Worksheet ObjWorkSheet4;
Excel.Sheets ObjSheets4;
Excel.Range ObjRange4;
//
Excel.Application ObjExcel5;
Excel.Workbook ObjWorkBook5;
Excel.Workbooks ObjWorkBooks5;
Excel.Worksheet ObjWorkSheet5;
Excel.Sheets ObjSheets5;
Excel.Range ObjRange5;
//
Excel.Application ObjExcel6;
Excel.Workbook ObjWorkBook6;
Excel.Workbooks ObjWorkBooks6;
Excel.Worksheet ObjWorkSheet6;
Excel.Sheets ObjSheets6;
Excel.Range ObjRange6;
//
Excel.Application ObjExcel7;
Excel.Workbook ObjWorkBook7;
Excel.Workbooks ObjWorkBooks7;
Excel.Worksheet ObjWorkSheet7;
Excel.Sheets ObjSheets7;
Excel.Range ObjRange7;
try
{
ObjExcel3 = new Excel.Application();
ObjExcel3.Visible = true;
// Задаём число листов в новом документе (Книге)
ObjExcel3.SheetsInNewWorkbook = 3;
ObjWorkBook3 = (Excel._Workbook)(ObjExcel3.Workbooks.Add(Type.Missing));
ObjWorkSheet3 = (Excel._Worksheet)ObjWorkBook3.ActiveSheet;
// Заполняем заголовочные ячейки
ObjWorkSheet3.Cells[1, 1] = "Имя";
ObjWorkSheet3.Cells[1, 2] = "Фамилия";
ObjWorkSheet3.Cells[1, 3] = "Полное имя";
ObjWorkSheet3.Cells[1, 4] = "Продажи";
// Форматируем A1:D1 как "жирный", вертикальное положение: по центру
ObjWorkSheet3.get_Range("A1", "D1").Font.Bold = true;
ObjWorkSheet3.get_Range("A1", "D1").VerticalAlignment =
Excel.XlVAlign.xlVAlignCenter;
// Создаём массив для заполнения ячеек данными (имена и фамилии)
string[,] saNames = new string[5, 2];
saNames[0, 0] = "Иван";
saNames[0, 1] = "Иванов";
saNames[1, 0] = "Антон";
398
saNames[1, 1] = "Антонов";
saNames[2, 0] = "Пётр";
saNames[2, 1] = "Петров";
saNames[3, 0] = "Андрей";
saNames[3, 1] = "Андреев";
saNames[4, 0] = "Кейв";
saNames[4, 1] = "Джонсон";
// Заполняем A2:B6 из массива ("Имя" и "Фамилия").
ObjWorkSheet3.get_Range("A2", "B6").Value2 = saNames;
// Заполняем C2:C6 по формуле (=A2 & " " & B2).
ObjRange3 = ObjWorkSheet3.get_Range("C2", "C6");
ObjRange3.Formula = "=A2 & \" \" & B2";
// Заполняем D2:D6 по формлуе (=RAND()*100000) и применяем формат
ObjRange3 = ObjWorkSheet3.get_Range("D2", "D6");
ObjRange3.Formula = "=RAND()*1000";
ObjRange3.NumberFormat = "0.00р";
// Автозаполнение A:D.
ObjRange3 = ObjWorkSheet3.get_Range("A1", "D1");
ObjRange3.EntireColumn.AutoFit();
// Манипулируем с переменным числом столбцов для квартальных
// данных продаж (вызываем метод DisplayQuarterlySales).
// Для построение графика будет использован ChartWizard
DisplayQuarterlySales(ObjWorkSheet3);
// Делаем Excel видимым и передаём управления пользователю
ObjExcel3.Visible = true;
ObjExcel3.UserControl = true;
}
catch (Exception ex)
{
String errorMessage;
errorMessage = "Ошибка: ";
errorMessage = String.Concat(errorMessage, ex.Message);
errorMessage = String.Concat(errorMessage, "\nЛиния: ");
errorMessage = String.Concat(errorMessage, ex.Source);
MessageBox.Show(errorMessage, "Автоматизация Microsoft Office Excel
(C#) :: Ошибка построения диаграммы");
}
ObjExcel3.Quit();
}
399
ObjResizeRange3 = ObjWorkSheet3.get_Range("E1",
"E1").get_Resize(Missing.Value, iNumQtrs);
ObjResizeRange3.Formula = "=\"К\" & COLUMN()-4 & CHAR(10) & \"(квартал)\"";
// Меняем ориентацию текста (38) и параметр поворота текста (true)
ObjResizeRange3.Orientation = 38;
ObjResizeRange3.WrapText = true;
// Заполняем заголовки (верхнюю ячейку столбца) начиная с E1 жёлтым цветом
ObjResizeRange3.Interior.ColorIndex = 36;
// Заполняем E2:E6 формулами (случайное число до 100) и меняем формат
отображения (рубли)
ObjResizeRange3 = ObjWorkSheet3.get_Range("E2",
"E6").get_Resize(Missing.Value, iNumQtrs);
ObjResizeRange3.Formula = "=RAND()*100";
ObjResizeRange3.NumberFormat = "0.00р";
// Применяем к E1:E6 чёрные границы ячеек
ObjResizeRange3 = ObjWorkSheet3.get_Range("E1",
"E6").get_Resize(Missing.Value, iNumQtrs);
ObjResizeRange3.Borders.Weight = Excel.XlBorderWeight.xlThin;
// Для всех ячеек ниже E1 формируем сумму, под числом рисуем двойную черту
ObjResizeRange3 = ObjWorkSheet3.get_Range("E8",
"E8").get_Resize(Missing.Value, iNumQtrs);
ObjResizeRange3.Formula = "=SUM(E2:E6)";
ObjResizeRange3.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle
= Excel.XlLineStyle.xlDouble;
ObjResizeRange3.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).Weight =
Excel.XlBorderWeight.xlThick;
// Создаём график для выбранных данных
ObjWorkBook3 = (Excel._Workbook)ObjWorkSheet3.Parent;
ObjChart3 = (Excel._Chart)ObjWorkBook3.Charts.Add(Missing.Value,
Missing.Value, Missing.Value, Missing.Value);
// Используем "Мастер диаграмм" чтобы создать новый график на основе
выбранных данных
ObjResizeRange3 = ObjWorkSheet3.get_Range("E2:E6",
Missing.Value).get_Resize(Missing.Value, iNumQtrs);
// Делаем график объёмной
ObjChart3.ChartWizard(Missing.Value, Excel.XlChartType.xl3DColumn,
Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value);
// Меняем подписи срава (по вертикали) на "Полное имя"
ObjSeries3 = (Excel.Series)ObjChart3.SeriesCollection(1);
// В цикле пробегаем по каждому элементу подписи по вертикали справа
for (int iRet = 1; iRet <= 5; iRet++)
{
ObjSeries3 = (Excel.Series)ObjChart3.SeriesCollection(iRet);
ObjSeries3.Name = ObjWorkSheet3.get_Range("C" + (iRet + 1), "C" + (iRet +
1)).Value2;
}
ObjChart3.Location(Excel.XlChartLocation.xlLocationAsObject,
ObjWorkSheet3.Name);
// Перемещаем график так, чтобы он не закрывал покрыть данные
ObjResizeRange3 = (Excel.Range)ObjWorkSheet3.Rows.get_Item(10,
Missing.Value);
ObjWorkSheet3.Shapes.Item("Chart 1").Top = (float)
(double)ObjResizeRange3.Top;
ObjResizeRange3 = (Excel.Range)ObjWorkSheet3.Columns.get_Item(2,
Missing.Value);
ObjWorkSheet3.Shapes.Item("Chart 1").Left = (float)
(double)ObjResizeRange3.Left;
// Сохраняем документ и устанавливаем статус документа "сохранён"
ObjWorkBook3.Saved = true;
// Не будем спрашивать разрешение на запись поверх существующего документа
ObjExcel3.DisplayAlerts = false;
// Сохраняем документ в папке с приложением
ObjWorkBook3.SaveAs(
400
Environment.CurrentDirectory + "\\Новый документ", // Имя файла (object
FileFormat)
ObjExcel3.DefaultSaveFormat, // Формат файла
"123", // Пароль (object Password)
"321", // Повтор пароля (object
WriteResPassword)
Type.Missing, // (object ReadOnlyRecommended)
Type.Missing, // (object object CreateBackup)
Excel.XlSaveAsAccessMode.xlNoChange,// (object XlSaveAsAccessMode
AccessMode)
Type.Missing, // (object ConflictResolution)
Type.Missing, // (object AddToMru)
Type.Missing, // (object TextCodepage)
Type.Missing, // (object TextVisualLayout)
Type.Missing); // (object Local)
System.Threading.Thread.Sleep(1000);
}
ObjExcel3.Quit();
try
{
B_Create.Enabled = true;
B_Other.Enabled = false;
B_Merge.Enabled = false;
ObjExcel3.Quit();
401
}
catch { }
try
{
ObjExcel4.Quit();
}
catch { }
try
{
ObjExcel5.Quit();
}
catch { }
try
{
B_Unfreeze.Enabled = false;
ObjExcel6.Quit();
}
catch { }
try
{
B_AutoFill.Enabled = true;
ObjExcel7.Quit();
}
catch { }
}
402
ObjRange4.Value2 = "Лист 2";
ObjRange4.Font.Size = 20;
ObjRange4.Font.Italic = true;
ObjRange4.Font.Bold = true;
// Выбираем лист 3
ObjWorkSheet4 = (Excel.Worksheet)ObjSheets4.get_Item(3);
// Делаем третий лист активным
ObjWorkSheet4.Activate();
// Вывод в ячейки используя номер строки и столбца Cells[строка, столбец]
for(m = 1; m < 20; m++)
{
403
ObjRange7.Interior.PatternColorIndex = Excel.Constants.xlAutomatic;
// Сохраняем результат
ObjWorkBooks7 = ObjExcel7.Workbooks;
ObjWorkBook7 = ObjWorkBooks7[1];
ObjWorkBook7.Save();
System.Threading.Thread.Sleep(1000);
ObjExcel7.Quit();
}
try
{
Excel.Worksheet sheet = (Excel.Worksheet)ObjWorkBook7.Sheets[viNumSheet];
Excel.DocEvents_Event sheetEvents = (Excel.DocEvents_Event)sheet;
Excel._Worksheet _sheet = (Excel._Worksheet)sheet;
sheetEvents.Activate += new
Excel.DocEvents_ActivateEventHandler(sheetEvents_Activate);
_sheet.Activate();
}
catch (Exception)
{
return 1;
}
return 0;
}
//ObjWorkSheet7.Activate();
404
ObjExcel5.Visible = true;
ObjExcel5.UserControl = true;
ObjWorkBooks5 = ObjExcel5.Workbooks;
ObjWorkBook5 = ObjExcel5.Workbooks.Open(Environment.CurrentDirectory + "\\
Пример автозаполнения", Type.Missing, Type.Missing, Type.Missing, "123", "321",
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing);
ObjSheets5 = ObjWorkBook5.Worksheets;
ObjWorkSheet5 = (Excel.Worksheet)ObjSheets5.get_Item(1);
// Делаем первый лист активным
ObjWorkSheet5.Activate();
ObjRange5 = ObjWorkSheet5.get_Range("B1", "B1");
ObjRange5.Value2 = "1";
ObjRange5 = ObjWorkSheet5.get_Range("B2", "B2");
ObjRange5.Value2 = "3";
Excel.Range ObjRange51 = ObjExcel5.get_Range("B1:B2", Type.Missing);
Excel.Range ObjRange52 = ObjExcel5.get_Range("B1:B15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillSeries);
ObjRange51 = ObjExcel5.get_Range("C1", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("C1:C15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillYears);
ObjRange51 = ObjExcel5.get_Range("D1", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("D1:D15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillMonths);
ObjRange51 = ObjExcel5.get_Range("E1", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("E1:E15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillDefault);
ObjRange51 = ObjExcel5.get_Range("F1", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("F1:F15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillWeekdays);
ObjRange51 = ObjExcel5.get_Range("G1", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("G1:G15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillDays);
ObjRange51 = ObjExcel5.get_Range("H1", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("H1:H15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillDays);
ObjRange51 = ObjExcel5.get_Range("I1:I2", Type.Missing);
ObjRange52 = ObjExcel5.get_Range("I1:I15", Type.Missing);
ObjRange51.AutoFill(ObjRange52, Excel.XlAutoFillType.xlFillSeries);
// Сохраняем результат
ObjWorkBooks5 = ObjExcel5.Workbooks;
ObjWorkBook5 = ObjWorkBooks5[1];
ObjWorkBook5.Saved = true;
// Не будем спрашивать разрешение на запись поверх существующего документа
ObjExcel5.DisplayAlerts = false;
ObjWorkBook5.SaveAs(
Environment.CurrentDirectory + "\\Автозаполнение",
ObjExcel5.DefaultSaveFormat, Type.Missing,
Type.Missing, Type.Missing, Type.Missing,
Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing);
System.Threading.Thread.Sleep(1000);
ObjExcel5.Quit();
}
405
Рис. 5. 2. Исходный документ «Пример автозаполнения» (Файл типа Лист Microsoft
Excel, разрешение *.xlsx)
406
// Exit Design Mode, Stop Recording, Chart, Picture, Reviewing, Drawing,
PivotTable
// Forms, Control Toolbox и другие
ObjExcel6.CommandBars["Standard"].Visible = true;
// Можно получить или изменить форму курсора - свойство Cursor.
// Возможные значения: xlDefault, xlIBeam, xlNorthwestArrow, xlWait.
// Посмотреть имя курсора:
Text = ObjExcel6.Cursor.ToString();
MessageBox.Show("Текущий курсор: " + Text, "Автоматизация Microsoft Office
Excel (C#) :: Курсор мыши");
// Можно изменить курсор
ObjExcel6.Cursor = Excel.XlMousePointer.xlWait;
// Можно изменить масштаб отображения документа (свойство Zoom):
ObjExcel6.ActiveWindow.Zoom = 50;
// Можно изменить шрифт по умолчанию и его размер. После перезапуска
// Excel все выведенное будет отображено данным шрифтом:
ObjExcel6.StandardFont = "Arial";
ObjExcel6.StandardFontSize = 10;
// Можно не отображать строку редактирования содержимого ячейки
// (свойство DisplayFormulaBar)
ObjExcel6.DisplayFormulaBar = false;
// Можно запретить редактирование ячеек в самих ячейках (свойство
EditDirectlyInCell),
// разрешив редактирование только в строке формул
ObjExcel6.EditDirectlyInCell = false;
// Можно вообще запретить доступ к документу. Если свойство Interactiv
// не вернуть в true, то нельзя будет даже закрыть Excel
ObjExcel6.Interactive = false;
// Можно программно запретить обновление экрана после каждого изменения
// и, после выполнения большого объема выводимой информации, разрешить.
// Результат: увеличение скорости вывода
ObjExcel6.ScreenUpdating = false; // Запретить
// ... здесь большой объём выводимой информации
ObjExcel6.ScreenUpdating = true; // Разрешить
// Можно принудительно выполнить пересчёты формул, используя метод Calculate,
// в диапазоне ячеек, в книге или во всех открытых рабочих книгах
ObjSheets6 = ObjWorkBook6.Worksheets;
ObjWorkSheet6 = (Excel.Worksheet)ObjSheets6.get_Item(1);
// Для диапазона:
// ObjRange6 = ObjWorkSheet6.get_Range("A1", "С10").Calculate();
// Для книги:
// ObjWorkBook6.Calculate();
// Для всех книг:
// ObjExcel6.Calculate();
// Можно проверить правильность написания текста. Например,
// следующие строки дадут результат "написан некорректно".
// В методе CheckSpelling можно задать словарь (второй параметр)
// и задать игнорировать ли регистр (третий параметр) при проверки
ObjRange6 = ObjWorkSheet6.get_Range("A1", Type.Missing);
ObjRange6.Value2 = "Текьст";
Text = (ObjExcel6.CheckSpelling(ObjRange6.Value2.ToString(), Type.Missing,
true) ? "написан корректно" : "написан некорректно");
MessageBox.Show("Текст в ячейке: \"" + ObjRange6.Value2 + "\", " + Text,
"Автоматизация Microsoft Office Excel (C#) :: Проверка орфографии");
// Можно отменить последнее из выполненных действий (метод Undo), выполненное
// в самом приложении (не влияет на операции, выполненные из приложения)
// ObjExcel6.Undo();
// Можно получить и изменить путь сохранения и открытия файлов по умолчанию
Text = ObjExcel6.DefaultFilePath; // Выведет Ваш путь
MessageBox.Show("Текущий путь: " + Text, "Автоматизация Microsoft Office
Excel (C#) :: Текущий путь");
ObjExcel6.DefaultFilePath = @"C:\";
Text = ObjExcel6.DefaultFilePath; // Выведет C:\
// Можно создать копию документа, используя метод Workbook.NewWindow()
// Например для документа "a" будут созданы окна "a:1" и "a:2":
407
Excel.Window ObjWindow6 = ObjWorkBook6.NewWindow();
// Можно создать копию документа и по другому - через свойства
Application.Workbooks.
// Если окон много, то для проверки наличия окна целесообразно
// использовать свойство Count
if (ObjExcel6.Windows.Count > 1)
{
ObjWindow6 = ObjExcel6.Windows[1];
ObjWindow6.Application.Workbooks[1].NewWindow();
}
// Можно изменить расположение окон используя метод Arange. Порядок
расположения
// определяет первый параметр метода: xlArrangeStyleCascade,
xlArrangeStyleHorizontal,
// xlArrangeStyleTiled, xlArrangeStyleVertical. Второй параметр при true
означает, что
// требуется упорядочить только видимые окна активной книги, при false - все.
// Третий и четвертый параметр - синхронизация разверток горизонтальной и
вертикальной
ObjExcel6.Windows.Arrange(Excel.XlArrangeStyle.xlArrangeStyleVertical, true,
true, true);
// Можно убрать заголовки строк и столбцов, используя свойство
DisplayHeadings
ObjWindow6.DisplayHeadings = false;
// Или так
ObjExcel6.ActiveWindow.DisplayHeadings = false;
// Можно при значении свойства DisplayFormulas равным true показываеть в
// ячейках формулы (там где они есть), а при false - значения
ObjWindow6.DisplayFormulas = false;
// Можно, используя свойство DisplayWorkbookTabs при true показываеть помимо
// Scrollbars позиции табуляции для выбора листов книг и кнопки навигации по
// листам, или, убрать их, при значении свойства равным false
ObjWindow6.DisplayWorkbookTabs = true;
// Можно разделить лист путем отделения как, отдельной части, несколько
// cтолбцов или строк, используя свойства SplitColumn или SplitRow
ObjWindow6.SplitColumn = 5;
ObjWindow6.SplitRow = 5;
// Можно разделить окно вертикально или горизонтально используя свойства
SplitVertical
// или SplitHorizontal (практически аналог предыдущего пункта)
ObjWindow6.SplitVertical = 10;
ObjWindow6.SplitHorizontal = 10;
// Можно изменить цвет сетки для листов.
// 1. Используя свойство GridlineColor
ObjWindow6.GridlineColor = ColorTranslator.ToOle(Color.Blue);
// 2. Используя свойство GridlineColorIndex
ObjWindow6.GridlineColorIndex = (Excel.XlColorIndex)3;
// Можно вообще убрать сетку, используя свойство DisplayGridlines
ObjWindow6.DisplayGridlines = false;
// Можно получить список всех недавно открывавшихся файлов.
// Для этого используется свойство Eccel.Application.RecentFiles
for (int j = 0; j < ObjExcel6.RecentFiles.Count; j++)
{
ObjRange6 = (Excel.Range)ObjWorkSheet6.Cells[j + 1, 1];
ObjRange6.Value2 = ObjExcel6.RecentFiles[j + 1].Name;
}
// Можно перейти на последнюю заполненную ячейку Excel
ObjExcel6.ActiveCell.SpecialCells(Excel.XlCellType.xlCellTypeLastCell,
Type.Missing).Select();
}
408
ObjExcel6.Interactive = true;
}
6. Завершающая часть
409
Рис. 6. 2. Модифицированное приложение Windows Forms: результат создания новой
книги для графика (данные за четыре квартала и график на основе данных)
Жмём Объединение:
410
Рис. 6. 4. Модифицированное приложение Windows Forms: результат создания
изменения книги с графиком (заполненный Лист2)
Жмём Автозаполнение:
411
Рис. 6. 6. Модифицированное приложение Windows Forms: результат работы кнопки
«Выполнить различные действия» и «Разморозить»
Содержание
412
22.Вводная часть
23.Создание приложения Windows Forms
24.Модификация приложения Windows Forms: подготовка интерфейса
редактора
25.Модификация приложения Windows Forms: функциональность
растрового редактора
26.Завершающая часть
27.О приложении к Лабораторной работе № 14
1. Вводная часть
413
будет доступна функция заливки фигур произвольным цветом, а также
изменения толщины пера и цвета пера для контуров фигур при рисовании;
будет доступна возможность очистки поля рисования (полная очистка и ластик),
а также сохранения рисунка в разные графические форматы (например: JPEG);
будет доступна возможность загрузки сохранённого рисунка или любого другого
рисунка;
будет доступна возможность работы с «эффектами», а именно осветление
выбранного участка рисунка.
System.Drawing:
Содержит большинство классов, структур, перечислений и делегатов,
обеспечивающих базовую функциональность рисования.
System.Drawing.Drawing2D:
Представляет основную поддержку для двумерной и векторной графики,
включая сглаживание, геометрические трансформации и графические пути.
System.Drawing.Imaging:
Содержит различные классы, обеспечивающие манипуляции с графическими
изображениями (битовые карты, файлы GIF и тому подобное).
System.Drawing.Printing:
Содержит классы, имеющие отношение к печати и предварительному просмотру
выводимых на печать изображений.
System.Drawing.Design:
Включает некоторые предопределенные диалоговые окна, таблицы свойств и
другие элементы интерфейса, имеющие отношение к расширению пользовательского
интерфейса времени проектирования.
System.Drawing.Text:
Включает классы для выполнения более сложных манипуляций со шрифтами и
семействами шрифтов.
414
context — DC). DC сохраняет информацию об определенном устройстве и может
транслировать вызовы функций программного интерфейса GDI в конкретные команды,
направляемые устройствам. Вы также можете опросить контекст устройства на предмет
того, какие возможности он предоставляет (например, может ли принтер печатать в
цвете или же только в черно-белом изображении), дабы соответствующим образом
откорректировать вывод. Если вы пытаетесь заставить устройство делать что-то такое,
что оно не способно сделать, то DC обычно обнаруживает это и предпринимает
соответствующие действия (которые, в зависимости от ситуации, могут означать
генерацию исключения либо модификацию запроса таким образом, чтобы получить как
можно более близкий результат в рамках возможностей данного устройства).
Однако DC не только имеет дело с аппаратным устройством. Он служит в
качестве моста между приложением и Windows, и принимает во внимание любые
требования и ограничения, налагаемые на рисование Windows. Например, если
Windows знает, что необходимо перерисовать лишь часть окна вашего приложения, DC
перехватит и отменит попытки рисования вне этой области. Благодаря связи DC с
Windows, работа через контекст устройств может упростить ваш код и в других
отношениях.
Например, аппаратным устройствам необходимо сообщать, где следует рисовать
объекты, и обычно им нужны координаты, отсчитываемые относительно верхнего
левого угла экрана (или другого выходного устройства). Однако приложения обычно
отображают нечто в клиентской области (области, зарезервированной для рисования)
собственного окна, возможно, используя собственную систему координат. Поскольку
окно может быть позиционировано в любом месте экрана, и пользователь в любой
момент может его переместить, преобразования между этими системами координат
могут оказаться непростой задачей. Однако DC всегда знает, где находится ваше окно,
и может выполнять такие преобразования автоматически.
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
415
Рис. 2. 1. Создание нового проекта
416
В поле Имя вводим LWP14SimpleRasterEditor — это название программы
(выбрано по названию лабораторного практикума, номеру и названию работы). В поле
Расположение указана конечная директория, где будет находиться весь проект.
Выберем расположение удобное для быстрого поиска. В поле Имя решения вводится
либо название программы «по умолчанию» из поля Имя автоматически, либо можно
ввести своё собственное. Под этим именем будет создана конечная папка проекта (если
Имя и Имя решения разные).
417
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
418
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
419
^ Необходим файл значка *.ico.
Size изменим со значений 300; 300 на 800;
600
^ Поменяем размер формы.
ToolStrip ( );
MenuStrip ( );
StatusStrip ( ).
420
Рис. 3. 1. Модифицированная форма приложения и расстановка необходимых
элементов управления
421
Рис. 3. 2. Свойства: элемент PictureBox, свойство Dock
422
Рис. 3. 2. Свойства: элемент PictureBox, свойство BackColor
423
Рис. 3. 3. Модифицированная форма приложения и расстановка необходимых
элементов управления и PictureBox
424
Рис. 3. 4. Модифицированная форма приложения и окончательная расстановка
необходимых элементов управления и PictureBox
425
Для перемещения разделителя, например, наверх списка элементов меню нужно
выделить разделитель левой кнопкой мыши и не отпуская клавишу перетащить в
нужном направлении:
MenuItem:
(Name): выбратьРисунокToolStripMenuItem
Text: Выбрать рисунок
ToolTipText: Выбрать рисунков для загрузки в рабочее
поле
MenuItem:
(Name): сохранитьКакРисунокToolStripMenuItem
Text: Сохранить как...
ToolTipText: Сохранить изображение в рабочем поле
как...
MenuItem:
(Name): выходКакРисунокToolStripMenuItem
Text: Выход
426
ToolTipText: Выйти из приложения
MenuItem:
(Name): очиститьToolStripMenuItem
Text: Очистить
ToolTipText: Очистить рабочее поле
Как и раньше, свойство (Name) оставлено без изменений. Имя было выбрано
автоматически.
MenuItem:
(Name): линияToolStripMenuItem
Text: Линия
ToolTipText: Режим рисования линии
MenuItem:
(Name): прямоугольникToolStripMenuItem
Text: Прямоугольник
ToolTipText: Режим рисования прямоугольника
MenuItem:
(Name): непрерывнаяToolStripMenuItem
Text: Непрерывная
ToolTipText: Режим непрерывной линии
427
MenuItem:
(Name): цветПераToolStripMenuItem
Text: Цвет пера
ToolTipText: Установить цвет пера для линий и границ
фигур
MenuItem:
(Name): цветЗаливкиToolStripMenuItem
Text: Цвет заливки
ToolTipText: Установить цвет заливки фигур
MenuItem:
(Name): толщинаПераToolStripMenuItem
Text: Толщина пера
ToolTipText: Введите значение толщины пера (по
умолчанию: 1)
Перейдём к нижнему меню (ToolStrip). Здесь всё просто. Все кнопки этого меню
по функциям будут повторять кнопки верхнего меню. А точнее нажатие на кнопку
нижнего меню, будет приводить к нажатию кнопки верхнего меню. Мы добавим нижнее
меню просто для наглядности.
Для добавления элемента в меню ToolStrip нужно выделить это меню и далее в
специальном выдающем списке выбрать нужны элемент для добавления:
428
На панели ToolStrip создаём кнопку Сохранить как... (вторая на панели) со
следующими свойствами:
Button:
(Name): сохранитьКакToolStripButton
Text: Сохранить как...
ToolTipText: Сохранить как...
DisplayStyle: Image
Рис. 3. 5. Выбор ресурса: окно выбора файла ресурса изображения для кнопки в
ToolStrip
429
В нашем случае этот файл выглядит так:
Button:
(Name): выбратьРисунокToolStripButton
Text: Выбрать рисунок
ToolTipText: Выбрать рисунок
430
DisplayStyle: Image
Button:
(Name): выходToolStripButton
Text: Выход
ToolTipText: Выход
DisplayStyle: Image
Button:
(Name): очиститьToolStripButton
Text: Очистить
ToolTipText: Очистить
DisplayStyle: Image
Button:
(Name): линияToolStripButton
Text: Л
ToolTipText: Выход
DisplayStyle: Text
Button:
(Name): прямоугольникToolStripButton
Text: П
ToolTipText: Прямоугольник
DisplayStyle: Text
Button:
(Name): окружностьToolStripButton
Text: О
ToolTipText: Окружность
DisplayStyle: Text
Button:
(Name): цветПераToolStripButton
Text: Цвет пера
ToolTipText: Цвет пера
DisplayStyle: Image
Button:
(Name): цветЗаливкиToolStripButton
Text: Цвет заливки
ToolTipText: Цвет заливки
DisplayStyle: Image
StatusLabel:
(Name): StatusLabel
Text: Приложение готово к работе
431
4. Модификация приложения Windows Forms: функциональность растрового
редактора
Событие Click для элемента меню (Выбрать рисунок) можно создать просто
дважды кликнув на элементе меню. Также можно выбрать этот элемент меню, далее на
панели Свойства справа перейти на вкладку События и далее ввести имя метода для
события Click, либо просто дважды клинуть по Click (Действия):
432
Рис. 3. 5. Свойства: Событие Click элемента меню Выбрать рисунок
433
Событие Click кнопки Сохранить как...:
434
PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox
StatusLabel.Text = "Рабочее поле очищено";
}
if (!DrawLine)
{
DrawLine = true;
линияToolStripMenuItem.Checked = true;
DrawRect = false;
прямоугольникToolStripMenuItem.Checked = false;
DrawCirc = false;
окружностьToolStripMenuItem.Checked = false;
StatusLabel.Text = "Включен режим рисования линии";
}
}
if (!DrawRect)
{
DrawLine = false;
линияToolStripMenuItem.Checked = false;
DrawRect = true;
прямоугольникToolStripMenuItem.Checked = true;
DrawCirc = false;
окружностьToolStripMenuItem.Checked = false;
StatusLabel.Text = "Включен режим рисования прямоугольника";
}
}
if (!DrawCirc)
{
DrawLine = false;
линияToolStripMenuItem.Checked = false;
DrawRect = false;
прямоугольникToolStripMenuItem.Checked = false;
DrawCirc = true;
окружностьToolStripMenuItem.Checked = true;
StatusLabel.Text = "Включен режим рисования окружности";
}
}
435
Кнопка «Непрерывная» включает режим рисования линий при котором
предыдущая линия становится началом следующей. Получается ломаная линия.
Событие Click этой кнопки выглядит так:
if (CD_Pen.ShowDialog() == DialogResult.OK)
{
Color_Pen = CD_Pen.Color;
Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;
}
else
{
Main_Pen = new Pen(Color.Black, Convert.ToInt16(X));
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;
}
}
436
текст внутри поля. Само поле предназначено для ввода числа отвечающего за толщину
пера (положительное значение от 0 и до примерно 1000 пикселей0; после 1000
значение не нельзя будет заменить разницы):
437
сохранитьКакToolStripMenuItem.PerformClick();
}
Найдём:
Добавим после:
// Диалоги
438
OpenFileDialog OFD_Picture;
SaveFileDialog SFD_Picture;
ColorDialog CD_Pen;
ColorDialog CD_Fill;
// Рабочее поле
Image image;
Graphics graphics;
Bitmap bitmap;
// Инструменты рисования
Pen Main_Pen;
Color Color_Pen;
Color Color_Fill;
// Объекты рисования
Point FirstPoint = new Point();
Point ToPoint = new Point();
Point LastPoint = new Point();
Rectangle SelectRect = new Rectangle();
Rectangle CircleRect = new Rectangle();
Rectangle LightRect = new Rectangle();
SolidBrush Fill;
// Вспомогательные переменные
Boolean DrawLine;
Boolean CurveLine;
Boolean DrawRect;
Boolean DrawCirc;
Double X;
public LWP14Main()
{
InitializeComponent();
// Инициализируем диалоги
OFD_Picture = new OpenFileDialog();
OFD_Picture.Filter = "Файлы изображений (*.bmp, *.jpg, *.gif, *.tif, *.png,
*.ico, *.emf, *.wmf)|*.bmp;*.jpg;*.gif; *.tif; *.png; *.ico; *.emf; *.wmf";
SFD_Picture = new SaveFileDialog();
SFD_Picture.Title = "Сохранить как";
SFD_Picture.OverwritePrompt = true;
SFD_Picture.CheckPathExists = true;
SFD_Picture.Filter = "Изображение в формате PNG|*.png|" + "Изображение в
формате JPEG|*.jpg|" + "Изображение в формате BMP|*.bmp|" + "Изображение в формате GIF|
*.gif|" + "Изображение в формате TIF|*.tif";
CD_Pen = new ColorDialog();
CD_Fill = new ColorDialog();
// Инициализируем рабочее поле
// Создаём пустой Bitmap на основе размеров PictureBox
bitmap = new Bitmap(PB_Bitmap.Size.Width, PB_Bitmap.Size.Height);
// Инициализируем фон для поля рисования
graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White); // Задаём белый цвет фона, иначе фон будет
прозрачным
//graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; //
Выключаем сглаживание
PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox
// Инициализируем инструменты рисования по умолчанию
Main_Pen = new Pen(Color.Black, 1);
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor; // Не задаём
маркер на конце пера
//graphics.DrawLine(Main_Pen, 10, 10, 11, 10); // Рисует точку в один
пиксель, при отсутствии маркера на Pen и толщине в 1
Color_Pen = Color.Black;
Color_Fill = Color.Violet;
Fill = new SolidBrush(Color_Fill);
439
// Инициализируем объекты рисования...
// ...нету их :)
// Инициализируем вспомогательные переменные по умолчанию
DrawLine = true;
линияToolStripMenuItem.Checked = true;
линияToolStripButton.Checked = true;
CurveLine = false;
DrawRect = false;
прямоугольникToolStripButton.Checked = false;
DrawCirc = false;
окружностьToolStripButton.Checked = false;
X = 1;
// Инициализируем прочее
StatusLabel.Text = "Приложение готово к работе";
}
Рисование любого объекта в приложении проходит в три этапа. Первый этап это
получение координат начала (начальной точки), затем промежуточный второй этап
(движение мыши по рабочей зоне) и третий этап получения координат окончания
(конечной точки) и сам процесс отрисовки объекта.
if (CurveLine)
{
FirstPoint = LastPoint;
440
}
}
// Если рисуем прямоугольник
if (DrawRect)
{
SelectRect.Width = 0;
SelectRect.Height = 0;
SelectRect.X = e.X;
SelectRect.Y = e.Y;
}
// Если рисуем окружность
if (DrawCirc)
{
CircleRect.Height = 0;
CircleRect.Width = 0;
CircleRect.X = e.X;
CircleRect.Y = e.Y;
}
}
}
if (e.Button == MouseButtons.Right)
{
LightRect.Width = e.X - LightRect.X;
LightRect.Height = e.Y - LightRect.Y;
}
if (e.Button == MouseButtons.Left)
{
// Если рисуем линию, отображаем заготовку линии
if (DrawLine)
{
// Отображаем заготовку линии до тех пор пока не отпустим левую
кнопку мыши
ControlPaint.DrawReversibleLine(PB_Bitmap.PointToScreen(FirstPoint),
PB_Bitmap.PointToScreen(ToPoint), Color.Black);
ToPoint = new Point(e.X, e.Y);
ControlPaint.DrawReversibleLine(PB_Bitmap.PointToScreen(FirstPoint),
PB_Bitmap.PointToScreen(ToPoint), Color.Black);
if (CurveLine) // Убираем возможные "артефакты", возникающие при
отрисовки непрерывной линии
PB_Bitmap.Refresh();
}
// Если рисуем прямоугольник, отображаем заготовку прямоугольника
пунктирными линиями
if (DrawRect)
{
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(SelectRect), Color.Black,
FrameStyle.Dashed);
SelectRect.Width = e.X - SelectRect.X; // Получаем значение ширины
прямоугольника
SelectRect.Height = e.Y - SelectRect.Y; // Получаем значение высоты
прямоугольника
441
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(SelectRect), Color.Black,
FrameStyle.Dashed);
//PB_Bitmap.Refresh();
}
// Если рисуем окружность, отображаем заготовку окружности пунктирными
линиями
if (DrawCirc)
{
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(CircleRect), Color.Black,
FrameStyle.Dashed);
CircleRect.Width = e.X - CircleRect.X;
CircleRect.Height = e.Y - CircleRect.Y;
ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(CircleRect), Color.Black,
FrameStyle.Dashed);
//PB_Bitmap.Refresh();
}
}
}
if (e.Button == MouseButtons.Right)
{
graphics = Graphics.FromImage(PB_Bitmap.Image);
graphics.DrawRectangle(Main_Pen, LightRect); // Нарисуем прямоугольник-
контур для осветлённой области
graphics.Dispose();
PB_Bitmap.Invalidate();
}
if (DrawLine)
{
LastPoint.X = e.X;
LastPoint.Y = e.Y;
graphics.DrawLine(Main_Pen, FirstPoint, LastPoint);
}
if (DrawRect)
{
if (Color_Fill != Color.Violet) { graphics.FillRectangle(Fill,
SelectRect); } // Заполнение цветом прямоугольной области ограниченной SelectRect
graphics.DrawRectangle(Main_Pen, SelectRect);
}
if (DrawCirc)
{
if (Color_Fill != Color.Violet) { graphics.FillEllipse(Fill,
CircleRect); } // Заполнение цветом эллептической области ограниченной CircleRect
graphics.DrawEllipse(Main_Pen, CircleRect);
}
graphics.Dispose();
442
PB_Bitmap.Invalidate(); // Обновляем PictureBox
}
}
if (e.Button == MouseButtons.Right)
{
StatusLabel.Text = "Произведено осветление зоны";
Найдём:
443
Boolean DrawCirc;
Double X;
Добавим после:
// Ластик
Pen Pen_Erase;
Boolean DrawErase;
SolidBrush Erase;
Rectangle EraseRect = new Rectangle();
Найдём:
// Инициализируем прочее
StatusLabel.Text = "Приложение готово к работе";
Добавим после:
// Ластик
Pen_Erase = new Pen(Color.White, 1);
DrawErase = false;
Erase = new SolidBrush(Color.White);
Найдём:
Добавим после:
// Ластик
if (e.Button == MouseButtons.Right && DrawErase == true)
{
EraseRect.X = e.X - Convert.ToInt32(X);
EraseRect.Y = e.Y - Convert.ToInt32(X);
EraseRect.Width = Convert.ToInt32(X) * 2;
EraseRect.Height = Convert.ToInt32(X) * 2;
graphics = Graphics.FromImage(PB_Bitmap.Image);
graphics.FillEllipse(Erase, EraseRect);
graphics.Dispose();
PB_Bitmap.Invalidate();
StatusLabel.Text = "Режим ластика/карандаша";
}
444
А также инициализируем два события главной формы (отлов нажатия и отжатия
клавиш левый и правый Shift): KeyDown и KeyUp:
5. Завершающая часть
445
Рис. 5. 2. Результат работы приложения: изображение Рисунок.gif, открытое в
стандартном редакторе изображений Windows 7
446
Рис. 5. 3. Модифицированное приложение Windows Forms: загружаем сторонний
рисунок
И осветляем его:
447
Рис. 5. 4. Модифицированное приложение Windows Forms: осветляем сторонний
рисунок
448
Рис. 5. 5. Модифицированное приложение Windows Forms: применяем
«ластик/карандаш» с толщиной пера в 25 пикселей (Shift+ПКМ) на заново открытом
рисунке
Содержание
28.Вводная часть
449
29.Создание решения, приложения Windows Forms и библиотеки классов
30.Модификация приложения Windows Forms: подготовка интерфейса
редактора и добавление файлов ресурсов
31.О будущей функциональности векторного редактора изображений
32.Модификация приложения Windows Forms: подготовка библиотеки
классов
33.Модификация приложения Windows Forms: функциональность
векторного редактора
34.Завершающая часть
35.О приложении к Лабораторной работе № 15
1. Вводная часть
Заходя вперёд скажем, что наше решение будет содержать два проекта:
LWP15Draw— Приложение Windows Forms и LWP15Toolkit — Библиотека классов.
LWP15Tools реализует функциональность приложения, а LWP15Toolkit содержит классы
для управления документами.
Для начала, надо создать решение, для этого выполним последовательно: Файл
-> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
450
Выберем слева в пункте Установленные шаблоны подпункт Другие типы
проектов и далее Решения Visual Studio, далее найдём в списке Новое решение. В
поле Имя вводим LWP15. Так будет назваться общая директория под два будущих
проекта.
Заполним его первым проектом. Выполним последовательно Файл -> Создать ->
Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N).
451
Рис. 2. 2. Окно создания нового проекта (проекта приложения Windows Forms)
452
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
453
Рис. 2. 4. Вводим данные нового проекта библиотеки классов
454
Выберем также, какой проект считать главным и запускать в режиме отладки. В
обозревателе решений нажмём на имя решения ( ).
Перейдём вниз на панель Свойства. Параметр для пункта Запускаемый проект
ставим LWP15Draw.
455
Так как наша программа будет похожа на редактор изображений, нам
необходимы все атрибуты такого редактора. У нас будет верхнее меню для навигации и
нижняя строка состояния для отображения подсказок.
ToolStrip ( );
MenuStrip ( );
StatusStrip ( ).
456
То, что должно получиться в итоге, показано на рисунке ниже:
457
Рис. 3. 2. Добавление нового элемента – LWP15Draw: Пользовательский элемент
управления
DrawArea.cs:
(Name): DrawArea
BackColor: White
Наше поле для рисования готово. Добавим его на форму позже. А пока, нам
необходимы файлы ресурсов. Ими станут файлы изображений для курсора и панели
инструментов.
458
Начнём с изображений для курсора. Курсоров будет пять. Для создания курсора
выполним: Проект -> Добавить новый элемент... (Ctrl+Shift+A). В открывшемся
окне ищем Файл курсора.
Первый курсор для рисования линий. Имя: C_Line.cur. Вписываем это имя и
жмём Добавить. Откроется редактор курсоров. Рисуем нечто подобное:
459
Третий курсор для рисования прямоугольников. Имя: C_Rectangle.cur. Рисуем
нечто подобное:
460
ПРИМЕЧАНИЕ № 3: Очень важно для каждого добавленного курсора (для
файла курсора *.cur в обозревателе решений ИЗМЕНИТЬ свойство Действие по
построению на значение Внедрённый ресурс. Если этого не сделать, могут
возникнуть ситуация при которой приложение будет компилироваться, но работать
некорректно (будет отсутствовать функциональность рисования).
Получим следующее:
461
Рис. 3. 5. Редактор коллекции элементов для toolStrip1
(Name): выделениеToolStripButton
Text: &Выделение
ToolTipText: Выделение
Image
Импортируем иконку
462
Для вставки изображения, выделяем свойство Image и жмём «...» справа в поле
значения:
(Name): карандашToolStripButton
Text: &Карандаш
ToolTipText: Карандаш
Image:
Импортируем иконку
(Name): линияToolStripButton
Text: &Линия
ToolTipText: Линия
Image:
Импортируем иконку
(Name): эллипсToolStripButton
Text: &Эллипс
ToolTipText: Эллипс
Image:
Импортируем иконку
(Name): прямоугольникToolStripButton
Text: &Прямоугольник
ToolTipText: Прямоугольник
Image:
Импортируем иконку
(Name): отменитьToolStripButton
Text: &Отменить
ToolTipText: Отменить
463
Image:
Импортируем иконку
(Name): вернутьToolStripButton
Text: В&ернуть
ToolTipText: Вернуть
Image:
Импортируем иконку
Итог:
(Name): рисованиеToolStripMenuItem
Text: Р&исование
464
Рис. 3. 6. Редактируем меню Файл для menuStrip1
465
Для меню Справка итоговые элементы такие:
466
Size изменим со значений 300; 300 на 400;
150
^ Поменяем размер формы.
FormBorderStyle изменим с Sizable на FixedDialog
^ Сделаем окно «неизменяем» по размерам.
StartPosition изменим с WindowsDefaultLocation на
CenterScreen
^ Определим первоначальное положение формы при вызове.
Button:
(Name): B_OK
Text: Закрыть
Label:
(Name): L_About
Text: О программе
AutoSize: False
Size: 250; 65
467
Icon изменим изображение (иконку)
приложения
^ Необходим файл значка *.ico.
Size изменим со значений 300; 300 на 385;
135
^ Поменяем размер формы.
FormBorderStyle изменим с Sizable на FixedDialog
^ Сделаем окно «неизменяем» по размерам.
StartPosition изменим с WindowsDefaultLocation на
CenterScreen
^ Определим первоначальное положение формы при вызове.
Расставим элементы:
Button:
(Name): B_OK
Text: Применить
Button:
(Name): B_Cancel
Text: Отменить
Button:
(Name): B_SelectColor
Text: ...
TextAlign: MiddleCenter
Label:
(Name): label1
Text: Текущий цвет:
Label:
(Name): label2
Text: Толщина пера:
ComboBox:
(Name): CB_PenWodth
DropDownStyle: DropDownList
Label:
(Name): L_Color
BorderStyle: Fixed3D
TextAlign: MiddleCenter
Text:
Label:
(Name): L_PenWidth
468
BorderStyle: Fixed3D
TextAlign: MiddleCenter
Text:
StatusLabel:
(Name): toolStripStatusLabel
Text: Строка состояния
Приготовления завершены.
469
Рис. 3. 10. Модифицированная форма приложения и расстановка необходимых
элементов управления
4. О будущей функциональности векторного редактора изображений
LWP15Tools:
LWP15Draw:
470
DrawEllipise — рисование графического объекта эллипса.
DrawLine — рисование графического объекта линии.
DrawPolygon — рисование графического объекта непрерывной
линии/карандаш.
Tool — абстрактный базовый класс для всех инструментов рисования.
ToolPointer — указатель инструмента (нейтральный инструмент). Содержит
реализации для выбора, перемещения, изменения размера графических
объектов.
ToolObject — абстрактный базовый класс для всех инструментов, создающих
новый графический объект.
ToolRectangle — реализует инструмент «прямоугольник».
ToolEllipse — реализует инструмент «эллипс».
ToolLine — реализует инструмент «линия».
ToolPolygon — реализует инструмент «непрерывная линия/карандаш».
Сериализация:
Число объектов
Имя типа
Объект
Имя типа
Объект
...
Имя типа
Объект
471
foreach (DrawObject o in graphicsList)
{
// Тип объекта
info.AddValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}",
entryType, i),
o.GetType().FullName);
// Сам объект
o.SaveToStream(info, i);
i++;
}
}
// Загружаем из потока
protected GraphicsList(SerializationInfo info, StreamingContext context)
{
graphicsList = new ArrayList();
// Число объектов
int n = info.GetInt32(entryCount);
string typeName;
object drawObject;
472
protected override bool PointInObject(Point point)
{
GraphicsPath areaPath;
Pen areaPen;
Region areaRegion;
// Создаём путь, который содержит широкую линию
// Для лёгкого выбора мышью
AreaPath = new GraphicsPath();
AreaPen = new Pen(Color.Black, 7);
AreaPath.AddLine(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
// startPoint и endPoint - принадлежат типу Point
AreaPath.Widen(AreaPen);
// Создаём область из пути
AreaRegion = new Region(AreaPath);
return AreaRegion.IsVisible(point);
}
menuDrawPointer.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Pointer);
menuDrawRectangle.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Rectangle);
menuDrawEllipse.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Ellipse);
menuDrawLine.Checked = (drawArea.ActiveTool == DrawArea.DrawToolType.Line);
473
menuDrawPolygon.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Polygon);
// ...
}
// Инструмент "Прямоугольник" выбран
private void CommandRectangle()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Rectangle;
}
474
#region Класс DocManagerData
...
#endregion
Этот код формирует из участков кода файла блоки, которые можно закрыть
нажав на «минус» слева от строчи #region и развернуть нажав «плюс» слева от
свёрнутого элемента. Выглядит это так:
/// <summary>
/// Открываем документ
/// </summary>
/// <param name="newFileName">
/// Имя файла документа. Empty - функция выводит OpenFileDialog
/// </param>
/// <returns></returns>
/// <summary>
/// Инициализация
/// </summary>
/// <param name="data"></param>
public DocManager(DocManagerData data)
{
475
data.FileDialogFilter = "Файлы LWP15Draw (*.lwp)|*.lwp|Все файлы (*.*)|*.*";
data.NewDocName = "New.lwp";
data.RegistryPath = registryPath;
Для начала определим все необходимые для работы приложения класса внутри
самого приложения.
476
мышь на такую точку, курсор будет изменён (зависит от типа точки и объекта
рисования). Нажатие на ключевую точку обеспечивает операцию изменения размеров
объекта либо перемещения (зависит от объекта рисования). Любой объект во время
выделения мышью подсвечивается ключевыми точками. Например, нарисованный и
выделенный прямоугольник имеет 8 ключевых точек:
public LWP15Properties()
{
InitializeComponent();
}
Добавим после:
using System.Globalization;
Найдём:
namespace LWP15Draw
{
public partial class LWP15Properties : Form
{
Изменим на:
namespace LWP15Draw
{
477
partial class LWP15Properties : Form
{
if (properties.PenWidth.HasValue)
{
int penWidth = properties.PenWidth.Value;
if (penWidth < 1) penWidth = 1;
if (penWidth > maxWidth) penWidth = maxWidth;
label2.Text = penWidth.ToString(CultureInfo.InvariantCulture);
CB_PenWidth.SelectedIndex = penWidth - 1;
}
else { label2.Text = undefined; }
}
478
ColorDialog dlg = new ColorDialog();
dlg.Color = L_Color.BackColor;
if (dlg.ShowDialog(this) == DialogResult.OK)
{
L_Color.BackColor = dlg.Color;
L_Color.Text = "";
}
}
Файл для класса назовём GraphicsList.cs, код файла будет таким: [искомый код
можно найти в приложении к данной лабораторной работе в (описания можно того или
иного приложения можно посмотреть в пунтке № 8 протокола работы), а именно
необходимо открыть Приложение № 3 (Файлы классов Graphics...)].
479
в пунтке № 8 протокола работы), а именно необходимо открыть Приложение № 4
(Файлы классов Command...)].
Код файла для реализации класса: [искомый код можно найти в приложении к
данной лабораторной работе в (описания можно того или иного приложения можно
480
посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть
Приложение № 2 (Файлы классов Draw...)].
public DrawLine(int x1, int y1, int x2, int y2) : base()
{
startPoint.X = x1;
startPoint.Y = y1;
endPoint.X = x2;
endPoint.Y = y2;
Initialize();
}
/// <summary>
/// Главная функция рисования линии на форме
/// </summary>
public override void Draw(Graphics g)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
Pen pen = new Pen(Color, PenWidth);
g.DrawLine(pen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
pen.Dispose();
}
Код файла для реализации класса: [искомый код можно найти в приложении к
данной лабораторной работе в (описания можно того или иного приложения можно
посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть
Приложение № 2 (Файлы классов Draw...)].
481
объектом, область для перемещения которого ограничена стронами прямоугольника.
Для перемещения, как и в случае с линией нужно выделить объект (в любом месте
прямоугольника) и зажать левую кнопку мыши.
Код файла для реализации класса: [искомый код можно найти в приложении к
данной лабораторной работе в (описания можно того или иного приложения можно
посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть
Приложение № 2 (Файлы классов Draw...)].
/// <summary>
/// Главная функция рисования эллипса на форме
/// </summary>
/// <param name="g"></param>
public override void Draw(Graphics g)
{
Pen pen = new Pen(Color, PenWidth);
g.DrawEllipse(pen, DrawRectangle.GetNormalizedRectangle(Rectangle));
pen.Dispose();
}
482
{
// Добавляем новую точку
newPolygon.AddPoint(point);
lastX = e.X;
lastY = e.Y;
}
Код файла для реализации класса: [искомый код можно найти в приложении к
данной лабораторной работе в (описания можно того или иного приложения можно
посмотреть в пунтке № 8 протокола работы), а именно необходимо открыть
Приложение № 2 (Файлы классов Draw...)].
/// <summary>
/// Добавление нового объекта в область рисования.
/// Функция вызывается когда пользователь нажимает ЛКМ на области рисования,
/// и один из полученных ToolObject-инструментов активен.
/// </summary>
/// <param name="drawArea"></param>
/// <param name="o"></param>
protected void AddNewObject(DrawArea drawArea, DrawObject o)
{
drawArea.GraphicsList.UnselectAll();
o.Selected = true;
drawArea.GraphicsList.Add(o);
drawArea.Capture = true;
drawArea.Refresh();
drawArea.SetDirty();
}
483
public override void OnMouseDown(DrawArea drawArea, MouseEventArgs e)
{
AddNewObject(drawArea, new DrawLine(e.X, e.Y, e.X + 1, e.Y + 1));
}
484
перемещением объекта, изменением размеров объекта и чистым выделением объект(-
ов). Этот инструмент также управляет пунктирным прямоугольником выделения:
if (selectMode == SelectionMode.NetSelection)
{
// Удаляем прямоугольник предыдущего выделения
ControlPaint.DrawReversibleFrame(
drawArea.RectangleToScreen(DrawRectangle.GetNormalizedRectangle(startPoint, oldPoint)),
Color.Black,
FrameStyle.Dashed);
// Рисуем прямоугольник нового выделения
ControlPaint.DrawReversibleFrame(
drawArea.RectangleToScreen(DrawRectangle.GetNormalizedRectangle(startPoint, point)),
Color.Black,
FrameStyle.Dashed);
return;
}
Первым делом «добьём» форму LWP15About. Событие Load для формы будет
таким:
485
перехватываются инструментами Tool... и реализуют то или иной действие в
зависимости от активного инструмента и действия с мышью. Сам элемент будет
растягиваться до строки состояния внизу, границ с боку формы и панели инструментов
сверху. Абсолютное значение размеров будет влиять лишь на доступную для
размещения объектов область. Эти размеры будут важны лишь при создании
растрового изображения (об этом в конце данного материала) на основе графики в
элементе.
В коде находим:
namespace LWP15Draw
{
public partial class DrawArea : UserControl
486
{
public DrawArea()
{
InitializeComponent();
}
}
namespace LWP15Draw
{
partial class DrawArea : UserControl
{
#region Конструктор
public DrawArea()
{
InitializeComponent();
}
#endregion
#region Перечисления
public enum DrawToolType
{
Pointer, Rectangle, Ellipse, Line, Polygon, NumberOfDrawTools
};
#endregion
#region Члены
private GraphicsList graphicsList; // Список объектов рисования
private DrawToolType activeTool; // Активный инструмент рисования
private Tool[] tools; // Массив инструментов
private LWP15Main owner;
private DocManager docManager;
private ContextMenuStrip m_ContextMenu;
private UndoManager undoManager;
#endregion
#region Свойства
/// <summary>
/// Ссылка на владельца формы
/// </summary>
public LWP15Main Owner
{
get { return owner; }
set { owner = value; }
}
/// <summary>
/// Ссылка на DocManager
/// </summary>
public DocManager DocManager
{
get { return docManager; }
set
{
docManager = value;
}
}
/// <summary>
/// Активный инструмент рисования
/// </summary>
public DrawToolType ActiveTool
{
487
get { return activeTool; }
set { activeTool = value; }
}
/// <summary>
/// Список объектов рисования
/// </summary>
public GraphicsList GraphicsList
{
get { return graphicsList; }
set
{
graphicsList = value;
undoManager = new UndoManager(graphicsList);
}
/// <summary>
/// true - если операция отмены возможна
/// </summary>
public bool CanUndo
{
get
{
if (undoManager != null) { return undoManager.CanUndo; }
return false;
}
}
/// <summary>
/// true - если операция возврата возможна
/// </summary>
public bool CanRedo
{
get
{
if (undoManager != null) { return undoManager.CanRedo; }
return false;
}
}
#endregion
/// <summary>
/// Инициализация
/// </summary>
/// <param name="owner"></param>
/// <param name="docManager"></param>
public void Initialize(LWP15Main owner, DocManager docManager)
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
// Сохраняем ссылку на владельца формы
this.Owner = owner;
this.DocManager = docManager;
// Устанавливаем инструмент по умолчанию
activeTool = DrawToolType.Pointer;
// Создаём список графических объектов
graphicsList = new GraphicsList();
// Создаём экземпляр UndoManager для текущего файла
undoManager = new UndoManager(graphicsList);
// Создаём массив инструментов рисования
tools = new Tool[(int)DrawToolType.NumberOfDrawTools];
488
tools[(int)DrawToolType.Pointer] = new ToolPointer();
tools[(int)DrawToolType.Rectangle] = new ToolRectangle();
tools[(int)DrawToolType.Ellipse] = new ToolEllipse();
tools[(int)DrawToolType.Line] = new ToolLine();
tools[(int)DrawToolType.Polygon] = new ToolPolygon();
}
/// <summary>
/// Добавления команды в историю
/// </summary>
public void AddCommandToHistory(Command command)
{
undoManager.AddCommandToHistory(command);
}
/// <summary>
/// Очистка истории
/// </summary>
public void ClearHistory()
{
undoManager.ClearHistory();
}
/// <summary>
/// Отменить
/// </summary>
public void Undo()
{
undoManager.Undo();
Refresh();
}
/// <summary>
/// Вернуть
/// </summary>
public void Redo()
{
undoManager.Redo();
Refresh();
}
/// <summary>
/// Устанавливаем флаг "грязный" (файл был изменён после последней операции
сохранения)
/// </summary>
public void SetDirty()
{
DocManager.Dirty = true;
}
/// <summary>
/// Обработчик нажатия правой кнопки мышки
/// </summary>
/// <param name="e"></param>
private void OnContextMenu(MouseEventArgs e)
{
// Измененяем текущий выбор при необходимости
Point point = new Point(e.X, e.Y);
int n = GraphicsList.Count;
DrawObject o = null;
if (GraphicsList[i].HitTest(point) == 0)
489
{
o = GraphicsList[i];
break;
}
}
if (o != null)
{
if (!o.Selected) GraphicsList.UnselectAll();
// Выбор объекта произведён
o.Selected = true;
}
else
{
GraphicsList.UnselectAll();
}
Refresh(); // В случае изменения выбора
// Выводин контекстное меню (всплывающее).
// Элементы меню вставлены из строки меня, главного элемента "Правка"
m_ContextMenu = new ContextMenuStrip();
int nItems = owner.ContextParent.DropDownItems.Count;
// Получаем элементы меню "Правка" и перемещаем их на контекстное-всплывающее
меню.
// Так как каждый шаг уменьшает количество элементов, читая их в обратном
порядке.
// Чтобы получить элементы в прямом порядке, вставим каждый из них в начало
for (int i = nItems - 1; i >= 0; i--)
{
m_ContextMenu.Items.Insert(0, owner.ContextParent.DropDownItems[i]);
}
// Выводит контекстное меню для владельца формы, а также обрабатывает
элементы выбора.
// Преобразует координаты точки в этом окне к координатам владельца
point.X += this.Left;
point.Y += this.Top;
m_ContextMenu.Show(owner, point);
Owner.SetStateOfControls(); // Включение/выключение элементов меню
// Контекстное меню вызвано, но меню "Правка" владельца теперь пусто.
// Подписываемся на событие закрытия контекстного меню и восстанавливаем там
элементы
m_ContextMenu.Closed += delegate(object sender,
ToolStripDropDownClosedEventArgs args)
{
if (m_ContextMenu != null)
{
nItems = m_ContextMenu.Items.Count;
for (int k = nItems - 1; k >= 0; k--)
{ owner.ContextParent.DropDownItems.Insert(0, m_ContextMenu.Items[k]); }
}
};
}
#endregion
}
490
private void DrawArea_Paint(object sender, PaintEventArgs e)
{
SolidBrush brush = new SolidBrush(Color.FromArgb(255, 255, 255));
e.Graphics.FillRectangle(brush, this.ClientRectangle);
if (graphicsList != null) { graphicsList.Draw(e.Graphics); }
//DrawNetSelection(e.Graphics);
brush.Dispose();
}
/// <summary>
/// Событие MouseDown для DrawArea
/// ЛКМ: перехватывается активным инструментом
/// ПКМ: перехватывается и описано в данном классе
/// </summary>
private void DrawArea_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) tools[(int)activeTool].OnMouseDown(this,
e);
else if (e.Button == MouseButtons.Right) OnContextMenu(e);
}
/// <summary>
/// Событие MouseMove для DrawArea
/// Перемещение без нажатия кнопок или с нажатие левой кнопки мыши
перехватываетсмя активным инструментом
/// </summary>
private void DrawArea_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left || e.Button == MouseButtons.None)
tools[(int)activeTool].OnMouseMove(this, e);
else this.Cursor = Cursors.Default;
}
/// <summary>
/// Событие MouseUp для DrawArea
/// ЛКМ: перехватывается активным инструментом
/// </summary>
private void DrawArea_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) tools[(int)activeTool].OnMouseUp(this, e);
}
#endregion
Находим:
491
public partial class LWP15Main : Form
{
Заменяем на:
#region Свойства
/// <summary>
/// Имя файла из командной строки
/// </summary>
public string ArgumentFile
{
get { return argumentFile; }
set { argumentFile = value; }
}
/// <summary>
/// Получаем ссылку на элемент строки меню "Правка".
/// Используется при вызове контекстного-всплывающего меню в классе DrawArea
/// </summary>
/// <value></value>
public ToolStripMenuItem ContextParent
{
get { return правкаToolStripMenuItem; }
}
#endregion
#region Конструктор
public LWP15Main()
{
InitializeComponent();
persistState = new PersistWindowState(registryPath, this);
}
#endregion
492
}
/// <summary>
/// Сохранение документа в поток поставляемый DocManager
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void docManager_SaveEvent(object sender, SerializationEventArgs e)
{
// DocManager просит сохранить документ в поставляемый поток
try
{
e.Formatter.Serialize(e.SerializationStream, drawArea.GraphicsList);
}
catch (ArgumentNullException ex) { HandleSaveException(ex, e); }
catch (SerializationException ex) { HandleSaveException(ex, e); }
catch (SecurityException ex) { HandleSaveException(ex, e); }
}
#endregion
493
drawArea.GraphicsList.SelectAll();
drawArea.Refresh();
}
if (drawArea.GraphicsList.DeleteSelection())
{
drawArea.SetDirty();
drawArea.Refresh();
drawArea.AddCommandToHistory(command);
}
}
if (drawArea.GraphicsList.Clear())
{
drawArea.SetDirty();
drawArea.Refresh();
drawArea.AddCommandToHistory(command);
}
}
if (drawArea.GraphicsList.MoveSelectionToBack())
{
drawArea.SetDirty();
drawArea.Refresh();
}
}
if (drawArea.GraphicsList.MoveSelectionToFront())
{
drawArea.SetDirty();
drawArea.Refresh();
}
}
if (drawArea.GraphicsList.ShowPropertiesDialog(drawArea))
{
drawArea.SetDirty();
drawArea.Refresh();
}
}
494
{
CommandPointer();
}
495
private void эллипсToolStripButton_Click(object sender, EventArgs e)
{
CommandEllipse();
}
Событие Resize:
/// <summary>
/// Изменение размеров DrawArea, Когда меняются размеры формы
496
/// </summary>
private void LWP15Main_Resize(object sender, EventArgs e)
{
if (this.WindowState != FormWindowState.Minimized && drawArea != null)
{ ResizeDrawArea(); }
}
Событие FormClosing:
/// <summary>
/// Событие закрытия формы
/// </summary>
private void LWP15Main_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
if (!docManager.CloseDocument()) e.Cancel = true;
}
SaveSettingsToRegistry();
}
/// <summary>
/// Всплывающая строка меню ("Файл", "Правка" и прочее) открыто
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void LWP15Main_DropDownOpened(object sender, EventArgs e)
{
// Устанавливаем активный инструмент на "Выделение".
// Педотвращает редкий сбой, когда выбран иной инструмент, пользователь
открывает главное менюuser opens
// и после нажатия в DrawArea событие MouseDown не вызывается и MouseMove
работает неправильно
drawArea.ActiveTool = DrawArea.DrawToolType.Pointer;
}
#endregion
497
выделениеToolStripMenuItem.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Pointer);
прямоугольникToolStripMenuItem.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Rectangle);
эллипсToolStripMenuItem.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Ellipse);
линияToolStripMenuItem.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Line);
карандашToolStripMenuItem.Checked = (drawArea.ActiveTool ==
DrawArea.DrawToolType.Polygon);
/// <summary>
/// Установка области рисования формы на основе пространство свободной области,
/// за исключением панели инструментов, строки состояния и строки меню
/// </summary>
private void ResizeDrawArea()
{
Rectangle rect = this.ClientRectangle;
drawArea.Left = rect.Left;
drawArea.Top = rect.Top + menuStrip1.Height + toolStrip1.Height;
drawArea.Width = rect.Width;
drawArea.Height = rect.Height - menuStrip1.Height - toolStrip1.Height -
statusStrip1.Height;
}
/// <summary>
/// Инициализация вспомогательных объектов из библиотеки классов LWP15Tools.
/// </summary>
private void InitializeHelperObjects()
{
// DocManager
DocManagerData data = new DocManagerData();
data.FormOwner = this;
data.UpdateTitle = true;
data.FileDialogFilter = "Файлы LWP15Draw (*.lwp)|*.lwp|Все файлы (*.*)|*.*";
data.NewDocName = "New.lwp";
data.RegistryPath = registryPath;
498
// Делаем "встроенные подписки" с помощью анонимных методов
docManager.OpenEvent += delegate(object sender, OpenFileEventArgs e)
{
// Обновляем список последних файлов
if (e.Succeeded) mruManager.Add(e.FileName);
else mruManager.Remove(e.FileName);
};
try
{
drawArea.Refresh();
drawArea.ClearHistory();
}
catch { }
};
if (drawArea.GraphicsList != null)
{
drawArea.GraphicsList.Clear();
drawArea.ClearHistory();
drawArea.Refresh();
}
};
docManager.NewDocument();
// DragDropManager
dragDropManager = new DragDropManager(this);
dragDropManager.FileDroppedEvent += delegate(object sender,
FileDroppedEventArgs e)
{
OpenDocument(e.FileArray.GetValue(0).ToString());
};
// MruManager
mruManager = new MruManager();
mruManager.Initialize(
this, // Владелец формы (this)
последниеФайлыToolStripMenuItem, // Элемент меню последних исользованных
файлов
файлToolStripMenuItem, // Родительский элемент ("Файл")
registryPath); // Путь в системном реестре для хранения
списка последних файлов
mruManager.MruOpenEvent += delegate(object sender, MruFileOpenEventArgs e)
{
OpenDocument(e.FileName);
};
}
/// <summary>
/// Обрабатываем исключение из функции docManager_LoadEvent
/// </summary>
/// <param name="ex"></param>
/// <param name="fileName"></param>
private void HandleLoadException(Exception ex, SerializationEventArgs e)
{
MessageBox.Show(this,
"Операция открытия файла завершилась неудачей. Имя файла: " + e.FileName
+ "\n" +
"Причина: " + ex.Message,
Application.ProductName);
e.Error = true;
}
499
/// <summary>
/// Обрабатываем исключение из функции docManager_SaveEvent
/// </summary>
/// <param name="ex"></param>
/// <param name="fileName"></param>
private void HandleSaveException(Exception ex, SerializationEventArgs e)
{
MessageBox.Show(this,
"Операция сохранения файла завершилась неудачей. Имя файла: " +
e.FileName + "\n" +
"Причина: " + ex.Message,
Application.ProductName);
e.Error = true;
}
/// <summary>
/// Открытие документа.
/// Используется для открытия файла переданного в командной строке или
"переброшенного" в окно
/// </summary>
/// <param name="file"></param>
public void OpenDocument(string file)
{
docManager.OpenDocument(file);
}
/// <summary>
/// Загрузка настроек приложения из системного реестра
/// </summary>
void LoadSettingsFromRegistry()
{
try
{
RegistryKey key = Registry.CurrentUser.CreateSubKey(registryPath);
DrawObject.LastUsedColor = Color.FromArgb((int)key.GetValue("Color",
Color.Black.ToArgb()));
DrawObject.LastUsedPenWidth = (int)key.GetValue("Width", 1);
}
catch (ArgumentNullException ex) { HandleRegistryException(ex); }
catch (SecurityException ex) { HandleRegistryException(ex); }
catch (ArgumentException ex) { HandleRegistryException(ex); }
catch (ObjectDisposedException ex) { HandleRegistryException(ex); }
catch (UnauthorizedAccessException ex) { HandleRegistryException(ex); }
}
/// <summary>
/// Сохранение настроек приложения в системный реестр
/// </summary>
void SaveSettingsToRegistry()
{
try
{
RegistryKey key = Registry.CurrentUser.CreateSubKey(registryPath);
key.SetValue("Color", DrawObject.LastUsedColor.ToArgb());
key.SetValue("Width", DrawObject.LastUsedPenWidth);
}
catch (SecurityException ex) { HandleRegistryException(ex); }
catch (ArgumentException ex) { HandleRegistryException(ex); }
catch (ObjectDisposedException ex) { HandleRegistryException(ex); }
catch (UnauthorizedAccessException ex) { HandleRegistryException(ex); }
}
500
private void HandleRegistryException(Exception ex)
{
Trace.WriteLine("Выполнение операции с системны реестром завершилась
неудачей: " + ex.Message);
}
/// <summary>
/// Выделение
/// </summary>
private void CommandPointer()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Pointer;
toolStripStatusLabel.Text = "Выбран инструмент \"Выделение\"";
}
/// <summary>
/// Прямоугольник
/// </summary>
private void CommandRectangle()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Rectangle;
toolStripStatusLabel.Text = "Выбран инструмент \"Прямоугольник\"";
}
/// <summary>
/// Эллипс
/// </summary>
private void CommandEllipse()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Ellipse;
toolStripStatusLabel.Text = "Выбран инструмент \"Эллипс\"";
}
/// <summary>
/// Линия
/// </summary>
private void CommandLine()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Line;
toolStripStatusLabel.Text = "Выбран инструмент \"Линия\"";
}
/// <summary>
/// Карандаш
/// </summary>
private void CommandPolygon()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Polygon;
toolStripStatusLabel.Text = "Выбран инструмент \"Карандаш\"";
}
/// <summary>
/// Диалог "О программе..."
/// </summary>
private void CommandAbout()
{
LWP15About frm = new LWP15About();
frm.ShowDialog(this);
toolStripStatusLabel.Text = "Вызвана форма \"О программе\"";
}
/// <summary>
/// Создать
/// </summary>
private void CommandNew()
501
{
docManager.NewDocument();
toolStripStatusLabel.Text = "Создан новый документ";
}
/// <summary>
/// Открыть
/// </summary>
private void CommandOpen()
{
docManager.OpenDocument("");
toolStripStatusLabel.Text = "Документ открыт";
}
/// <summary>
/// Сохранить
/// </summary>
private void CommandSave()
{
docManager.SaveDocument(DocManager.SaveType.Save);
toolStripStatusLabel.Text = "Документ сохранён";
}
/// <summary>
/// Сохранить как
/// </summary>
private void CommandSaveAs()
{
docManager.SaveDocument(DocManager.SaveType.SaveAs);
toolStripStatusLabel.Text = "Документ сохранён";
}
/// <summary>
/// Отменить
/// </summary>
private void CommandUndo()
{
drawArea.Undo();
toolStripStatusLabel.Text = "Произведена операция \"Отменить\"";
}
/// <summary>
/// Вернуть
/// </summary>
private void CommandRedo()
{
drawArea.Redo();
toolStripStatusLabel.Text = "Произведена операция \"Вернуть\"";
}
#endregion
502
Свойства такие:
(Name): сохранитьКакИзображениеToolStripMenuItem
Text: Сохранить как &изображение...
Image:
Импортируем иконку
503
break;
case "gif":
drawAreaBitmap.Save(fileName,
System.Drawing.Imaging.ImageFormat.Gif);
break;
case "tif":
drawAreaBitmap.Save(fileName,
System.Drawing.Imaging.ImageFormat.Tiff);
break;
case "png":
drawAreaBitmap.Save(fileName,
System.Drawing.Imaging.ImageFormat.Png);
break;
default:
break;
}
toolStripStatusLabel.Text = "Изображение " +
System.IO.Path.GetFileName(SFD_Picture.FileName) + " успешно сохранено! Полный путь: " +
SFD_Picture.FileName;
}
SFD_Picture.Dispose();
drawAreaBitmap.Dispose();
}
#endregion
Первое что нужно сделать, это добавить новый класс для рисования объекта
изображения. Назовём его DrawImage (файл DrawImage.cs). За основу возьмём
класс DrawRectangle, так как он наиболее походит нам для реализации нового класса.
Код класс рисования будет таким:
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Runtime.Serialization;
using System.Windows.Forms;
namespace LWP15Draw
{
/// <summary>
/// Рисование графического объекта "Изображение"
/// </summary>
class DrawImage : LWP15Draw.DrawObject
{
private Rectangle rectangle;
private Bitmap _image;
private Bitmap _originalImage; // Оригинальное изображение
504
{
get { return _image; }
set
{
_originalImage = value;
ResizeImage(rectangle.Width, rectangle.Height);
}
}
/// <summary>
/// Клонирование данного экземпляра
/// </summary>
public override DrawObject Clone()
{
DrawImage drawImage = new DrawImage();
drawImage._image = _image;
drawImage._originalImage = _originalImage;
drawImage.rectangle = rectangle;
FillDrawObjectFields(drawImage);
return drawImage;
}
public DrawImage()
{
SetRectangle(0, 0, 1, 1);
Initialize();
}
/// <summary>
/// лавная функция рисования изображения на форме
/// </summary>
/// <param name="g"></param>
public override void Draw(Graphics g)
{
505
Pen pen = new Pen(Color, PenWidth);
if (_image == null)
{
g.DrawRectangle(pen, rectangle);
}
else
{
g.DrawImage(_image, new Point(rectangle.X, rectangle.Y));
g.DrawRectangle(pen, rectangle);
}
/// <summary>
/// Получение ключевых точек изображения
/// </summary>
/// <param name="handleNumber"></param>
/// <returns></returns>
public override Point GetHandle(int handleNumber)
{
int x, y, xCenter, yCenter;
switch (handleNumber)
{
case 1:
x = rectangle.X;
y = rectangle.Y;
break;
case 2:
x = xCenter;
y = rectangle.Y;
break;
case 3:
x = rectangle.Right;
y = rectangle.Y;
break;
case 4:
x = rectangle.Right;
y = yCenter;
break;
case 5:
x = rectangle.Right;
y = rectangle.Bottom;
break;
case 6:
x = xCenter;
506
y = rectangle.Bottom;
break;
case 7:
x = rectangle.X;
y = rectangle.Bottom;
break;
case 8:
x = rectangle.X;
y = yCenter;
break;
}
return new Point(x, y);
}
/// <summary>
/// Проверка нажатия.
/// Возвращаемые параметры: -1 - нет нажатия
/// 0 - нажатие где угодно
/// > 1 - обработка ключевой точки
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public override int HitTest(Point point)
{
if (Selected)
{
/// <summary>
/// Получение курсора для ключевых точек
/// </summary>
/// <param name="handleNumber"></param>
/// <returns></returns>
public override Cursor GetHandleCursor(int handleNumber)
{
switch (handleNumber)
{
case 1:
return Cursors.SizeNWSE;
case 2:
return Cursors.SizeNS;
case 3:
return Cursors.SizeNESW;
case 4:
return Cursors.SizeWE;
case 5:
return Cursors.SizeNWSE;
case 6:
return Cursors.SizeNS;
case 7:
507
return Cursors.SizeNESW;
case 8:
return Cursors.SizeWE;
default:
return Cursors.Default;
}
}
/// <summary>
/// Изменение размеров объекта изображения
/// </summary>
/// <param name="point"></param>
/// <param name="handleNumber"></param>
public override void MoveHandleTo(Point point, int handleNumber)
{
int left = Rectangle.Left;
int top = Rectangle.Top;
int right = Rectangle.Right;
int bottom = Rectangle.Bottom;
switch (handleNumber)
{
case 1:
left = point.X;
top = point.Y;
break;
case 2:
top = point.Y;
break;
case 3:
right = point.X;
top = point.Y;
break;
case 4:
right = point.X;
break;
case 5:
right = point.X;
bottom = point.Y;
break;
case 6:
bottom = point.Y;
break;
case 7:
left = point.X;
bottom = point.Y;
break;
case 8:
left = point.X;
break;
}
Dirty = true;
SetRectangle(left, top, right - left, bottom - top);
ResizeImage(rectangle.Width, rectangle.Height);
}
508
public override bool IntersectsWith(Rectangle rectangle)
{
return Rectangle.IntersectsWith(rectangle);
}
/// <summary>
/// Перемещение объекта изображения
/// </summary>
/// <param name="deltaX"></param>
/// <param name="deltaY"></param>
public override void Move(int deltaX, int deltaY)
{
rectangle.X += deltaX;
rectangle.Y += deltaY;
Dirty = true;
}
/// <summary>
/// Нормализация объекта прямоугольника
/// </summary>
public override void Normalize()
{
rectangle = DrawRectangle.GetNormalizedRectangle(rectangle);
}
/// <summary>
/// Сохранение объекта изображения в потоке сериализации
/// </summary>
/// <param name="info"></param>
/// <param name="orderNumber"></param>
public override void SaveToStream(SerializationInfo info, int orderNumber)
{
info.AddValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}", entryRectangle, orderNumber), rectangle);
info.AddValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}",
entryImage, orderNumber), _image);
info.AddValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}",
entryImageOriginal, orderNumber), _originalImage);
base.SaveToStream(info, orderNumber);
}
/// <summary>
/// Загрузка объекта изображения из потока сериализации
/// </summary>
/// <param name="info"></param>
509
/// <param name="orderNumber"></param>
public override void LoadFromStream(SerializationInfo info, int orderNumber)
{
rectangle = (Rectangle)info.GetValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}", entryRectangle, orderNumber),
typeof(Rectangle));
_image = (Bitmap)info.GetValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}", entryImage, orderNumber),
typeof(Bitmap));
_originalImage = (Bitmap)info.GetValue(
String.Format(CultureInfo.InvariantCulture,
"{0}{1}", entryImageOriginal, orderNumber),
typeof(Bitmap));
base.LoadFromStream(info, orderNumber);
}
using System;
using System.Drawing;
using System.Windows.Forms;
namespace LWP15Draw
{
/// <summary>
/// Изображение
/// </summary>
internal class ToolImage : ToolObject
{
510
public ToolImage()
{
Cursor = new Cursor(GetType(), "C_Image.cur");
}
if (e.Button == MouseButtons.Left)
{
Point point = new Point(e.X, e.Y);
drawArea.GraphicsList[0].MoveHandleTo(point, 5);
drawArea.Refresh();
}
}
if (ofd.ShowDialog() == DialogResult.OK)
{
((DrawImage)drawArea.GraphicsList[0]).TheImage =
(Bitmap)Bitmap.FromFile(ofd.FileName);
}
ofd.Dispose();
base.OnMouseUp(drawArea, e);
}
}
}
511
(Name): изображениеToolStripButton
Text: &Изображение
ToolTipText: Изображение
(Name): изображениеToolStripMenuItem
Text: &Изображение
Image: Импортируем иконку
Добавляем после:
// Прочее
private bool dirty;
/// <summary>
/// ID объекта
/// </summary>
public int ID
{
get { return id; }
set { id = value; }
}
Добавляем после:
/// <summary>
/// true когда объект изменён
/// </summary>
public bool Dirty
{
get { return dirty; }
set { dirty = value; }
}
Заменяем на:
512
tools[(int)DrawToolType.Ellipse] = new ToolEllipse();
tools[(int)DrawToolType.Rectangle] = new ToolRectangle();
Добавляем после:
/// <summary>
/// Изображение
/// </summary>
private void CommandImage()
{
drawArea.ActiveTool = DrawArea.DrawToolType.Image;
toolStripStatusLabel.Text = "Выбран инструмент \"Изображение\"";
}
#endregion
7. Завершающая часть
513
Рис. 7. 1. Модифицированное приложение Windows Forms: рисуем
514
Рис. 7. 2. Модифицированное приложение Windows Forms: открываем ранее
сохранённый файл и просматриваем список последних открытых файлов
515
Рис. 7. 3. Результат работы приложения: изображение Test.gif, открытое в стандартном
редакторе изображений Windows 7
516
Рис. 7. 4. Модифицированное приложение Windows Forms: меняем свойства объектов в
открытом документе Test.lwp
517
8. О приложении к Лабораторной работе № 15
Содержание
36.Вводная часть
37.Создание приложения WCF
38.Модификация приложения WCF: приложение-клиент для WCF-службы
39.Создание приложения Windows Forms: сервер чата на WCF
40.Создание приложения Windows Forms: клиент чата на WCF
41.Завершающая часть
42.О приложении к Лабораторной работе № 16
1. Вводная часть
518
Windows Communication Foundation (WCF) — программный «фреймворк»,
используемый для обмена данными между приложениями входящими в состав .NET
Framework. До своего выпуска в декабре 2006 года в составе .NET Framework 3.0,
WCF был известен под кодовым именем Indigo.
В данной работе будет рассмотрен простейший случай работы с WCF, а также будет
написан клиент и сервер для реализации возможностей чата на основе WCF (оба
приложения будут в виде Windows Forms).
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
519
Рис. 2. 1. Создание нового проекта
520
В поле Имя вводим LWP16WCF — это название программы (выбрано по
названию лабораторного практикума, номеру и названию работы). В поле
Расположение указана конечная директория, где будет находиться весь проект.
Выберем расположение удобное для быстрого поиска. В поле Имя решения вводится
либо название программы «по умолчанию» из поля Имя автоматически, либо можно
ввести своё собственное. Под этим именем будет создана конечная папка проекта (если
Имя и Имя решения разные).
521
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
522
Рис. 2. 5. Запуск приложения WCF по конфигурации Debug (не выбран файл
Service1.svc)
523
Собственно тестирование происходит так: дважды нажмём на GetData(), затем в
поле Запрос -> строка value введём любое число и нажмём Вызвать. В окне
Предупреждение системы безопасности жмём ОК и наслаждается ответом службы
в поле Ответ -> return:
524
Рис. 2. 8. Установщик веб-платформы 3.0: выбор компонентов на вкладке Важный
сайт
525
3. Модификация приложения WCF: приложение-клиент для WCF-службы
Выделим имя класса (слово) Service1 и далее ПКМ -> во всплывающем меню:
Рефакторинг -> Переименовать (или F2):
526
Рис. 3. 2. Переименование сервиса: Просмотр изменений - Переименование
[OperationContract]
string GetData(int value);
Изменим так:
[OperationContract]
527
string GetInt(int value);
[OperationContract]
string GetString(string value);
Для [ServiceContract]:
[ServiceContract(SessionMode = SessionMode.Required)]
Заменим на:
528
Рис. 3. 3. Обозреватель решений: содержимое решения, состоящего из двух проектов
529
Рис. 3. 4. Добавить ссылку на службу: добавление службы из нашего решения в
другой проект этого же решения
На форме клиентского проекта Form1 расставим два текстовых поля, под ними
одну кнопку и ещё ниже два статических поля Label. Все имена и свойства
добавленных элементов оставим без изменений. Задаём следующие параметры самой
формы на панели Свойства:
530
приложения
^ Необходим файл значка *.ico.
FormBorderStyle > изменим с Sizable на FixedDialog
^ Сделаем окно «неизменяем» по размерам.
Size изменим со значений 290; 290 на 350;
200
^ Поменяем размер формы.
Дважды щёлкнем по кнопке на форме, тем самым создав событие Click. Впишем
код:
returnString1 = client.GetInt(Convert.ToInt32(textBox1.Text));
label1.Text = returnString1;
returnString2 = client.GetString(textBox2.Text);
label2.Text = returnString2;
client.Close(); // Закрываем сеанс связи
}
531
public partial class LWP16Main : Form
{
Вставим после:
String returnString1;
String returnString2;
public LWP16Main()
{
InitializeComponent();
label1.Text = "";
label2.Text = "";
textBox1.Text = "Ведите число";
textBox2.Text = "Введите строку";
button1.Text = "Связать со службой";
}
532
Рис. 3. 6. Модифицированное приложение Windows Forms: работа клиента для службы
WCF
Открываем снова Visual Studio и создаём пустое решение (Файл -> Создать ->
Новый проект). Выбираем Другие типы проектов -> Решение Visual Studio ->
Новое решение. Имя выбираем как LWP16:
533
Рис. 4. 1. Вводим данные нового пустого решения
Жмём ОК. Было добавлено решение, но пока без проектов. Создаём новый
проект в текущем решении. Для этого выполним следующие действия: Файл -> Создать
-> Проект. В окне открывшемся окне «Создать проект», в поле Решение, выберем:
Добавить в решение. В качестве проекта выберем Приложение Windows Forms. Имя
будет: LWP16-ChatServer. Жмём ОК. Получим следующее:
534
Рис. 4. 2. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
535
Переименуем форму в обозревателе решений (ПКМ на иконке формы ->
Переименовать). Новое имя формы будет LWP16MainServer. После переименования,
автоматически изменится свойство (Name) формы.
Button:
(Name): B_Start
Text: Запуск
Button:
(Name): B_Stop
Text: Остановка
GroupBox:
(Name): GB_1
Text: Серверные операции
Label:
(Name): StatusLabel
Text: Состояние сервера
536
private void B_Start_Click(object sender, EventArgs e)
{
try
{
cprs = new CustomPeerResolverService(); // Инициалазируем службу узлов
cprs.RefreshInterval = TimeSpan.FromSeconds(5); // Время в секундах
обновления таблицы записей распознователя одноранговых узлов
host = new ServiceHost(cprs); // Передаём серверу службу узлов
cprs.ControlShape = true; // Инициализируем совместное использование
ссылок
cprs.Open(); // Открываем службу
host.Open(TimeSpan.FromDays(1)); // Запускаем сервер на период одного дня
StatusLabel.Text = "Сервер WCF успешно запущен!";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
B_Start.Enabled = false;
B_Stop.Enabled = true;
}
}
try
{
cprs.Close();
host.Close();
StatusLabel.Text = "Сервер WCF успешно остановлен!";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
B_Start.Enabled = true;
B_Stop.Enabled = false;
}
}
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.PeerResolvers;
Найдём:
Добавим после:
537
private CustomPeerResolverService cprs; // Объекта базовой реализации
настраиваемой службы распознователя одноранговых узлов
private ServiceHost host; // Объект сервера для службы
Метод LWP16MainServer():
public LWP16MainServer()
{
InitializeComponent();
B_Stop.Enabled = false;
}
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="System.ServiceModel.PeerResolvers.CustomPeerResolverService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:5433/LWP16_ChatServer"/>
</baseAddresses>
538
</host>
<endpoint address="net.tcp://localhost:5433/LWP16_ChatServer"
binding="netTcpBinding" bindingConfiguration="TcpConfig"
contract="System.ServiceModel.PeerResolvers.IPeerResolverContract">
</endpoint>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="TcpConfig">
<security mode="None"></security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
<add baseAddress="net.tcp://localhost:5433/LWP16-ChatServer"/>
<endpoint address="net.tcp://localhost:5433/LWP16-ChatServer"
binding="netTcpBinding" bindingConfiguration="TcpConfig"
contract="System.ServiceModel.PeerResolvers.IPeerResolverContract">
</endpoint>
539
конфигурацию привязки с атрибутом bindingConfiguration. Для нашего случая
атрибут имеет привязку к имени TcpConfig:
<binding name="TcpConfig">
<security mode="None"></security>
</binding>
540
Рис. 5. 2. Добавить ссылку: добавляем новую ссылку на библиотеку Microsoft Speech
Object Library
541
Рис. 5. 1. Расстановка элементов на форме приложения-клиента
GroupBox:
(Name): GB_UserDetails
Text: Пользовательские данные
Label:
(Name): L_LoginInfo
Text: Введите имя для входа и нажмите на
«Войти в чат»
TextBox:
(Name): TB_UserName
Button:
(Name): B_Login
Text: Войти в чат
GroupBox:
(Name): GB_UserList
Text: Пользователи в сети
ListBox:
(Name): LB_Users
542
GroupBox:
(Name): GB_MessageWindow
Text: Сообщения чата
RichTextBox:
(Name): RTB_Messages
ReadOnly: True
Multiline True
TextBox:
(Name): TB_SendMessage
Button:
(Name): B_Send
Text: Отправить
Button:
(Name): B_WakeUp
Text: !
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.InteropServices;
using SpeechLib; // Для работы имитации голоса (в Windows XP не использовать)
Найдём:
namespace LWP16_ChatClient
{
Добавим после:
[ServiceContract(CallbackContract = typeof(ILWP16Service))]
public interface ILWP16Service // Интерфейс подключения к службе сервера
{
[OperationContract(IsOneWay = true)] // Операция не возвращает ответовное
сообщение
void Join(string memberName); // Объявление метода присоединения к чату (по
имени)
[OperationContract(IsOneWay = true)]
void SendMessage(string memberName, string message); // Объявление метода отсылки
сообщения (по имени и тексту)
[OperationContract(IsOneWay = true)]
void WakeUp(string memberName, string wakeup); // Объявление метода отсылки
сообщения (по имени и тексту)
[OperationContract(IsOneWay = true)]
void Leave(string memberName); // Объявление метода выхода из чата (по имени)
[OperationContract(IsOneWay = true)]
void ImageFF(string memberName, Bitmap image); // Объявление метода выхода из
чата (по имени)
}
Найдём:
543
public partial class LWP16MainClient : Form
{
Заменим на:
public LWP16MainClient()
{
InitializeComponent();
this.AcceptButton = B_Login; // Привязываем событие Нажатия Enter с кнопкой
"Войти в чат"
}
После добавим:
544
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " говорит: " + message + " [" +
DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку с именем и сообщением
if (message == "WakeUp") { Beep(500, 100); }
}
// Метод присоединения к чату (по имени)
void LWP16Client_WakeUp(string name, string wakeup)
{
if (!LB_Users.Items.Contains(name)) // Если имени нет в ListBox при получении
сообщения в RichTextBox
{
LB_Users.Items.Add(name); // Добавляет нового пользователя в ListBox
}
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " попытался разбудить чат: [" +
DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку c именем и датой входа
пользователя
if (wakeup == "WakeUp")
{
Beep(500, 100);
Beep(500, 100);
SpVoice voice = new SpVoice();
voice.Speak("Wake Up Mate", SpeechVoiceSpeakFlags.SVSFDefault);
}
}
// Метод выхода из чата (по имени)
void LWP16Client_RemoveUser(string name)
{
try
{
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " вышел: [" + DateTime.Now.ToString() +
"]"); // Добавляем в RichTextbox строчку с именем и датой выхода пользователя
LB_Users.Items.Remove(name); // Удаляем по имени из ListBox
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}
545
{
MessageSent(memberName, message);
}
}
#endregion
Перейдём к событиям формы. Для кнопки «Войти в чат» событие Click будет
содержать код:
try
{
NewJoin += new UserJoined(LWP16Client_NewJoin); // Переопределяем
вызов метода подключения к чату через экзмепляр события
MessageSent += new UserSendMessage(LWP16Client_MessageSent); //
Переопределяем вызов метода отсылки сообщения в чат через экзмепляр события
NewWakeUp += new UserWakeUp(LWP16Client_WakeUp); // Переопределяем
вызов метода "разбудить чат" в чат через экзмепляр события
RemoveUser += new UserLeft(LWP16Client_RemoveUser); // Переопределяем
вызов метода выхода из чата через экзмепляр события
channel = null;
this.userName = TB_UserName.Text.Trim(); // Удаляем пробелы из имени
пользователя
// class ServiceModel.InstanceContext
InstanceContext context = new InstanceContext(new
LWP16MainClient(TB_UserName.Text.Trim()));
factory = new DuplexChannelFactory<ILWP16Channel>(context,
"ChatEndPoint"); // Получаем данные из app.config и передаём данные duplex-каналу
channel = factory.CreateChannel(); // Создаём канал и передаём его
экземпляру интерфейса чата
// class ServiceModel.IOnlineStatus
IOnlineStatus status = channel.GetProperty<IOnlineStatus>(); //
Определяем экземпляр для индикации доступности объекта по каналу
status.Offline += new EventHandler(Offline); // Вызов метода Offline
(если в чате больше никого)
status.Online += new EventHandler(Online); // Вызов метода Online
(если в чате больше одного пользователя)
channel.Open(); // Открываем канал
channel.Join(this.userName); // Вызываем метод Join() с текущим
именем пользователя введённым в TB_Username
GB_MessageWindow.Enabled = true; // Включаем группу "Сообщения чата"
GB_UserList.Enabled = true; // Включаем группу "Список пользователей"
546
GB_UserDetails.Enabled = false; // Гасим группу "Данные для входа"
this.AcceptButton = B_Send; // Enter = "Отослать"
RTB_Messages.AppendText("*****************************ДОБРО
ПОЖАЛОВАТЬ В ЧАТ*****************************\r\n");
TB_SendMessage.Select();
TB_SendMessage.Focus();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
try
{
if (channel != null)
{
channel.Leave(this.userName); // Если закрываем форму, вызваем метод
Leave() и удаляем пользователя
channel.Close(); // Закрываем канал между сервером и клиентом
}
if (factory != null)
{
factory.Close(); // Закрываем duplex-канал
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
547
Теперь разъясним принцип работы всего того что было тут наворочено. После
запуска приложения-сервера, происходит инициализация канала по определённому
адресу. Был выбран локальный адрес и порт 5433, а также абсолютное имя
пространства имён LWP16_ChatServer для адресации по этому порту. Имя для
адресации в строке конфигурации клиента:
<custom address="net.tcp://localhost:5433/LWP16_ChatServer"
Может быть любым, но! Главное чтобы оно совпадало с серверной строкой
адреса (в файле конфигурации приложения-сервера). То есть, запущенное
приложение-клиент должно содержать в файле конфигурации этот абсолютный адрес.
После ввода имени, становится доступной кнопка «Войти в чат». После нажатия на
кнопку, в приложении-клиенте происходит вызов (через делегат и событие) метода
интерфейса: Join(string memberName), содержащее переданное из текстового поля
строку с именем. И далее выполняется: LWP16Client_NewJoin(string name), где в
RichTextBox добавляется запись о присоединении пользователя с таким-то именем к
чату и временем присоединения. Имя также добавляется в ListBox. Если пользователь в
уже не один, срабатывает метод Online(), которые сразу же оповещает и пользователя
и другого пользователя о том, что они «в сети». Метод срабатывает в том случае, если
в сети больше одного пользователя, после нажатия кнопки «Войти в чат» пользователя
ещё не вошедшего в чат. Вызывает событие после получения по каналу «индикатора
события доступности»:
6. Завершающая часть
548
Стартуем сервер нажатием кнопки «Запуск». Запускаем копию приложения клиента.
Вводим имя и жмём «Войти в чат»:
Рис. 6. 1. Результат работы приложения клиента после запуска сервера: вход первого
пользователя в чат и отсылка сообщения
549
Рис. 6. 2. Результат работы приложения клиента после запуска сервера: вход второго
пользователя в чат и его действия
550
Рис. 6. 3. Результат работы приложения клиента после запуска сервера: вход третьего
пользователя в чат и его действия
551
Варианты заданий: Варианты для выполнения самостоятельных заданий с
использованием материала данной работы приведены по ссылке в конце этого
материала (сслыка доступна в программном продукте).
Содержание
43.Вводная часть
44.Создание приложения Silverlight
45.Модификация приложения Silverlight: первые шаги и полноэкранный
режим
46.Модификация приложения Silverlight: простой проигрыватель MP3-
файлов
47.Модификация приложения Silverlight: работа с анимацией
48.Завершающая часть
49.О приложении к Лабораторной работе № 17
1. Вводная часть
552
спецификой браузера, либо через плагин, либо путём «песочницы» (виртуальной
машины).
Многопоточность Да Да Нет Да
Двумерная графика Да Да Да Да
Неофициально
Трёхмерная графика Да Да Да
и небезопасно
Поддержка сокетов Да Да Да Да
Асинхронные HTTP
Да Да Да Да Да
запросы
Синхронные HTTP
Да Да Да Нет
запросы
Модификация HTTP
Да Да Да Частично Да
заголовков запроса
Постоянное HTTP/1.1
Нет Да Да Нет Нет
соединение
553
Поддержка «cookie» Да Да Да Да Да Нет
Таблицы стилей Да Да Да Да Да
Пользовательские
Нет Да Да Да Да
шрифты
Таймеры Да Да Да Да Да
Поддержка доступа к
Да Да Да Да Нет Нет
DOM браузера
Поддержка исполнения
Да Да Да Да Нет Нет
скриптов JavaScript
Поддержка
динамической Да Да Да Да Да
подгрузки кода
Доступ к файловой
Да Да Частично Да Нет
системе
Adobe Native
Браузер HTML5+JavaScript Silverlight JavaFX
Flash Client
FireFox 4 Да Да Да Да Нет
554
Google Chrome (>9.0) Да Да Да Да Да
555
Рис. 1. 3. Архитектура приложения Silverlight 2.0
556
Рис. 1. 4. Окно «Конфигурация Microsoft Silverlight»
557
Запускаем Visual Studio 2010, откроется Начальная страница:
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
558
Рис. 2. 2. Окно создания нового проекта
559
Рис. 2. 3. Вводим данные нового проекта приложения Silverlight
После нажатия клавиши ОК. Откроется ещё одно окно, в котором будет
предложено:
560
Ввести имя нового веб-проекта на основе приложения (Имя нового веб-
проекта оставляем без изменений: LWP17Silverlight.Web), указать версию Silverlight
(в нашем случае «четвёрка»), а также Включить службы RIA WCF1 (не ставим
галочку).
561
После нажатия кнопки ОК (рисунок 2. 4), среда разработки сформирует два (а
не один как раньше) проекта в одном решении, каждый со своим исходных кодом и
дополнительными файлами.
Как видим, проект пока что пуст. Единственное что можно сделать, это вызвать
окно конфигурации Silverlight:
562
Обратим также внимание на имя страницы в окне браузера и на
соответствующие имена в обозревателе решений веб-проекта. Обе страницы
(LWP17SilverlightTestPage.aspx и LWP17SilverlightTestPage.html) идентичны и
подцепляют «яваскрипт»-код Silvelight.js и главную страницу первого проекта в
составе решения (WPF-приложения). Код обеих веб-страниц автоматически
редактируется после каждой компиляции. Привязка объекта WPF-приложения к
страницах осуществляется следующим образом (автоматически, через обычный HTML-
код):
563
Рис. 2. 8. Запуск приложения Silverlight по конфигурации Debug: просмотр сведений о
сервере и локальном адресе страницы
<UserControl x:Class="LWP17Silverlight.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
564
элементы не будут ограничены размерами изначальной страницы). Для переключения к
фиксированным размерам можно нажать в конструкторе на кнопку:
Либо впишем:
Итак, поменяем эту строчку следующим кодом (изменим размеры для дизайна):
Код XAML:
565
Вписываем Azure либо выбираем понравившийся цвет. Далее редактируем
свойство RowDefinitions, жмём «...»:
566
Рис. 3. 2. Завершённый элемент Grid, разделённый на строки и столбцы
простой TextBlock ( ):
567
В полноэкранном режим будут работать только следующие клавиши:
<LineBreak/>
Стрелки клавиатуры, пробел, Tab, Page Up, Page down, Home, End и
Enter.</TextBlock>
В ячейку ниже [1, 0] добавим кнопку (Button), и сразу же замени код кнопки
следующим XAML-кодом:
public MainPage()
{
InitializeComponent();
}
568
Изменяем на:
public MainPage()
{
InitializeComponent();
// Подключаем событие Loaded чтобы подключить события на этапе загрузки
страницы
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
569
Рис. 3. 3. Результат работы приложения Silverlight: нажата клавиша «пробел»
570
Простой проигрыватель MP3-файлов. Для начала добавим два простеньких
класса. Один класс будет отвечать за свойства MP3-файла: имя и путь. Добавим класс
для проекта WPF-приложения: выделим имя проекта (LWP17Silverlight) и выполним:
Проект -> Добавить класс... (Shift+Alt+C): в открывшемся окне в поле Имя
указываем DataItem.cs. Код файла будет таким:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace LWP17Silverlight
{
public class DataItem
{
private string nameItem;
private string pathItem;
Второй класс будет содержать всего две функции, которые будут работать со
временем воспроизведения файла. Класс назовём ProgressConverter (файл
ProgressConverter.cs) с кодом:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
namespace LWP17Silverlight
{
public class ProgressConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return ((TimeSpan)value).TotalSeconds;
571
}
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Добавим после:
xmlns:lwp17="clr-namespace:LWP17Silverlight"
Найдём:
Добавим после:
<UserControl.Resources>
<lwp17:ProgressConverter x:Key="progress"></lwp17:ProgressConverter>
</UserControl.Resources>
Установленные свойства:
572
Перетягиваем с панели элементов в самую правую внизу страницы [4, 1]
573
В списке Выбрать элемент выбираем DataGridTextColumn, жмём дважды
добавить. Свойства первого столбца такие:
Итоговый XAML-код:
574
Рис. 4. 2. Расстановка элементов на странице MainPage.xaml проекта WPF приложения
Silverlight
575
XAML-код элемента будет таким:
Кнопка «Стоп»:
Кнопка «Воспроизвести:
Кнопка «Пауза»:
Кнопка «Заглушить»:
576
<Slider LargeChange="0.1" Maximum="1" SmallChange="0.01" Value="{Binding Volume,
ElementName=mediaElement, Mode=TwoWay, UpdateSourceTrigger=Default}" Grid.Column="1"
Grid.Row="2" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right"
Width="94" />
<TextBlock Height="23" HorizontalAlignment="Right" Margin="0,0,98,0"
Name="textBox6" Text="Громкость:" VerticalAlignment="Top" Grid.Column="1" Grid.Row="2" />
577
<music open="1" path="Resource/MP3/Daniel_Barbosa_-
_Asian_Gardens_(Feat._Shen_Shen).mp3" name="Asian Gardens (Feat. Shen Shen)">Daniel
Barbosa</music>
<music open="1" path="Resource/MP3/Elena_-_Zombie_(Ambient_Vocal_Edit).mp3"
name="Zombie (Ambient Vocal Edit)">Elena</music>
</root>
// Для MP3-проигрывателя
using System.Xml;
using System.IO;
using System.Windows.Threading;
using System.Windows.Browser;
Найдём:
public MainPage()
{
Добавим до:
Найдём:
Добавим после:
this.XmlProcessMethod();
Найдём:
Добавим после:
/// <summary>
/// Функция использует асинхронную загрузку данных из XML-файла
/// и подписывается на событие для объекта WebClient DownLoadXmlComplete
/// и событие MeidaEnded объекта MediaElement
/// </summary>
private void XmlProcessMethod()
{
578
WebClient webClient = new WebClient();
webClient.DownloadStringAsync(new Uri(HtmlPage.Document.DocumentUri,
"Resource/MusicList.xml"));
webClient.DownloadStringCompleted += new
DownloadStringCompletedEventHandler(this.DownLoadXmlComplete);
mediaElement.MediaEnded += new RoutedEventHandler(mediaElement_MediaEnded);
}
/// <summary>
/// Загрузка музыки согласно данные XML-файла и конвертирование данных в список.
/// Привязка свойства ItemSource элемента DataGrid к данному списку.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DownLoadXmlComplete(object sender, DownloadStringCompletedEventArgs
e)
{
using (XmlReader reader = XmlReader.Create(new StringReader(e.Result)))
{
DataItems = new List<DataItem>();
while (reader.Read())
{
579
private void mediaElement_MediaOpened(object sender, RoutedEventArgs e)
{
textBlock5.Text = "Статус: Воспроизведение начато";
// Получаем общее время композиции
timeDuration = mediaElement.NaturalDuration.HasTimeSpan ?
mediaElement.NaturalDuration.TimeSpan : TimeSpan.FromMilliseconds(0);
sliderProcess.Maximum = timeDuration.TotalSeconds;
}
if (!boolIsMuted)
{
textBlock5.Text = "Статус: Звук выключен";
buttonMuted.Content = "Включить";
mediaElement.IsMuted = true;
boolIsMuted = true;
}
else
{
textBlock5.Text = "Статус: Звук включен";
buttonMuted.Content = "Заглушить";
mediaElement.IsMuted = false;
boolIsMuted = false;
}
}
580
уменьшаем громкость, меняем баланс на левый наушник или на правый, выключаем и
включаем звук.
<Storyboard>Дочерние анимации</Storyboard>
581
BeginStoryboard или Begin. Это имеет побочный эффект в виде перезапуска
анимации. В коде можно использовать метод Seek для возврата раскадровки
обратно в предыдущее положение.
Если анимация применяется непосредственно к свойству с помощью метода
BeginAnimation, снова вызываем метод BeginAnimation и передаём ему
измененную анимацию.
Если ведется работа непосредственно на уровне часов, создаём и применяем
новый набор часов и используем их для замены предыдущего набора созданных
часов.
582
Рис. 5. 1. Добавление нового элемента – LWP17Silverlight: Страница Silverlight
d:DesignWidth="640" d:DesignHeight="480"
Title="Page1 Page">
Изменим эти строчки так (равносильно изменению свойств, как уже было
сказано):
d:DesignWidth="300" d:DesignHeight="300"
Title="Знакомство с Silverlight (C#) :: Основы анимации">
<lwp17:Page_BasicAnimation></lwp17:Page_BasicAnimation>
xmlns:lwp17="clr-namespace:LWP17Silverlight"
<Grid x:Name="LayoutRoot">
...
</Grid>
583
<Rectangle x:Name="rectangle1" Fill="Turquoise"
Canvas.Top="100" Canvas.Left="100"
Width="100" Height="100">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation RepeatBehavior="3x"
Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="Height"
To="200" Duration="0:0:5"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
584
значению, указанному в атрибуте To («К»), либо к значению, указанному в атрибуте
By («По»).
Двойные типы анимируются с помощью DoubleAnimation или
DoubleAnimationUsingKeyFrames. Этот метод используется для анимации свойств,
содержащих двойное значение — например, измерений, вроде Canvas.Left или
визуальных атрибутов, вроде Opacity.
Типы точек анимируются с помощью PointAnimation или типа
PointAnimationUsingKeyFrames. Этот конкретный метод используется для анимации
свойств, содержащих значение точки, таких, как сегментов строк или кривых,
определенных с использованием точек.
Типы цветов анимируются с помощью ColorAnimation или типа
ColorAnimationUsingKeyFrames. Этот метод используется для анимации свойств,
содержащих значение цвета — фона или штриха элемента, например.
<DoubleAnimation Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="Height" />
<DoubleAnimation Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="Height" Duration="0:0:5" />
<DoubleAnimation Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="Height" BeginTime="0:0:5" />
<DoubleAnimation Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="Height" SpeedRatio="2" Duration="0:0:5" />
<DoubleAnimation Storyboard.TargetName="rectangle1"
Storyboard.TargetProperty="Height" SpeedRatio="2" Duration="0:0:5" AutoReverse="True"
/>
585
Когда работа анимации завершена, можно применить ряд параметров, чтобы
заставить её вести себя нужным образом. Они указываются с помощью свойства
RepeatBehavior. Это свойство может принимать три различных типа значений:
Время, определённое в секундах. Временная шкала подождет это время и затем
начнёт анимацию снова.
Установка RepeatBehavior на Forever («Постоянно») для постоянного
повторения.
Определенное число повторений, установленное путем указанием числа, за
которым следует x. Например, если анимация должна произойти трижды,
указывается значение 3х.
<Rectangle x:Name="rectangle2"
Width="100" Height="100"
Fill="Black"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="rectangle2"
Storyboard.TargetProperty=
586
"(Shape.Fill).(SolidColorBrush.Color)"
To="#00000000" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
<Path Stroke="Black">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="100,100">
<QuadraticBezierSegment x:Name="seg"
Point1="200,0" Point2="300,100" />
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard>
<Storyboard>
<PointAnimation Storyboard.TargetName="seg"
Storyboard.TargetProperty="Point2"
From="300,100" To="300,200"
Duration="0:0:5" AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
587
анимация с тремя равномерно распределёнными опорными моментами, можно указать
окончание первого опорного момента в 0:0:3, второго в 0:0:6 и третьего в 0:0:9.
Длина опорного момента не указывается: вместо этого указывается конечное время для
каждого опорного кадра.
В качестве ещё одного примера, представим себе двойную анимацию, которая
должна охватывать половину диапазона от 100 до 500. Анимация должна двигаться
очень быстро в первой половине и очень медленно во второй. В целом, она будет
требовать шести секунд переноса. Поскольку 350 – это середина между 100 и 500,
опорный кадр следует определить как начинающийся в точке 350. Ему следует указать
продолжаться одну секунду между начальной точкой и средней точкой (опорное время
0:0:1) и затем установить продолжительность опорного времени между средней точкой
и конечной точкой в пять секунд, используя второе опорное время как 0:0:6. Теперь
элемент установлен так, чтобы пролететь по экрану к средней точке и медленно ползти
дальше.
В предыдущих примерах, оба сегмента анимации используют линейную
интерполяцию. Для обеспечения большей гибкости, предоставлены два других типа
опорных кадров: дискретный опорный кадр, который мгновенно перебрасывает
значение между двумя значениями и сплайновый опорный кадр, который перемещает
значение между начальной и конечной точками, используя квадратическую кривую для
определения интерполяции.
Для указания опорных кадров на анимации используется постфикс
UsingKeyFrames. То есть, для указания двойных анимаций и использования опорных
кадров, следует использовать DoubleAnimationUsingKeyFrame, на котором указывается
цель и свойство (тем же образом, что при использовании DoubleAnimation).
DoubleAnimationUsingKeyFrames содержит определения опорных кадров. И то же самое
относится к PointAnimationUsingKeyFrames или ColorAnimationUsingKeyFrames.
588
объект перескочить к значению на момент указанного опорного кадра. Добавляем
следующий код на страницу, результатом будет скачкообразное изменение размеров
фиолетового прямоугольника в течении трёх секунд:
589
KeySpline="0.3,0 0.6,1" Value="200" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
<navigation:Page x:Class="LWP17Silverlight.Page_BasicPointAnimation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="300" d:DesignHeight="300"
Title="Знакомство с Silverlight (C#) :: Базовая точечная анимация с
событиями">
<navigation:Page.Resources>
<Storyboard x:Name="MyStoryboard">
<PointAnimation x:Name="MyPointAnimation" Duration="0:0:2"
Storyboard.TargetProperty="Center"
Storyboard.TargetName="MyAnimatedEllipseGeometry">
</PointAnimation>
</Storyboard>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="9*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Text="Щёлкните левой кнопкой мыши на любой позиции на сером фоне"
TextAlignment="Center"></TextBlock>
</StackPanel>
<StackPanel MouseLeftButtonDown="StackPanel_MouseLeftButtonDown"
x:Name="MyStackPanel" Background="Gray" Grid.Row="1">
<Path>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF1F7FB" Offset="0"/>
<GradientStop Color="#FF3794E4" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Data>
<!-- Рисуем эллипс -->
<EllipseGeometry x:Name="MyAnimatedEllipseGeometry" Center="50,80"
RadiusX="15" RadiusY="15" />
</Path.Data>
</Path>
</StackPanel>
</Grid>
590
</navigation:Page>
/// <summary>
/// Обработчик события изменяет свойство To объекта PointAnimation,
/// и начинает проигрывание анимации Storyboard
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs
e)
{
var targetpoint = e.GetPosition(this.MyStackPanel);
this.MyPointAnimation.To = targetpoint;
this.MyStoryboard.Begin();
}
<navigation:Page x:Class="LWP17Silverlight.Page_AnimateDependencyProperty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:c="clr-namespace:LWP17Silverlight;assembly=LWP17Silverlight"
mc:Ignorable="d"
xmlns:navigation="clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="300" d:DesignHeight="300"
Title="Знакомство с Silverlight (C#) :: Анимация свойства зависимостей">
<navigation:Page.Resources>
<Storyboard x:Name="MyAnimationStoryboard">
<PointAnimation x:Name="MyAnimation"
Duration="0:0:2"
Storyboard.TargetProperty="EllipseCenter"
Storyboard.TargetName="MyAnimatedEllipseGeometry">
</PointAnimation>
</Storyboard>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="9*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Text="Щёлкните левой кнопкой мыши на любой позиции на чёрном фоне"
TextAlignment="Center"></TextBlock>
</StackPanel>
591
<StackPanel x:Name="MyStackPanel"
MouseLeftButtonDown="MyStackPanel_MouseLeftButtonDown" Background="Black" Grid.Row="1">
<Canvas>
<Line x:Name="MyLine" Fill="Red" Stroke="Red" Visibility="Collapsed"
StrokeThickness="5" Canvas.ZIndex="1"></Line>
<c:MyEllipse x:Name="MyAnimatedEllipseGeometry"
EllipseCenterChanged="MyAnimatedEllipseGeometry_EllipseCenterChanged"></c:MyEllipse>
</Canvas>
</StackPanel>
</Grid>
</navigation:Page>
<lwp17:Page_AimateDependencyProperty></lwp17:Page_AimateDependencyProperty>
</sdk:TabItem>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
namespace LWP17Silverlight
{
public partial class Page_AnimateDependencyProperty : Page
{
Point _currenttargetpoint;
public Page_AnimateDependencyProperty()
{
InitializeComponent();
}
/// <summary>
/// Сихнронизация конечной точки объекта MyLine c последней точкой
/// которую пользователь выбирает нажатием и текущей позицие объекта MyEllipse.
/// Анимация создаётся для MyLine
/// </summary>
592
/// <param name="sender"></param>
/// <param name="e"></param>
private void MyAnimatedEllipseGeometry_EllipseCenterChanged(DependencyObject
sender, DependencyPropertyChangedEventArgs e)
{
this.MyLine.Visibility = Visibility.Visible;
this.MyLine.X1 = this.MyAnimatedEllipseGeometry.EllipseCenter.X;
this.MyLine.Y1 = this.MyAnimatedEllipseGeometry.EllipseCenter.Y;
this.MyLine.X2 = this._currenttargetpoint.X;
this.MyLine.Y2 = this._currenttargetpoint.Y;
}
}
}
<navigation:Page x:Class="LWP17Silverlight.MyEllipse"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:c="clr-namespace:LWP17Silverlight;assembly=LWP17Silverlight"
mc:Ignorable="d"
xmlns:navigation="clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="300" d:DesignHeight="300">
<Grid x:Name="LayoutRoot" Background="Transparent" IsHitTestVisible="False">
<Path>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF1F7FB" Offset="0"/>
<GradientStop Color="#FF3794E4" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Data>
<EllipseGeometry x:Name="MyAnimatedEllipseGeometry"
Center="50,50" RadiusX="15" RadiusY="15" />
</Path.Data>
</Path>
</Grid>
</navigation:Page>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
namespace LWP17Silverlight
{
public delegate void EllipseCenterChangedEventHandler(DependencyObject sender,
DependencyPropertyChangedEventArgs e);
593
public static readonly DependencyProperty EllipseCenterProperty =
DependencyProperty.Register("EllipseCenter", typeof(Point),
typeof(MyEllipse),
new PropertyMetadata(new PropertyChangedCallback((obj, e) => { MyHandler(obj,
e); })));
EllipseCenterChangedEventHandler _myDelegate;
public MyEllipse()
{
InitializeComponent();
EllipseCenter = this.MyAnimatedEllipseGeometry.Center;
}
/// <summary>
/// Это метод обратного вызова, который вызывает метод OnEllipseCenterChanged
/// объекта MyEllipse, свойство EllipseCenter которого было изменено
/// </summary>
/// <param name="obj"></param>
/// <param name="e"></param>
static void MyHandler(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyEllipse ellipse = obj as MyEllipse;
if (ellipse != null) { ellipse.OnEllipseCenterChanged(obj, e); }
}
/// <summary>
/// Этот метод вызывается методом свойства EllipseCenterProperty.
/// Это обновляет свойство Center объекта MyAnimatedEllipseGeometry интерфейса,
/// когда срабатывает событие EllipseCenterChanged
/// </summary>
/// <param name="obj"></param>
/// <param name="e"></param>
void OnEllipseCenterChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
MyEllipse ellipse = obj as MyEllipse;
if (ellipse != null) { ellipse.MyAnimatedEllipseGeometry.Center =
ellipse.EllipseCenter; }
if (_myDelegate != null) _myDelegate(obj, e);
}
}
}
<DoubleAnimation>
<DoubleAnimation.EasingFunction>
594
Функции
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<navigation:Page x:Class="LWP17Silverlight.Page_EasingFunction"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:c="clr-namespace:LWP17Silverlight;assembly=LWP17Silverlight"
mc:Ignorable="d"
xmlns:navigation="clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="300" d:DesignHeight="300"
Title="Знакомство с Silverlight (C#) :: Применение EasingFunction">
<navigation:Page.Resources>
<BackEase x:Name="BackEase" Amplitude="1" EasingMode="EaseIn"></BackEase>
<c:MyEase x:Name="MyEase"></c:MyEase>
<Storyboard x:Name="MyAnimationStoryboard">
<PointAnimation x:Name="MyAnimation"
Duration="0:0:2"
Storyboard.TargetProperty="Center"
Storyboard.TargetName="MyAnimatedEllipseGeometry"
EasingFunction="{StaticResource BackEase}">
</PointAnimation>
</Storyboard>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="7*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<RadioButton GroupName="g1" Content="Применяем BackEase" IsChecked="true"
x:Name="BackEaseRadioButton" Click="BackEaseRadioButton_Click"></RadioButton>
<RadioButton GroupName="g1" Content="Применяем MyEase" IsChecked="false"
x:Name="MyEaseRadioButton" Click="MyEaseRadioButton_Click"></RadioButton>
<TextBlock Text="Щёлкните левой кнопкой мыши на любой позиции на розовом
фоне" TextAlignment="Center"></TextBlock>
</StackPanel>
<StackPanel MouseLeftButtonDown="MyStackPanel_MouseLeftButtonDown"
x:Name="MyStackPanel" Background="Pink" Grid.Row="1">
<Path>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF1F7FB" Offset="0"/>
<GradientStop Color="#FF3794E4" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Data>
<EllipseGeometry x:Name="MyAnimatedEllipseGeometry"
Center="50,50" RadiusX="15" RadiusY="15" />
</Path.Data>
</Path>
</StackPanel>
</Grid>
</navigation:Page>
595
private void MyStackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs
e)
{
var targetpoint = e.GetPosition(this.MyStackPanel);
this.MyAnimation.To = targetpoint;
this.MyAnimationStoryboard.Begin();
}
/// <summary>
/// Класс MyEase
/// </summary>
public class MyEase : EasingFunctionBase
{
protected override double EaseInCore(double normalizedTime)
{
return normalizedTime / 5;
}
}
<navigation:Page x:Class="LWP17Silverlight.Page_UsingKeyFrames"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="300" d:DesignHeight="300"
Title="Знакомство с Silverlight (C#) :: Покадровая анимация">
<navigation:Page.Resources>
<Storyboard x:Name="MyAnimationStoryboard">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="path" Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[3].(TranslateTransform.X)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.8000000" Value="-4"
KeySpline="1,0,1,1"/>
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="-5" KeySpline="0,0,0,1"/>
596
<SplineDoubleKeyFrame KeyTime="00:00:01.2000000" Value="-4"
KeySpline="1,0,1,1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="path" Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[3].(TranslateTransform.Y)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.8000000" Value="127"
KeySpline="1,0,1,1"/>
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="69" KeySpline="0,0,0,1"/>
<SplineDoubleKeyFrame KeyTime="00:00:01.2000000" Value="127"
KeySpline="1,0,1,1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="9*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Button Content="Нажмите кнопку для начала анимации"
Click="Button_Click"></Button>
</StackPanel>
<StackPanel x:Name="MyStackPanel" Background="WhiteSmoke" Grid.Row="1">
<Path x:Name="path" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF1F7FB" Offset="0"/>
<GradientStop Color="#FF3794E4" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Data>
<EllipseGeometry x:Name="MyAnimatedEllipseGeometry"
Center="50,50" RadiusX="15" RadiusY="15" />
</Path.Data>
</Path>
</StackPanel>
</Grid>
</navigation:Page>
597
Готово. Компилируем, проверяем работоспособность. Если требуется поменять
заголовок страницы с приложением при запуске, открываем файлы
LWP17SilverlightTestPage.aspx или LWP17SilverlightTestPage.html, и ищем так строку:
<title>LWP17Silverlight</title>
6. Завершающая часть
598
Рис. 6. 3. Результат работы приложения Silverlight: анимация на вкладке «Анимация
свойства зависимостей»
599
Рис. 6. 5. Результат работы приложения Silverlight: анимация на вкладке «Применение
EasingFunction»
Содержание
50.Вводная часть
51.Создание веб-приложения ASP.NET и новой страницы веб-приложения
52.Модификация веб-приложения ASP.NET: реализация различной
функциональности
53.Модификация веб-приложения ASP.NET: AJAX
54.Завершающая часть
55.О приложении к Лабораторной работе № 18
1. Вводная часть
600
В этой работе будет рассмотрена работа с подтипом приложений доступных для
создания в Visual Studio 2010, а именно: Веб-приложение ASP.NET.
601
С точки зрения пользователя, открывшего браузер и перешедшего на веб-сайт,
всё веб-приложение для него: веб-страница. То есть пользователя не нужно знать,
как и на чём написана страница в браузере. Главное для него, разумеет содержимое.
Итак, ASP.NET предоставляет огромные и мощные инструменты по созданию веб-
страниц с применением полноценных языков программирования и вытекающими
отсюда практически безграничными возможностями.
Преимущества ASP.NET:
602
Дополнительные сведения о ASP.NET можно получить в разделе ASP.NET и
Visual Web Developer (http://msdn.microsoft.com/ru-ru/library/dd566231.aspx)
на странице
ASP.NET 4 (http://msdn.microsoft.com/ru-ru/library/ee532866.aspx).
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
603
Рис. 2. 2. Окно создания нового проекта
604
Рис. 2. 3. Вводим данные нового проекта веб-приложения ASP.NET
605
Рис. 2. 4. Обозреватель решений: состав проекта веб-приложения ASP.NET
сформированного средой разработки
606
Рис. 2. 5. Запуск веб-приложения ASP.NET по конфигурации Debug
607
Рис. 2. 6. Запуск веб-приложения ASP.NET по конфигурации Debug: создание базы
данных пользователей
Далее, если на ПК не установлен SQL Server 2012 или ниже, будем работать в
«местном аналоге» предлагаемым по умолчанию студией. Откроем папку «App_Data»:
608
Рис. 2. 7. Обозреватель серверов: ASPNETDB.MDF -> Таблицы ->
aspnet_Membership
609
Рис. 2. 8. aspnet_Membership: режим конструктора таблицы
Для работы с базами данных (как SQL Server, SQL Express, так и Microsoft
Access) Visual Studio имеет достаточно средств для комфортной работы с базами
различных поставщиков. В среде разработки предусмотрело большинство средств
работы с базами данных. От «схемы данных» и «связи данных», до создания SQL-
610
запросов. Останавливаться на этом не будем. Куда интереснее посмотреть
непосредственно код подключения и работы с базой данных.
<configuration>
<connectionStrings>
<add name="ApplicationServices"
connectionString="data source=.\SQLEXPRESS;Integrated
Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="ApplicationServices"
enablePasswordRetrieval="false" enablePasswordReset="true"
requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>
611
title="Документация по ASP.NET на MSDN">документация по ASP.NET доступна на
MSDN</a>.
</p>
</asp:Content>
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server"/>
</div>
Так вот, чтобы не делать на всех страницах одну и ту же шапку, меню и всё
остальное, применяют «главную страницу», на которой это всё и размещают, а
содержимое будет выводиться в специальную область.
612
Рис. 2. 9. Добавление нового элемента – LWP18ASPNET: добавление встраиваемой
страницы
613
Тут же изменится HTML-код (тэг p будет вставлен автоматически):
1. Поместите точку (курсор) вставки в конце текста «Это новая страница нашего
сайта!» и нажмём Enter один раз, чтобы создать дополнительное пространство в
поле элемента div. Вручную перепишем тэг <p></p> на тэг <div><div>:
<p>
Это новая страница нашего сайта!</p>
<div>
</div>
<div>
<p>Введите ваше имя:<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:Button
ID="Button1" runat="server" Text="Button" />
</p>
</div>
614
Теперь выполним следующее:
615
Label1.Text = TextBox1.Text + ", добро пожаловать на страницу!";
}
616
Редактируем код:
617
Рис. 2. 10. Результат работы веб-приложения ASP.NET: выбрана дата в календаре
Width: 300px
ReadOnly: True
<p>
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<input id="Text1" type="text" />
</p>
618
Кроме того, что они объявляются по-разному, в TextBox добавлен параметр
runat="server", который он говорит компилятору о том, что исполнение данного
элемента будет происходить на сервере. Похоже на TextBox в Windows Forms, то есть
элемент имеет свойства, методы и события.
Второй же элемент, не что иное, как известный всем HTML тэг и компилятором он
никак обрабатываться не будет, а будет использоваться как обычный HTML.
В случае PHP или HTML: у пользователя всегда была нужная переменная на
каждой странице, и её надо все время передавать через запросы (речь не о «cookie»),
например через «input type=hidden».
Global.asax:
619
// Код, выполняемый при запуске приложения
Application["users"] = 0;
}
<p>
<asp:TextBox ID="TextBox3" runat="server" ReadOnly="True"
Width="300px"></asp:TextBox>
</p>
Button:
620
3. Enabled — когда true кнопка активна. То есть её возможно нажать.
4. EnableViewState — хранить или нет состояние элемента в ViewState.
5. OnClientClick — указывается инструкция JavaScript, которая выполнится при
нажатии по кнопке.
6. PostBackUrl — страница на которую будет выполнен постбэк (передача
параметров). Аналог action в тэге form.
7. ToolTip — всплывающая подсказка.
8. ValidationGroup — группа для проверки значений перед постбэком.
9. Visible — когда false кнопка не отображается.
<p>
<asp:Button ID="Button1" runat="server" Text="Узнать имя этой кнопки"
OnClientClick="alert(this.name);" />
</p>
Создаём TabControl:
621
Рис. 3. 1. Редактор элементов меню: «Вкладка № 1»
622
Меню разместим горизонтально. Нажмём мышкой на код:
Orientation: Horizontal
<p>
<asp:MultiView ID="MultiView1" runat="server">
<asp:View ID="View1" runat="server">
Вкладка № 1
</asp:View>
<asp:View ID="View2" runat="server">
Вкладка № 2
</asp:View>
<asp:View ID="View3" runat="server">
Вкладка № 3
</asp:View>
</asp:MultiView>
</p>
DropDownList:
<p align="right">
Зарегистрированные пользователи:
</p>
623
Рис. 3. 2. Мастер конфигурации источника данных: выбор истопника и ввод
префикса
624
Рис. 3. 3. Настроить источник данных – ASPNETDB: выбор соединения данных
625
Рис. 3. 4. Настроить источник данных – ASPNETDB: запрос из базы
626
Там уже автоматически настроятся оставшиеся поля для DropDownList (как на
кусочке рисунка выше). Жмём ОК. Теперь элемент «списка» будет отображать на
странице сайта всех зарегистрированных пользователей:
Panel:
<p>
<asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px">
<asp:TextBox ID="TextBox4" runat="server"></asp:TextBox>
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Нажми
меня!" /></asp:Panel>
<asp:Panel ID="Panel2" runat="server" Height="50px" Visible="False"
Width="125px">
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></asp:Panel>
</p>
Literal:
<p>
<asp:Literal ID="Literal1" runat="server"></asp:Literal>
</p>
627
Wizard:
При реализации многостраничной регистрации, или списка вопросов, которые не
умещались на одну страницу и прочее, возникает задача разнести вопросы по
нескольким последовательно связанным страницам. Можно делать через множество
страниц и передавть все предыдущие ответы через поля «hidden», можно через GET,
возможно, объединять элементы в Panel и затем их скрывать. В ASP.NET 2.0 появился
элемент управления Wizard. В Wizard настраивается: количество шагов, формат
кнопок, надписи на них, внешний вид и прочее. Всё что необходимо, это добавить
нужные элементы внутрь шага Wizard.
Перетащим Wizard на новую сточку на странице (новый тэг «p»). Это создаст
HTML-код:
<p>
<asp:Wizard ID="Wizard1" runat="server">
<WizardSteps>
<asp:WizardStep ID="WizardStep1" runat="server" Title="Step 1">
</asp:WizardStep>
<asp:WizardStep ID="WizardStep2" runat="server" Title="Step 2">
</asp:WizardStep>
</WizardSteps>
</asp:Wizard>
</p>
628
В результате создания шагов и заполнения вкладок, а также применения
автоформата Профессиональный, код получим такой:
<p>
<asp:Wizard ID="Wizard1" runat="server" ActiveStepIndex="0" BackColor="#F7F6F3"
BorderColor="#CCCCCC" BorderStyle="Solid" BorderWidth="1px"
Font-Names="Verdana" Font-Size="0.8em" >
<HeaderStyle BackColor="#5D7B9D" BorderStyle="Solid" Font-Bold="True"
Font-Size="0.9em" ForeColor="White" HorizontalAlign="Left" />
<NavigationButtonStyle BackColor="#FFFBFF" BorderColor="#CCCCCC"
BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" Font-
Size="0.8em"
ForeColor="#284775" />
<SideBarButtonStyle BorderWidth="0px" Font-Names="Verdana" ForeColor="White"
/>
<SideBarStyle BackColor="#7C6F57" BorderWidth="0px" Font-Size="0.9em"
VerticalAlign="Top" />
<StepStyle BorderWidth="0px" ForeColor="#5D7B9D" />
<WizardSteps>
<asp:WizardStep ID="WizardStep1" runat="server" Title="Шаг № 1">
<asp:TextBox ID="TextBox5" runat="server"></asp:TextBox>
</asp:WizardStep>
<asp:WizardStep ID="WizardStep2" runat="server" Title="Шаг № 2">
<asp:CheckBox ID="CheckBox1" runat="server" Text="Выдели меня!" />
</asp:WizardStep>
<asp:WizardStep ID="WizardStep3" runat="server" Title="Шаг № 3">
Готово!
</asp:WizardStep>
</WizardSteps>
</asp:Wizard>
</p>
629
if (Session["myBrowser"] != null) { TextBox2.Text = "Ваш браузер: " +
Session["myBrowser"].ToString(); }
Session.Abandon();
if (FileUpload1.HasFile)
{
SavePath += FileUpload1.FileName;
FileUpload1.SaveAs(SavePath);
Label3.Visible = true;
Label3.Text = "Файл сохранён в, путь: " + SavePath + ". Размер файла: " +
(FileUpload1.FileBytes.Length/(1024)).ToString() + "Кб";
}
В итоге, жмём кнопку обзор, выбираем любой файл и жмём Загрузить файл.
Файл копируется на диск D (это может быть корневой каталог сервера и соответственно
абсолютный путь до директории сайта на сервере). Пример работы загрузки в корневой
каталог «веб-сайта» (метод MapPath):
630
HTTP запроса Для получения всех параметров клиента сделаем следующее. Добавим
DropDowList на «Вкладку № 1» (изменим код вкладки View1):
Кэширование:
631
В ASP.NET кэширование добавляется с использованием директивы страницы
OutputCache. Поместим в HTML-код нашей страницы в самом верху после первой
строчки следующий код:
632
Рис. 3. 5. Редактор узла TreeView: создаём «дерево» по своему вкусу
ShowLines: True
ShowCheckBoxes: All
633
<asp:TreeNode Text="2, 1" Value="2, 1"></asp:TreeNode>
</asp:TreeNode>
<asp:TreeNode Text="Корень № 3" Value="Корень № 3">
<asp:TreeNode Text="3, 1" Value="3, 1">
<asp:TreeNode Text="3, 1, 1" Value="3, 1, 1"></asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
<asp:TreeNode Text="Корень № 4" Value="Корень № 4">
<asp:TreeNode Text="4, 1" Value="4, 1">
<asp:TreeNode Text="4, 1, 1" Value="4, 1, 1">
<asp:TreeNode Text="4, 1, 1, 1" Value="4, 1, 1, 1"></asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
<asp:TreeNode Text="Корень № 5" Value="Корень № 5">
<asp:TreeNode Text="5, 1" Value="5, 1">
<asp:TreeNode Text="5, 1, 1" Value="5, 1, 1">
<asp:TreeNode Text="5, 1, 1, 1" Value="5, 1, 1, 1">
<asp:TreeNode Text="5, 1, 1, 1, 1" Value="5, 1, 1, 1,
1"></asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
</asp:TreeNode>
</Nodes>
<NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black"
HorizontalPadding="5px" NodeSpacing="1px" VerticalPadding="2px" />
<ParentNodeStyle Font-Bold="False" />
<SelectedNodeStyle BackColor="White" BorderColor="#888888" BorderStyle="Solid"
BorderWidth="1px" Font-Underline="False" HorizontalPadding="3px"
VerticalPadding="1px" />
</asp:TreeView>
634
Что получилось в итоге:
Cookie:
Текст события Click кнопки Button2 перепишем, добавив строчку кода, которая
отправляет написанное в TextBox4 в cookie-файл пользователя:
Код при загрузке страницы проверяет наличие cookie с полем TextC, если не
пустое и существует, заполняем TextBox4. Также этот код устанавливает «время
635
жизни» cookie с нашего сайта (1 час). В результате, вводим данные в текстовое поле,
открываем ещё одну страницу браузера и видим введённое нами число в нужном поле:
Web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login.aspx" timeout="2880" />
</authentication>
<sessionState mode="StateServer"></sessionState>
<globalization
fileEncoding="utf-8"
requestEncoding="utf-8"
responseEncoding="utf-8"
culture="ru-RU"
uiCulture="ru-RU" />
636
пользователю лучше сразу показать, где ошибка, экономя его время. Итак, на стороне
клиента правильность заполнения кода можно проверить на JavaScript. В ASP.NET есть
несколько элементов управления, которые генерируют JavaScript с целью проверки
правильного заполнения формы. Находятся в группе Проверка:
Начнём:
<p>
<asp:TextBox ID="TextBox6" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
ErrorMessage="RequiredFieldValidator" ControlToValidate="TextBox6"
Text="[ пожалуйста, заполните текстовое поле ]" Font-Size="Large"
ForeColor="#FF3300" Font-Bold="True"></asp:RequiredFieldValidator><br />
<asp:Button ID="Button5" runat="server" Text="Выполнить" />
</p>
<p>
<asp:TextBox ID="TextBox6" runat="server"></asp:TextBox>
637
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
ErrorMessage="RequiredFieldValidator" ControlToValidate="TextBox6"
Text="[ пожалуйста, заполните текстовое поле ]" Font-Size="Large"
ForeColor="#FF3300" Font-Bold="True">
</asp:RequiredFieldValidator>
<br />
<asp:RangeValidator ID="RangeValidator1" runat="server"
ErrorMessage="RangeValidator" ControlToValidate="TextBox6"
Text="[ значение поле не в диапазоне от 0 до 100 ]" Font-Size="Large"
ForeColor="#FF3300" Font-Bold="True" MaximumValue="100" MinimumValue="0">
</asp:RangeValidator>
<br />
<asp:Button ID="Button5" runat="server" Text="Выполнить" />
</p>
<p>
<asp:TextBox ID="TextBox7" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server"
ErrorMessage="RegularExpressionValidator" ControlToValidate="TextBox7"
Text="[ пожалуйста, введите корректный адрес E-mail ]" Font-Size="Large"
Font-Bold="True" ForeColor="Red"
ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"></
asp:RegularExpressionValidator>
<br />
<asp:Button ID="Button6" runat="server" Text="Выполнить" />
</p>
638
CompareValidator сравнивает значения введённые в разные элементы
управления. Таким образом, можно проверить правильность ввода пароля в два поля.
Свойства:
<p>
<asp:TextBox ID="TextBox8" runat="server"></asp:TextBox>
<asp:TextBox ID="TextBox9" runat="server"></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ErrorMessage="CompareValidator" ControlToValidate="TextBox8"
ControlToCompare="TextBox9" Font-Size="Large" Font-Bold="True"
Text="[ значения в текстовых полях не совпадают ]"
ForeColor="Red"></asp:CompareValidator>
<br />
<asp:Button ID="Button7" runat="server" Text="Выполнить" />
</p>
Если полей на форме достаточно много, то имеет смысл показывать все ошибки
вместе, например, вверху или внизу страницы. Для этого существует
ValidationSummary. Свойства:
639
И всплывающее окно:
Введение в AJAX:
640
open('Тип запроса «GET» или «POST»', 'URL страницы", 'исполнение запроса —
«True» — асинхронное исполнение', 'username', 'pasword') — создаёт запрос к серверу.
Добавим после:
641
Этот код будет сигнализировать о том, что страница не перегружается при
использовании AJAX-вызовов. Загрузим веб-сайт, текст в Label1 будет «Текст при
первой загрузке страницы Default.aspx», нажимаем серверную кнопку — «Текст при
перегрузке страницы». По смене текста будем судить о том, перегружалась или нет
страница:
</p>
<p>
<asp:Label ID="Label2" runat="server" Text="Label"></asp:Label>
</p>
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate >
<asp:Timer ID="Timer1" runat="server" Interval="1000">
</asp:Timer>
<asp:TextBox ID="TextBox1" runat="server" ReadOnly="True"></asp:TextBox>
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
642
<ProgressTemplate>
Обновление...
</ProgressTemplate>
</asp:UpdateProgress>
</div>
Label2.Text = DateTime.Now.ToString();
TextBox1.Text = DateTime.Now.ToString();
643
Рис. 4. 2. Добавление нового элемента – LWP18ASPNET: Текстовый файл
644
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
Добавим после:
Добавим на страницу Ajax.aspx ещё один серверный Label и Input (Buton). Для
события onclick кнопки запишем вызов некоторой функции
getDataFromServer('AJAX_Text1.txt', 'Label3') при нажатии кнопки. Задача
функции — вывести данные в Label3 из файла без перегрузки страницы. HTML-код
будет таким:
<p align="center">
<span class="bold" style="color:Green">[ реализация технологии AJAX: загрузка
текста из файла ]</span><br />
<asp:Label ID="Label3" runat="server" Text="Здесь будет выводимый
текст"></asp:Label><br />
<input type="button" id="Button2" value="Загрузить текст из AJAX_Text1.txt"
onclick="getDataFromServer('AJAX_Text1.txt', 'MainContent_Label3')"/>
</p>
645
var myAjaxObject = false;
if (window.XMLHttpRequest) {
myAjaxObject = new XMLHttpRequest();
}
else {
if (window.ActiveXObject) {
myAjaxObject = new ActiveXObject("Microsoft.XMLHTTP");
}
}
if (myAjaxObject) {
myAjaxObject.open("GET", dataSource);
myAjaxObject.onreadystatechange =
function () {
if (myAjaxObject) {
myAjaxObject.open("GET", dataSource);
myAjaxObject.onreadystatechange =
function () {
}
}
myAjaxObject.send(null);
646
После нажатия кнопки «Загрузить текст из AJAX_Text1.txt» видим, что страница
не обновлялась (сигнализатор: текст в Label1 на странице).
Добавим после:
// Чтение файла
String s = AppDomain.CurrentDomain.BaseDirectory + (@"AJAX_Text1.txt");
byte[] bText;
using (FileStream filestream = new FileStream(s, FileMode.Open, FileAccess.Read))
{
int bufSize = (int)filestream.Length;
bText = new byte[bufSize];
filestream.Read(bText, 0, (int)filestream.Length);
}
// Так мы вновь потеряем кириллицу
// Response.BinaryWrite(bText);
// А так нет
s = System.Text.Encoding.UTF8.GetString(bText);
Response.Write(s);
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script language = "javascript" type="text/javascript">
function getDataFromServer(dataSource, targetControl) {
var myAjaxObject = false;
if (window.XMLHttpRequest) {
myAjaxObject = new XMLHttpRequest();
}
else {
if (window.ActiveXObject) {
myAjaxObject = new ActiveXObject("Microsoft.XMLHTTP");
}
}
647
if (myAjaxObject) {
myAjaxObject.open("GET", dataSource);
myAjaxObject.onreadystatechange =
function () {
<system.web>
Добавим после:
<httpHandlers>
<add verb="GET" path="Ajax_Script.aspx" type="AJAX" />
</httpHandlers>
<p align="center">
<span class="bold" style="color:Green">[ реализация технологии AJAX: загрузка
текста из файла, используя класс ]</span><br />
<asp:Label ID="Label4" runat="server" Text="Здесь будет выводимый
текст"></asp:Label><br />
<input type="button" id="Button3" value="Загрузить текст из AJAX_Text1.txt"
onclick="getDataFromServer('Ajax_Script.aspx', 'MainContent_Label4')"/>
</p>
648
id = Request.QueryString.Get(0);
if (!string.IsNullOrEmpty(id))
{
if (id == "1") { id = AppDomain.CurrentDomain.BaseDirectory +
(@"AJAX_Text1.txt"); }
else { id = AppDomain.CurrentDomain.BaseDirectory + (@"AJAX_Text2.txt"); }
}
else { return; }
byte[] bTextid;
using (FileStream filestream = new FileStream(id, FileMode.Open,
FileAccess.Read))
{
int bufSize = (int)filestream.Length;
bTextid = new byte[bufSize];
filestream.Read(bTextid, 0, (int)filestream.Length);
}
id = System.Text.Encoding.UTF8.GetString(bTextid);
Response.Write(id);
}
onclick="getDataFromServer('Ajax_Script.aspx', 'MainContent_Label4')"/>
Изменим на:
onclick="getDataFromServer('Ajax_Script.aspx?id=1', 'MainContent_Label4')"/>
<p align="center">
<span class="bold" style="color:Green">[ реализация технологии AJAX: загрузка
текста из файла, используя класс с выбором ]</span><br />
<asp:Label ID="Label5" runat="server" Text="Здесь будет выводимый
текст"></asp:Label><br />
<input type="button" id="Button4" value="Загрузить текст из AJAX_Text1.txt"
onclick="getDataFromServer('Ajax_Script.aspx?id=1', 'MainContent_Label5')"/>
<input type="button" id="Button5" value="Загрузить текст из AJAX_Text2.txt"
onclick="getDataFromServer('Ajax_Script.aspx?id=2', 'MainContent_Label5')"/>
</p>
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Drawing;
649
using System.IO;
using System.Drawing.Imaging;
try
{
bitmap = new Bitmap(s);
bitmap.Save(memorystream, ImageFormat.Png);
byte[] b = memorystream.GetBuffer();
// Формат файла рисунка может быть отличен от исходного файла
Response.ContentType = "image/png";
Response.BinaryWrite(b);
bitmap.Dispose();
}
catch (Exception) { }
memorystream.Dispose();
}
}
#endregion
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script language = "javascript" type="text/javascript">
function getImageFromServer(dataSource, targetControl) {
var myAjaxObject = false;
650
if (window.XMLHttpRequest) {
myAjaxObject = new XMLHttpRequest();
}
else {
if (window.ActiveXObject) {
myAjaxObject = new ActiveXObject("Microsoft.XMLHTTP");
}
}
if (myAjaxObject) {
myAjaxObject.open("GET", dataSource);
myAjaxObject.onreadystatechange =
function () {
Первое:
Второе:
651
«AJAXML»:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script language="javascript" type="text/javascript">
function getXMLFromServer(dataSource, targetControl) {
var myAjaxObject = false;
if(myAjaxObject)
{
myAjaxObject.open("GET", dataSource);
myAjaxObject.onreadystatechange =
function()
{
652
var i;
var points = xmlDocument.getElementsByTagName("site");
var html='';
Не забываем поместить код скрипта (<script> ... </script>) в тэг head файла
Site.Master.
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
653
Response.ContentType = "text/xml"; // Ключевая строка для передачи XML
Response.BinaryWrite(System.Text.Encoding.UTF8.GetBytes(s));
}
}
}
#endregion
}
<p align="center">
<span class="bold" style="color:Green">[ реализация технологии AJAX: загрузка
текста из XML-файла, используя класс ]</span><br />
<input type="button" id="Button7" value="Загрузить XML-файл"
onclick="getXMLFromServer('Ajax_Script_XML.aspx', 'iddivforxml')" /><br />
<asp:Label ID="Label7" runat="server" Text="Ниже будет выводимое
содержимое:"></asp:Label>
</p>
<div align="center" id="iddivforxml"></div>
<div class="title">
<h1>
Мое приложение ASP.NET (C#)
</h1>
И изменим на:
<div class="title">
<h1>
Знакомство с ASP.NET (C#)
</h1>
5. Завершающая часть
654
Рис. 5. 1. Результат работы веб-приложения ASP.NET: общий вид страницы «Домашняя
страница»
655
Рис. 5. 3. Результат работы веб-приложения ASP.NET: общий вид страницы «Новая
страница»
656
веб-сервера IIS), собранную из кусков кода приведённых в данной лабораторной
работе, можно загрузить по ссылке в конце этого материала (сслыка доступна в
программном продукте).
Содержание
1. Вводная часть
2. Создание приложения Windows Forms
3. Модификация приложения Windows Forms: подготовка интерфейса и
добавление TabControl
4. Модификация приложения Windows Forms: вкладка «Просто фон!»
5. Модификация приложения Windows Forms: вкладка «Объекты и
градиент»
6. Модификация приложения Windows Forms: вкладка «Мой монитор
сломался?!»
7. Модификация приложения Windows Forms: вкладка «Векторные часы»
8. Модификация приложения Windows Forms: вкладка «Огонь!!!»
9. Модификация приложения Windows Forms: вкладка «Дождик»
10.Завершающая часть
11.О приложении к Лабораторной работе № 19
1. Вводная часть
657
мере, по замыслу) все достоинства своего предшественника и предоставляет множество
новых мощных возможностей. Кроме того, при её проектировании заранее ставилась
цель наименее болезненного переноса приложений на 64-битные платформы.
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
658
Рис. 2. 2. Окно создания нового проекта
659
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
660
Теперь, можно откомпилировать созданную программу, нажав клавишу F5
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
661
^ Поменяем заголовок формы (то, что отображается в шапке приложения слева).
Icon изменим изображение (иконку)
приложения
^ Необходим файл значка *.ico.
Size изменим со значений 300; 300 на 800;
600
^ Поменяем размер формы.
TabControl:
(Name): TB_Main
Anchor: Top, Bottom, Left, Right
662
Рис. 3. 1. Модифицированная форма приложения и расстановка необходимых
элементов управления
663
Рис. 4. 1. Редактор коллекции TabPage: меняем Text и Cursor
TabPage:
Text: Просто фон!
Cursor: No
В качестве фона вкладки будет выбрано некое изображение. Пусть это будет
изображение с именем Background_Image.jpg (использованное в данной работе
изображение можно получить в ZIP-архиве по ссылке в конец этого материала). Для
добавления выполним действия: Проект -> Существующий элемент...
(Shift+Alt+A), в открывшемся окне находим изображение и жмём Добавить.
664
// Используем аргумент e для получение параметров рисования (устройста и
прямоугольной области)
DoPaint(e.Graphics, e.ClipRectangle);
}
using System.Drawing.Drawing2D;
g.SmoothingMode = SmoothingMode.AntiAlias;
Метод Назначение
665
Как уже говорилось, позволяет указать метод устранения
SmoothingMode ступенчатости (Anti Aliasing) при выводе примитивов – линий и
геометрических фигур.
TabPage:
Text: Объекты и градиент
Cursor: Hand
666
На второй вкладке разместим сначала элемент Panel (
Panel:
(Name): panel1
BackColor: Black
AutoScroll: True
Button:
(Name): button1
Text: Прямоугольник
Button:
(Name): Button2
Text: Треугольник
Button:
(Name): Button3
Text: Круг
667
Открываем код нашей формы LWP19Main (файл LWP19Main.cs). Ищем:
public LWP19Main()
{
InitializeComponent();
Добавляем после:
DoubleBuffered = true;
Ищем:
Добавляем после:
if (bDrawFigure1)
{
// Заливаем цветом прямоугольник
g.FillRectangle(brush2, new Rectangle(100, 150, 200, 100));
}
if (bDrawFigure2)
{
// Заливаем цветом треугольник
g.FillPolygon(brush2, triangle);
}
if (bDrawFigure3)
{
668
// Заливаем цветом эллипс
g.FillEllipse(brush1, new Rectangle(175, 175, 50, 50));
}
}
Следующая вкладка будет реализовывать вот что: откроем её, нажмём где-
нибудь на свободном участке вкладки левую кнопку мыши и потащить курсор.
Выделенный участок инвертируется (инвертируются его цветовая палитра) даже за
пределами формы — это такое взаимодействие высокоуровневой GDI+ и
низкоуровневой GDI...
TabPage:
Text: Объекты и градиент
Cursor: Cross
669
Рис. 6. 1. Модифицированная форма приложения и расстановка необходимых
элементов управления на третьей вкладке
if (e.Button == MouseButtons.Left)
{
// Фиксируем стартовую точку
frameRect.Location = new Point(e.X, e.Y);
frameRect.Size = new Size(0, 0);
}
base.OnMouseDown(e); // Вызываем базовый метод
}
if (e.Button == MouseButtons.Left)
{
DrawFrame(); // Стираем старый прямоугольник
670
frameRect.Width = e.X - frameRect.Left;
frameRect.Height = e.Y - frameRect.Top;
DrawFrame(); // Рисуем новый прямоугольник
}
base.OnMouseMove(e); // Вызываем базовый метод
}
if (e.Button == MouseButtons.Left)
{
DrawFrame(); // Стираем старый прямоугольник
frameRect.Size = new Size(0, 0);
}
base.OnMouseUp(e); // Вызываем базовый метод
}
if (!frameRect.Size.IsEmpty)
{
Rectangle r = RectangleToScreen(frameRect);
// Собственно, инвертируем цвета
ControlPaint.FillReversibleRectangle(r, Color.FromArgb(40, 0, 0, 160));
}
}
TabPage:
671
Text: Векторные часы
Cursor: AppStarting
Timer:
(Name): timer1
Interval: 1000
Этот код нужен вот для чего: если выбираем активной вкладкой, вкладку номер
четыре, то запускаем таймер.
672
Point ptCenter = new Point(tabPage4.ClientRectangle.Width / 2,
tabPage4.ClientRectangle.Height / 2);
// Вычисляем радиус циферблата на основе размеров вкладки
int radius = Math.Min(tabPage4.ClientRectangle.Width,
tabPage4.ClientRectangle.Height) / 2;
using (LinearGradientBrush br = new
LinearGradientBrush(tabPage4.ClientRectangle, Color.White, Color.Azure,
LinearGradientMode.BackwardDiagonal))
{
//br.SetSigmaBellShape(.5f, 1.0f);
// Заполняем циферблат градиентом
g.FillEllipse(br, ptCenter.X - radius, ptCenter.Y - radius, radius * 2,
radius * 2);
}
// Рисуем эллипс цифербалата
using (Pen pen = new Pen(Color.Black))
g.DrawEllipse(pen, ptCenter.X - radius, ptCenter.Y - radius, radius * 2,
radius * 2);
// Цикл минуты
for (int minute = 0; minute < 60; minute++)
{
Point pt = RadialPoint(radius - 10, minute);
using (SolidBrush br = new SolidBrush(Color.Black))
{
if ((minute % 5) == 0)
g.FillRectangle(br, pt.X - 3, pt.Y - 3, 6, 6);
else
g.FillRectangle(br, pt.X - 1, pt.Y - 1, 2, 2);
}
}
673
Компилируем (Debug) и запускаем. Переходим на закладку «Векторные часы»,
чем запускаем таймер, заголовок вкладки будет отображать текущее время, а на самой
вкладке видим круглые стрелочные часы собранные из примитивов. Мило!
На этот раз будем имитировать горение огня (стена огня). Для создания эффекта огня
будем использовать генерацию случайных чисел каждую миллисекунду. Однако,
отображать сам огонь будем не на вкладке TabControl, а в новой форме. Это связано с
«некрасивым» обновлением вкладки при рисовании огня (возникает заметное
мерцание).
TabPage:
Text: Огонь!!!
Cursor: No
if (e.TabPage.Name == tabPage5.Name)
{
tabPage5.BackColor = Color.Black;
LWP19Fire Fire = new LWP19Fire();
Fire.ShowDialog();
}
using System.Drawing.Imaging;
Добавим после:
674
// Конец: вкладка "Огонь!!!"
public LWP19Fire()
{
InitializeComponent();
// Инициализируем источник для последующего использования
random = new Random();
// Устанавливаем начальные параметры вкладки
this.Text = "Расширенная работа с GDI+ (C#) :: форма для
вкладки \"Огонь!!!\"";
this.ClientSize = new Size(width, height);
this.MaximizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.BackColor = Color.Black;
SetStyle(ControlStyles.Opaque, true);
// Вызываем событие рисования (для метода DoFire)
this.Paint += new PaintEventHandler(this.DoFire);
// Генерируем палитру 640x480 (на основе Bimtap с 256 цветами на пиксель)
buf = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
ColorPalette pal = buf.Palette;
// Заполняем палитру, используя 64-цветные блоки:
// чёрный с красным, красный с желтым, желтый с белым, белый.
// Так как каждый диапазон составляет 64 цветов и RGB охватывает 256
значений,
// использовуем левый сдвиг (<<) по массиву
for (int i = 0; i < 64; i++)
{
pal.Entries[i] = Color.FromArgb(i << 2, 0, 0);
pal.Entries[i + 64] = Color.FromArgb(255, i << 2, 0);
pal.Entries[i + 128] = Color.FromArgb(255, 255, i << 2);
pal.Entries[i + 192] = Color.FromArgb(255, 255, 255);
}
buf.Palette = pal;
}
675
for (i = bufdata; i < bufbottom; i++)
{
v = *i + *(i + 1) + *(i + height) + *(i + height - 1);
v /= 4;
if (v < 0) v = 0;
*i = (Byte)v;
}
}
// Разблокируем изображение и рисуем на его на форме
buf.UnlockBits(buflock);
e.Graphics.DrawImageUnscaled(buf, 0, 0);
this.Invalidate();
}
Последняя вкладка будет вызывать дождь. Внутри вкладки. Опять же, как и в
случае с часами и огнём, не будем использовать сторонних ресурсов. Разве что можно
добавить звуки дождя.
TabPage:
Text: Дождик
Cursor: PanSouth
TabPage:
(Name): timer2
Interval: 60
PictureBox:
(Name): pictureBox1
Anchor: Top, Bottom, Left, Right
676
// Удаление от экрана
private float _depth;
// Положение
private PointF _location;
// Прямоугольник для отрисовки
private Rectangle _rectangleToDraw;
// Скорость
private PointF _speed;
677
И событие Paint для PictureBox:
678
Рис. 10. 2. Модифицированное приложение Windows Forms: вкладка «Объекты и
градиент»
679
Рис. 10. 3. Модифицированное приложение Windows Forms: вкладка «Мой монитор
сломался?!»
680
Рис. 10. 4. Модифицированное приложение Windows Forms: вкладка «Векторные часы»
681
Рис. 10. 5. Модифицированное приложение Windows Forms: вкладка «Огонь!!!»
682
Рис. 10. 6. Модифицированное приложение Windows Forms: вкладка «Дождик»
if (e.TabPage.Name == tabPage6.Name)
{
timer2.Enabled = true;
player.SoundLocation = "/Raindrop.wav";
player.Play();
}
else
{
timer2.Enabled = false;
player.Stop();
}
using System.Media;
683
можно загрузить по ссылке в конце этого материала (сслыка доступна в программном
продукте).
Содержание
56.Вводная часть
57.Создание приложения Windows Forms
58.Модификация приложения Windows Forms: запуск и завершение
приложения Inventor
59.Модификация приложения Windows Forms: создание новых документов
60.Модификация приложения Windows Forms: доступ к элементам
документов Inventor
61.Завершающая часть
62.О приложении к Лабораторной работе № 20
1. Вводная часть
684
2D/3D моделирование;
создание изделий из листового материала и получение их разверток;
разработка электрических и трубопроводных систем;
проектирование оснастки для литья пластмассовых изделий;
динамическое моделирование;
параметрический расчет напряженно-деформированного состояния деталей и
сборок;
визуализация изделий;
автоматическое получение и обновление конструкторской документации
(оформление по ЕСКД).
Функциональные возможности:
685
Рис. 1. 1. Анимация, полученная с помощью Autodesk Inventor
Студенческие лицензии:
Студенческие версии Autodesk Inventor, предназначенные исключительно для
использования студентами и преподавателями в образовательных целях, доступны для
бесплатной загрузки с сайта Образовательного сообщества Autodesk . Функционально
такая версия Autodesk Inventor ничем не отличается от полной, за одним исключением:
все файлы, созданные или отредактированные в ней, имеют специальную пометку (так
называемый educational flag), которая будет размещена на всех видах.
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
686
использовать «фреймворк» (набора компонентов для написания программ). В нашем
случае выберем .NET Framework 4.
687
Рис. 2. 3. Вводим данные нового проекта приложения Windows Forms
688
Рис. 2. 4. Обозреватель решений: состав проекта приложения Windows Forms
сформированного средой разработки
689
Рис. 2. 5. Запуск приложения Windows Forms по конфигурации Debug
Для начала изменим размер нашей единственной формы. Для этого можно
потянуть за уголок в нужном направлении на странице визуального представления
формы1. Но также размер можно менять на панели свойств этой формы. Для этого
нужно поменять значение размера в пикселях (высоту и ширину) в поле Size.
690
^ Поменяем размер формы.
(Name): B_Open
Text: Запустить AIP 2012
Size: 1550; 23
(Name): B_Close
Text: Завершить работу AIP 2012
Size: 155; 23
(Name): GB_Open
Text: Запуск и завершение приложения Inventor
691
Расставим эти элементы как показано на рисунке ниже:
692
{
LaunchInventor(true);
}
if (aInventor.Length == 0)
{
// Запускаем экземпляр приложения Inventor
Process InventorProcess = System.Diagnostics.Process.Start("C:\\Program
Files\\Autodesk\\Inventor 2012\\Bin\\Inventor.exe");
B_Close.Enabled = true;
B_Open.Enabled = false;
Thread.Sleep(20000);
}
// Подключаемся к запущенному экземпляру Inventor
oApp1 =
(Inventor.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Inventor.Ap
plication");
if (oApp1 == null)
{
return false;
}
// Предотвращаем выскакивание сообщений об ошибках
oApp1.SilentOperation = true;
// Делаем экземпляр приложения видимым
oApp1.Visible = bVisible;
return true;
}
// Закрываем экземпляр Inventor
private bool CloseInventor()
{
// quit Inventor - we're done with it
if (oApp1 != null)
{
oApp1.Quit();
oApp1 = null;
}
return true;
}
using System.Threading;
using System.Diagnostics;
using Inventor;
using System.Reflection;
693
Найдём в этом же файле:
И добавим после:
public LWP20Main()
{
InitializeComponent();
B_Close.Enabled = false;
}
Рис. 3. 4. Запуск приложения Inventor после нажатия кнопки «Запустить AIP 2012»
694
Единственно «но». Популярность разработки приложений на C# «под и для»
Inventor достаточно невысокая по сравнению с тем же «родным» для корпорации
Autodesk Inventor Visual Basic for Application и Visual Basic .NET.. Но найти
необходимую документацию всё же можно.
public LWP20Main()
{
InitializeComponent();
B_Close.Enabled = false;
B_Create.Enabled = false;
}
Найдём:
Добавим после:
B_Create.Enabled = true;
695
B_Open.Enabled = true;
B_Create.Enabled = false;
}
696
Следующая часть нашего приложения сможет, например по имени любой части
сборки вводимой в текстовом поле скрыть или показать этот элемент на сборке в
Inventor.
(Name): B_Start
Text: Инициализировать сборку
Size: 155; 23
(Name): B_Hide
Text: Скрыть
Size: 100; 23
(Name): B_Show
Text: Показать
Size: 100; 23
(Name): TB_Name
Text: Имя элемента
Size: 100; 23
(Name): OFD_Name
Filter: Сборка *.iam|*.iam
Добавим после:
697
Событие Click кнопки B_Start:
public LWP20Main()
{
InitializeComponent();
B_Close.Enabled = false;
B_Create.Enabled = false;
B_Start.Enabled = false;
B_Show.Enabled = false;
B_Hide.Enabled = false;
}
698
B_Hide.Enabled = false;
}
Найдём:
Thread.Sleep(20000);
Добавим до:
B_Start.Enabled = true;
6. Завершающая часть
699
Рис. 6. 1. Работа кнопки Скрыть: скрытая Деталь1 файла сборки Сборка1.iam
Содержание
1. Вводная часть
2. Основные положения при работе с F#
3. Создание приложения «Учебник по F#»
4. Создание консольного приложения
5. О приложении к Лабораторной работе № 21
1. Вводная часть
700
предполагает явного хранения состояния программы. Соответственно, не предполагает
оно и изменяемость этого состояния (в отличие от императивного, где одной из базовых
концепций является переменная, хранящая своё значение и позволяющая менять его
по мере выполнения алгоритма). F# — язык функционального программирования для
платформы .NET.
Первая версия языка появилась в 2005 году. С тех пор вокруг F# стало
формироваться сообщество. За счёт поддержки функциональной парадигмы язык
оказался востребован в научной сфере и финансовых организациях. Во многом
благодаря этому Microsoft решила перевести F# из статуса исследовательских проектов
в статус поддерживаемых продуктов и поставить его в один ряд с основными языками
платформы .NET. И это несмотря на то, что в последнее время всё большую активность
проявляют динамические языки, поддержка которых также присутствует в .NET
Framework. 12 апреля 2010 года свет увидела новая версия флагманского продукта
для разработчиков — Microsoft Visual Studio 2010, которая поддерживает разработку
на F# прямо из коробки, то есть можно создавать приложения сразу после установки
среды разработки.
701
Рис. 2. 1. Fsi.exe
Иногда можно встретить в начале F# кода директиву #light on. Эта директива
отключает режим совместимости синтаксиса с OCaml, делая отступы в коде значимыми
(как, например в Python или Haskell). В последних версиях языка облегчённый режим
включен по умолчанию, поэтому необходимости в указании директивы #light больше
нет.
702
3. Создание приложения «Учебник по F#»
Для начала, надо создать пустой проект, для этого выполним последовательно:
Файл -> Создать -> Проект… (также можно просто нажать сочетание клавиш
Ctrl+Shift+N или пункт «Создать проект…» на Начальной странице):
703
Рис. 3. 2. Создание нового проекта
704
Рис. 3. 4. Вводим данные нового проекта «Учебник по F#»
705
Рис. 3. 5. Исходный код консольного приложения сформированного средой разработки
let c = Console.ReadKey()
Без точки с запятой как в C#... Код служит «паузой» для окна консоли после
компиляции.
706
Рис. 3. 5. Запуск приложения «Учебник по F#» по конфигурации Debug
707
Рис 3. 6. Результат выполнения выделенного участка кода в окне F# Interactive
708
Ни одно введение в язык программирования не обходится без программы «Hello,
World». F# не станет исключением.
709
Рис. 3. 8. Результат выполнения новых участков кода в приложении «Учебник по F#»
Оператор let. Это вообще самый важный оператор. Формально let присваивает
идентификатору значение. «Определяет переменную», но это было бы неверно.
Идентификаторы в F# имеют природу двойственную. Во-первых, однажды
определенный идентификатор может так никогда и не измениться. Именно этот момент
позволяет писать программы, не нарушающие параллельность обработки: изменение
состояния язык F# не приветствует. Во-вторых, идентификатор может относиться не
только к примитивному или объектному типу, как в C# и Visual Basic, но и к
функциональному типу, подобному тем, что встречаются в LINQ.
Обратим внимание ещё и на то, что явно тип идентификатора никогда не
указывается. Идентификатор результата, к примеру, никогда не определяется — он
выводится из правой части выражения, следующего за ним. Эта функция известна как
вывод типа. Она свидетельствует о способности компилятора проанализировать код,
определить возвращаемое значение и автоматически его использовать. Аналогичным
образом действуют новые выражения с выводимым типом в C#, содержащие ключевое
слово var.
let add a b =
a + b
Программа выше делает именно то, что от неё ожидается: складывает числа a и
b и неявно возвращает результат вызывающей стороне. Технически, каждая функция
F# возвращает значение, даже если оно по природе своей является не значением, а
особой единицей под названием unit.
710
Бывают ситуации, в которых функция должна игнорировать передаваемые ей
параметры. Тогда в F# используется знак нижнего подчеркивания — в качестве
заполнителя для параметра. Добавим следующий код в наш «учебник»:
let add a b = a + b
let return10 _ = add 5 5
let ten = return10 12
printf "\n\nДесять = %d\n" ten
Результат:
let add5 a =
add a 5
711
переводе на язык функций C# можно было бы сказать, что десять — это экземпляр
делегата метода с параметризацией типов, тип которого в действительности лучше
просто игнорировать (только по правилам C# это невозможно):
open System.Threading
printf "\n"
let printWithThread str =
printfn "[ID потока = %d] %s" Thread.CurrentThread.ManagedThreadId str
let evals =
let z = 1.0
[ async { do printWithThread "Вычисляем z*z\n"
return z + z };
async { do printWithThread "Вычисляем sin(z)\n"
return (sin z) };
async { do printWithThread "Вычисляем log(z)\n"
return (log z) } ]
let awr =
async { let! vs = Async.Parallel evals
do printWithThread "Вычисляем v1 + v2 + v3\n"
return (Array.fold (fun a b -> a + b) 0.0 vs) }
712
В нём показана работа асинхронных рабочих потоков в упрощенном,
удобоваримом виде. Если не вдаваться в подробности, evals является массивом
функций, которые должны быть выполнены. Каждая из них помещается в очередь на
выполнение путем вызова Async.Parallel. При выполнении становится ясно, что
функции, входящие в массив evals, фактически находятся в отдельных потоках, идущих
от функции в awr. Хотя, по причине природы пула потоков .NET, часть функций из
массива evals или даже все функции могут выполняться в одном потоке, что заметно
при выводе результата: части некоторых строк при печати окажутся не в том месте.
Факт выполнения функций в пуле потоков .NET ещё раз подтверждает отличную
приспособленность языка F# ко взаимодействию с нижележащей средой выполнения.
То, что он опирается на библиотеку классов .NET там, где другие функциональные
языки используют специальные методы (например, многопоточность), означает, что в
программах на C# можно использовать библиотеки и модули F# — и наоборот.
Для начала нужно создать новый проект. Основу на этот раз станет тип проекта
Visual F#: Приложение F#. Имя будет LWP21Console.
713
Рис. 4. 1. Вводим данные нового проекта «Приложение F#»
714
Рис. 4. 4. Пример установленного шаблона консольного приложения для F#
Либо можно поискать шаблоны в сети Интернет (окно Создать проект ->
Шаблоны в Интернете):
715
Рис. 4. 5. Шаблоны в Интернете: доступные шаблоны
716
for сообщение in ["\nПриготовиться!"; "3"; "2"; "1"; "Поехали!\n"] // Выводим
сообщения цикла последовательно, через одну секунду
do
printfn "%s" сообщение
ждать 1000
let начало = сейчас() // Фиксируем время старта
[1..время] |> Seq.sumBy (fun i -> // Фиксируем сумму правильных результатов (1), 0 -
неправильные результаты
let a, b = случайно.Next 13, случайно.Next 13
printf "%d x %d = " a b
let введено = ввод()
match преобразование введено with
| true, ответ when ответ = a * b -> // Истина, если ответ верен (a * b)
использовать зелёный (fun () -> printfn "Правильно!")
1
| _, _ ->
сигнал()
использовать красный (fun () -> printfn "%d x %d = %d" a b (a*b))
0
) |> (fun счёт ->
let получить = (сейчас() - начало).ToString("c") // Получаем итоговое время и
преобразуем в строку
// Подстановка параметров: сначала подставляем результат работы блока "[1.."
(число верных ответов),
// потом общее число заданий "..время]" (число срабатывания блока),
// потом время на ответы
printfn "\nИгра завершена!\n%d правильных ответов из %d заданий, за время: %s.\n"
счёт время получить
)
while true do
игра (задания) // Число заданий
ждать 5000
printfn "Играть снова?\nДля продолжения игры нажмите любую клавишу . . ."
ожидание() |> ignore
while true do
игра (задания) // Число заданий
717
Рис. 4. 6. Результат работы консольного приложения игры «Умножение на время»
Содержание
1. Вводная часть
2. Создание приложения «Приложение F#»
3. Модификация приложения F#: match
4. Модификация приложения F#: создание форм и рисование объектов на
форме
5. Модификация приложения F#: работа с базой данных Microsoft Access
6. О приложении к Лабораторной работе № 22
1. Вводная часть
718
В предыдущей работе было рассказано, что F# — это язык функционального
программирования для платформы .NET. В данной работе будет продолжена работа с
этим языком на новых, более объёмных и содержательных примерах.
719
Для начала, надо создать пустой проект, для этого выполним последовательно:
Файл -> Создать -> Проект… (также можно просто нажать сочетание клавиш
Ctrl+Shift+N или пункт «Создать проект…» на Начальной странице):
720
Рис. 2. 3. Окно создания нового проекта
721
Рис. 2. 4. Вводим данные нового проекта «Приложение F#»
722
Рис. 2. 5. Обозреватель решений: состав проекта приложения F# сформированного
средой разработки
723
Рис. 2. 7. Пример установленного шаблона консольного приложения для F#
Либо можно поискать шаблоны в сети Интернет (окно Создать проект, группа
Шаблоны в Интернете):
724
Рис. 2. 8. Шаблоны в Интернете: доступные шаблоны
725
Рис. 3. 1. Добавление ссылки: добавляем ссылку на компоненты Windows Forms
Нам понадобятся:
System.Windows.Forms;
System.Data;
System.Drawing;
// Выражение match
match test-expression with
| pattern1 [ when condition ] -> result-expression1
| pattern2 [ when condition ] -> result-expression2
| ...
// Шаблон функции
function
726
| pattern1 [ when condition ] -> result-expression1
| pattern2 [ when condition ] -> result-expression2
| ...
727
Функция сопоставления шаблонов, продемонстрированная в предыдущей
синтаксической конструкции, представляет собой лямбда-выражение (ключевое
слово fun), в котором сопоставление шаблонов выполняется непосредственно в
аргументе. Функция сопоставления шаблонов, продемонстрированная в предыдущей
синтаксической конструкции, эквивалентна следующему:
728
| 1 -> printfn "Вам подходит: Январь (31 день)"
| 2 -> printfn "Вам подходит: Февраль (28/29 дней)"
| 3 -> printfn "Вам подходит: Март (31 день)"
| 4 -> printfn "Вам подходит: Апрель (30 день)"
| 5 -> printfn "Вам подходит: Май (31 день)"
| 6 -> printfn "Вам подходит: Июнь (30 дней)"
| 7 -> printfn "Вам подходит: Июль (31 день)"
| 8 -> printfn "Вам подходит: Август (31 день)"
| 9 -> printfn "Вам подходит: Сетябрь (30 дней)"
| 10 -> printfn "Вам подходит: Октябрь (31 дней)"
| 11 -> printfn "Вам подходит: Ноябрь (30 дней)"
| 12 -> printfn "Вам подходит: Декабрь (31 день)"
| _ -> printfn "Вы ввели число не из диапазона"
printfn "\t\t\tНажмите клавишу Enter для продолжения..."
Console.ReadKey()
Console.Clear()
729
let exitButton = new Button(Text = "Выход", Location = new System.Drawing.Point(415,
167), Size = new Size(75, 23))
// Событие нажатия на кнопку "Выход"
exitButton.Click.Add(fun quit -> graphicForm.Close())
// Событие прорисовки элементов формы
graphicForm.Paint.Add(fun draw ->
let array = [|new Point(100, 150);new Point(250, 10);new Point(400, 150)|]
// Перо рисования (цвет: синий, толщина 10 пикселей)
let pen = new Pen(Color.Blue, Width = 10.0f)
// Создаём заливку
let brush = new SolidBrush(Color.LightGreen)
// Рисуем треугольник на основе массива точек
draw.Graphics.DrawPolygon(pen, array)
// Заполняем треугольник
draw.Graphics.FillPolygon(brush, array)
// Рисуем эллипс
draw.Graphics.DrawEllipse(pen, 10.0f, 10.0f, 100.0f, 100.0f)
// Заполняем эллипс
draw.Graphics.FillEllipse(brush, 10.0f, 10.0f, 100.0f, 100.0f))
// Добавляем на форму кнопку "Выход"
graphicForm.Controls.Add(exitButton)
// Запускаем новую форму
Application.Run(graphicForm)
printfn "\t\t\tНажмите клавишу Enter для продолжения..."
Console.ReadKey()
Console.Clear()
730
2010 и OneNote 2010 в комплекте с «офисом», нажатие кнопки «Печать» вызовет это
приложение. Также, код изменяет курсор на форме на один из стандартных (крест), но
можно использовать и свой (закомментированный код). Весь код такой:
731
Рис. 4. 2. Результат работы приложения F#: форма «Печать выбранного рисунка»
732
exitButton3.Click.Add(fun quit3 -> drawingForm.Close())
colorButton3.Click.Add(fun colors3 ->
// Вызываем диалог выбора цвета
if colorDialog3.ShowDialog() = DialogResult.OK then
// Сохраняем выбранный цвет
color3.Colors<-[|colorDialog3.Color|])
Application.Run(drawingForm)
printfn "\t\t\tНажмите клавишу Enter для продолжения..."
Console.ReadKey()
Console.Clear()
733
Рис. 5. 1. Содержимое таблицы базы данных Работники.mdb
Импортируем эту базу данных в сам проект. Для этого выполним: Проект ->
Существующий элемент... (Shift+Alt+A). После вставки базы в проект приложения
F#, перейдём на панель Свойства для файла базы и изменим значение свойства
«Копировать в выходной каталог» на «Всегда копировать».
734
dataGridView4.Columns.Add(chrfnamecol) |> ignore
dataGridView4.Columns.Add(chrlnamecol) |> ignore
dataGridView4.DataSource <- dataSet4.Tables.["[Главная таблица]"]
// Применяем шрифт для формы
dataForm.Font <- ffont
// Применям связь данных базы с DataGridView (имя стоблца в базе / имя столбца в
элементе)
chrnumbercol.DataPropertyName <- "Номер"
chrnumbercol.HeaderText <- "Номер"
chrfnamecol.DataPropertyName<-"Имя"
chrfnamecol.HeaderText<-"Имя работника"
chrlnamecol.DataPropertyName<-"Фамилия"
chrlnamecol.HeaderText<-"Фамилия работника"
// Добавляем элементы на форму
dataForm.Controls.Add(dataGridView4)
dataForm.Controls.Add(exitButton4)
dataForm.Controls.Add(searchButton4)
dataForm.Controls.Add(label14)
dataForm.Controls.Add(label24)
dataForm.Controls.Add(label34)
dataForm.Controls.Add(label44)
dataForm.Controls.Add(textBoxNumber4)
dataForm.Controls.Add(labelNumber4)
dataForm.Controls.Add(labelFirstName4)
dataForm.Controls.Add(labelLastName4)
// Связываем Label'ы со столбцами базы данных
labelNumber4.Text <- Convert.ToString(dataSet4.Tables.["[Главная
таблица]"].Rows.Item(0).Item(0))
labelFirstName4.Text <- Convert.ToString(dataSet4.Tables.["[Главная
таблица]"].Rows.Item(0).Item(1))
labelLastName4.Text <- Convert.ToString(dataSet4.Tables.["[Главная
таблица]"].Rows.Item(0).Item(2))
searchButton4.Click.Add(fun search->
// Обрабатываем номер строки индекса
let mutable introws = 0
// Определяем, была найдена запись или нет
let mutable blnfound = false
// Обрабатываем общее количество записей
let mutable inttotrec = Convert.ToInt32(dataSet4.Tables.["[Главная
таблица]"].Rows.Count)
// Обрабатываем данные вводимые пользователем
let strtext = Convert.ToString(textBoxNumber4.Text)
// До тех пор, пока совпадений не найдено и конец записей не
достигнут
while((blnfound = false) && (introws <= inttotrec-1)) do
let strempnum = Convert.ToString(dataSet4.Tables.["[Главная
таблица]"].Rows.Item(introws).Item(0))
// Сравниваем данные введённые в TextBox пользователем с нашей
таблицей ("Номер")
// Если есть совпадений, отображаем результат запроса
if strtext.ToUpper() = strempnum.ToUpper() then
blnfound<-true
labelNumber4.Text <- Convert.ToString(dataSet4.Tables.
["[Главная таблица]"].Rows.Item(introws).Item(0))
labelFirstName4.Text <- Convert.ToString(dataSet4.Tables.
["[Главная таблица]"].Rows.Item(introws).Item(1))
labelLastName4.Text <- Convert.ToString(dataSet4.Tables.
["[Главная таблица]"].Rows.Item(introws).Item(2))
// Сравниваем со следующей запись до появления совпадений
introws<-introws + 1
// Если совпадения не найдены
if blnfound = false then
MessageBox.Show("Запись не найдена!", "Работа с базой данных
Microsoft Access :: Сообщение об ошибке", MessageBoxButtons.OK,
MessageBoxIcon.Information) |> ignore)
735
exitButton4.Click.Add(fun exit->
dataForm.Close()
oleconn.Close())
Application.Run(dataForm)
printfn "\t\t\tНажмите клавишу Enter для продолжения..."
Console.ReadKey()
Console.Clear()
Application.Exit() // Завершаем приложение
Рис. 5. 1. Результат работы приложения F#: форма «Работа с базой данных Microsoft
Access»
736
23. Дополнительная лабораторная работа № 1
Ниже на рисунке показан процесс установка SDK (версии 1.1), а именно опции,
включённые перед установкой:
Установлен SDK (версии 1.1) будет в папку с нашей ОС: WINNT (Windows
2000), а именно в папку Microsoft.NET.
Для Windows 7 содержимое этой папки выглядит примерно так (Рис. 2):
737
Рис. 2. Содержимое папки Microsoft.NET (Microsoft Windows 7 Professional)
738
Рис. 4. Установка Windows SDK for Windows 7 and .NET Framework 4 (Web Setup)
Окончательная версия .NET Framework 4.0 была выпущена 12 апреля 2010 года
вместе с версией среды разработки Visual Studio 2010 и обновления для языка C#
(C# 4.0).
739
Как гласит заголовок этого материала, пока что, мы не будем запускать среду
разработки. Но она и не нужна, ведь можно использовать даже обычный Блокнот.
Данный способ полезен, когда нет нужды запускать визуальные среды разработки и
код достаточно простой, но можно компилировать и массивные программы с
множеством ресурсов. Также предполагается, что программист достаточно
квалифицирован, ведь наличие визуализации (хотя бы подсветки) написания кода
существенно облегчает процесс написания программ.
Нам необходимо всего два файла: csc.exe и cscui.dll. Оба файла можно найти в
папке Windows/Microsoft.NET/Framework (Рис. 1).
740
Ссылки для загрузки этих файлов приведены в конце этого материала.
using System;
using System.Globalization;
class MainApp
{
public static void Main()
{
Console.Title = "This is My First App!";
//
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.BackgroundColor = ConsoleColor.DarkBlue;
//
Console.WriteLine("Hello World!\nИ по-русски можно? Конечно, привет мир!\nThis is
my first application using C#!\nP.S. C# and F# FTW?\n");
//
Calendar MyCalendar = new GregorianCalendar();
CultureInfo MyCulture = new CultureInfo("ru-RU");
DateTime MyDate = new DateTime(2012, 12, 21, 18, 30, 0, 0);
//
Console.WriteLine("Время записанное в календаре: " +
MyCalendar.ToDateTime(MyDate.Year,
MyDate.Month,
MyDate.Day,
MyDate.Hour,
MyDate.Minute, 0, 0) + "\n");
//
DateTime now = DateTime.Now;
Console.WriteLine("Время запуска приложения: {0}\n", now);
//
Random rand = new Random();
int a = 0, b = 0;
Далее сохраним как Test.cs (в Блокноте: Сохранить как… -> Тип файла: Все
файлы). Такое расширение (.cs) имеют файлы кода для программ на Си-шарп (Рис. 3):
741
Рис. 3. Создание файла для компиляции
После этого, создаём в этой же папке ещё один текстовый файл, внутри
вписываем:
csc.exe Test.cs
pause
Сохраняем как Compile.bat (должен появиться новый .bat файл). Команда pause
нужна для того, чтобы окошко компиляции не исчезало сразу.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Test.cs
742
pause
743
Рис. 6. Компиляция при помощи командной строки Visual Studio (2010), русские
символы допускаются
В нашей папке появился новый файл Test.exe. Собственно это и есть наша
программа, скомпилированная только что без использования какой-либо среды
программирования. Это удобно лишь в том случае, если под рукой нет среды
разработки. Результат работы программы представлен на рисунке ниже:
744
Рис. 8. Рабочая программа (консольное приложение)
using System;
Как видим, также как и в C и C++ язык унаследовал символ «;» как символ
разделения операторов, однако можно писать код и без этого разделителя (частично).
class MainApp {
В Visual C#, весь код должен содержаться в методах класса. Таким образом, к
началу точки входа (в данном случае: в программу), необходимо сначала создать
класс. Имя класса, здесь не имеет значения. Далее, указывается точка входа:
745
Компилятор требует, что-то, что можно назвать Main(). Точка входа должна быть
маркирована как public, так и static. Кроме того, точка входа не имеет аргументов и
ничего не возвращает (но это как в и C++ зависит от сложности программ, например:
static void Main(string[] args)). Конструкция Main() должна начинаться с заглавной
буквы, но само слово – не зарезервировано (не подсвечено синим в среде Visual
Studio):
Здесь всё просто. Двойной правый слеш — комментарии (зелёным). Дальше идут
функции реализующие работу с консольным приложением. А именно класс
System.Console предоставляющий стандартные потоки (входной и выходной, а также
сообщения об ошибках) консольным приложениям. Первая строчка отвечает за
заголовок окна консольного приложения (Console.Title), вторая за фон под текстом, а
третья за цвет букв. Четвёртая же выводит строку в скобках без кавычек. \n
определяется как перевод курсора на следующую строку. Если System — это
пространство имён, Console — класс, определённый в этом пространстве, то метод
WriteLine — это метод определённый в этом классе (метод аналогичен процедуре,
функции или подпрограмме).
Для двух верхних строчек следующего куска кода, нам понадобилось расширить
пространство имён, используемого при компиляции строчкой, в противном случае мы
не смогли бы получить доступ определённым зарезервированным словам:
using System.Globalization;
746
//
Random rand = new Random();
int a = 0, b = 0;
Немного поясним несколько простых правил при написании кода, так как
зачастую подобные ошибки наиболее часто встречаемы и как следствие наиболее
неприятны.
Также в куске кода выше, показано, что можно написать код приложения даже в
одну строчку.
class MainApp
{
747
public static void Main()
{
while (System.Windows.Forms.MessageBox.Show("Hello World without ';'! Привет мир
без точек с запятой!", "This is My First Message Box!",
System.Windows.Forms.MessageBoxButtons.YesNoCancel) !=
System.Windows.Forms.DialogResult.Yes)
{ } // До тех пор, пока не будет нажата кнопка «Да», окно не закроется
}
}
Рис. 10. Результат работы кода без точек с запятой (нужно нажать «Да»)
Содержание
1. Вводная часть
2. Создание приложения Windows Foundation Presentation
3. Модификация приложения Windows Foundation Presentation:
добавление ресурсов
4. Модификация приложения Windows Foundation Presentation:
добавление исходного кода
5. Модификация приложения Windows Foundation Presentation:
оформление элемента ListBox
6. Завершающая часть
7. О приложении к данной работе
1. Вводная часть
748
Запускаем Visual Studio 2010, откроется Начальная страница:
Для начала, надо создать проект, для этого выполним последовательно: Файл -
> Создать -> Проект… (также можно просто нажать сочетание клавиш Ctrl+Shift+N
или пункт «Создать проект…» на Начальной странице):
749
Рис. 2. 2. Окно создания нового проекта
750
Рис. 2. 3. Вводим данные нового проекта приложений WPF
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace NewListBox
{
/// <summary>
/// Логика взаимодействия для App.xaml
/// </summary>
public partial class App : Application
{
}
}
<Application x:Class="NewListBox.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
751
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace NewListBox
{
/// <summary>
/// Логика взаимодействия для MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
<Window x:Class="NewListBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
752
Рис. 2. 4. Обозреватель решений: состав проекта приложения WPF сформированного
средой разработки
753
Рис. 2. 5. Макет формы MainWindows.xaml: отображение конструктора формы (сверху)
и представление этой формы в качестве рамзетки XAML (внизу)
754
Рис. 3. 1. Создание новой директории для файлов внутри проекта
Солнце (Sun.jpg):
Меркурий (Mercury.gif):
Венера (Venus.gif):
755
Земля (Earth.gif):
Марс (Mars.gif):
Юпитер (Jupiter.gif):
Сатурн (Saturn.gif):
Уран (Uranus.gif):
Нептун (Neptune.gif):
756
Плутон (Pluto.gif):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NewListBox.Planets
{
class SolarSystem
{
}
}
using System;
757
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
namespace NewListBox
{
public class SolarSystem
{
// объявление коллекции динамических данных
ObservableCollection<SolarSystemObject> и заполнение коллекции элементами
private ObservableCollection<SolarSystemObject> solarSystemObjects;
public SolarSystem()
{
this.solarSystemObjects = new ObservableCollection<SolarSystemObject>();
/* Добавляем новые объекты типа SolarSystemObject(Название, число: удаление
от солнца [а. е.], число: средний диаметр [км], ссылка на изображение внутри проекта,
описание) */
this.solarSystemObjects.Add(new SolarSystemObject("Солнце", 0, 1380000, new
Uri(@"Planets\Sun.jpg", UriKind.Relative), "Со́лнце — единственная звезда Солнечной
системы, вокруг которой обращаются другие объекты этой системы: планеты и их спутники,
карликовые планеты и их спутники, астероиды, метеороиды, кометы и космическая пыль."));
this.solarSystemObjects.Add(new SolarSystemObject("Меркурий", 0.38, 4880, new
Uri(@"Planets\Mercury.gif", UriKind.Relative), "Мерку́рий — самая близкая к Солнцу планета
Солнечной системы, обращающаяся вокруг Солнца за 88 земных суток."));
this.solarSystemObjects.Add(new SolarSystemObject("Венера", 0.72, 12103.6,
new Uri(@"Planets\Venus.gif", UriKind.Relative), "Вене́ра — вторая внутренняя планета
Солнечной системы с периодом обращения в 224,7 земных суток. Планета получила своё
название в честь Венеры, богини любви из римского пантеона. Венера — третий по яркости
объект на небе Земли после Солнца и Луны и достигает видимой звёздной величины в
−4,6."));
this.solarSystemObjects.Add(new SolarSystemObject("Земля", 1, 12756.3, new
Uri(@"Planets\Earth.gif", UriKind.Relative), "Земля́ — третья от Солнца планета Солнечной
системы, крупнейшая по диаметру, массе и плотности среди планет земной группы. Наша
родная планета."));
this.solarSystemObjects.Add(new SolarSystemObject("Марс", 1.52, 6794, new
Uri(@"Planets\Mars.gif", UriKind.Relative), "Марс — четвёртая по удалённости от Солнца и
седьмая по размерам планета Солнечной системы; масса планеты составляет 10,7 % массы
Земли."));
this.solarSystemObjects.Add(new SolarSystemObject("Юпитер", 5.20, 142984, new
Uri(@"Planets\Jupiter.gif", UriKind.Relative), "Юпи́тер — пятая планета от Солнца,
крупнейшая в Солнечной системе. Наряду с Сатурном, Ураном и Нептуном Юпитер
классифицируется как газовый гигант. Планета была известна людям с глубокой древности,
что нашло своё отражение в мифологии и религиозных верованиях различных культур:
месопотамской, вавилонской, греческой и других. Современное название Юпитера происходит
от имени древнеримского верховного бога-громовержца."));
this.solarSystemObjects.Add(new SolarSystemObject("Сатурн", 9.54, 120536, new
Uri(@"Planets\Saturn.gif", UriKind.Relative), "Сату́рн — шестая планета от Солнца и вторая
по размерам планета в Солнечной системе после Юпитера. Сатурн, а также Юпитер, Уран и
Нептун, классифицируются как газовые гиганты. Сатурн назван в честь римского бога
земледелия."));
this.solarSystemObjects.Add(new SolarSystemObject("Уран", 19.218, 51118, new
Uri(@"Planets\Uranus.gif", UriKind.Relative), "Ура́н — седьмая по удалённости от Солнца,
третья по диаметру и четвёртая по массе планета Солнечной системы. Была открыта в 1781
году английским астрономом Уильямом Гершелем и названа в честь греческого бога неба
Урана, отца Кроноса (в римской мифологии Сатурна) и, соответственно, деда Зевса."));
758
this.solarSystemObjects.Add(new SolarSystemObject("Нептун", 30.06, 49532, new
Uri(@"Planets\Neptune.gif", UriKind.Relative), "Непту́н — восьмая и самая дальняя планета
Солнечной системы. Нептун также является четвёртой по диаметру и третьей по массе
планетой. Масса Нептуна в 17,2 раза, а диаметр экватора в 3,9 раза больше таковых у
Земли. Планета была названа в честь римского бога морей."));
this.solarSystemObjects.Add(new SolarSystemObject("Плутон", 39.5, 2274, new
Uri(@"Planets\Pluto.gif", UriKind.Relative), "Плуто́н (134340 Pluto) — крупнейшая по
размерам, наряду с Эридой, карликовая планета Солнечной системы, транснептуновый объект
(ТНО) и девятое/десятое по величине небесное тело, обращающееся вокруг Солнца.
Первоначально Плутон классифицировался как планета, однако сейчас он считается одним из
крупнейших объектов (возможно, самым крупным) в поясе Койпера."));
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NewListBox
{
public class SolarSystemObject
{
// Название планеты
private string name;
759
}
// Описание
private string details;
760
Добавим последний необходимый класс для обработки положения
окружностей орбит в элементе управления. Назовём его ConvertOrbit.cs:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Data;
using System.Globalization;
namespace NewListBox
{
public class ConvertOrbit : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
double orbit = (double)value;
double factor = System.Convert.ToDouble(parameter, culture.NumberFormat); //
Получаем форматирование для единиц измерения (зависит от настроек операционной системы по
умолчанию, а именно от региона и региональных стандартов), parameter - получаем извне
return Math.Pow(orbit / 40, 0.4) * 700 * factor; // Возводим выражение за
запятой в степень 0.4, необходимо для определения положения в элементе управления в
пикселях? определяет значения для положения по X и Y, 700 - размер окна, factor -
получаем извне (parameter)
}
<Window x:Class="NewListBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:NewListBox"
Title="WPF :: Солнечная система в ListBox (C#)" Height="700" Width="700">
<!-- Ресурсы окна -->
<Window.Resources>
</Window.Resources>
</Grid>
</Window>
xmlns:local="clr-namespace:NewListBox"
Поменяли свойство Title, высоту (700) и ширину (700) окна. Для сетки
(Grid) установили положение внутренних элементов по вертикали и по
761
горизонтали (по центру), а также задали свойство отсечения лишнего у
элемента, чтобы сетка помещалась в окне. И в конце объявили пространство
объектов для ресурсов окна.
</Canvas>
</DataTemplate>
</Window.Resources>
762
<StackPanel Orientation="Horizontal">
<TextBlock Text="Орбита: " />
<TextBlock Text="{Binding Path=Orbit}" />
<TextBlock Text=" а. е." />
</StackPanel>
<TextBlock Text="{Binding Path=Details}"
TextWrapping="Wrap"/>
</StackPanel>
</Image.ToolTip>
</Image>
</Canvas>
</DataTemplate>
<!-- Стиль одного элемента в ListBox -->
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Left" Value="{Binding Path=Orbit,
Converter={StaticResource convertOrbit}, ConverterParameter=0.707}"/>
<!-- Отступ для элемента слева -->
<Setter Property="Canvas.Bottom" Value="{Binding Path=Orbit,
Converter={StaticResource convertOrbit}, ConverterParameter=0.707}"/>
<!-- Отступ для элемента снизу -->
<Setter Property="Template">
<!-- Меняем шаблон -->
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<!-- Вставляем в элемент ListBoxItem сетку -->
<!-- Вставляем окружность выделения -->
<Ellipse x:Name="selectedPlanet" Margin="-10"
StrokeThickness="4"/>
<!-- Устанавливаем радиус окружности (-10, -10, -10, -10) и
толщину пера (4) -->
<!-- ContentPresenter сообщает что окружность должна
отрисовывать не поточечное, с горизонтальным положением и вертикальным положением в
зависимости от шаблона первоначального элемента -->
<ContentPresenter SnapsToDevicePixels="{TemplateBinding
SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<!-- Событие "если выделен элемент" для ListBoxItem -->
<Trigger Property="IsSelected" Value="true">
<!-- Устанавливаем цвет и тип пера "штрих" -->
<Setter Property="Stroke" TargetName="selectedPlanet"
Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
763
</Setter>
</Style>
</Window.Resources>
</Style>
<!-- Стиль элемента управления ListBox -->
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<!-- Конечный размер ListBox, фон -->
<Canvas Width="590" Height="590" Background="Black" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
6. Завершающая часть
764
Компилируем приложение (Release) и запускаем. Результат работы показан
ниже (Рис. 6. 1):
765