C# для вундеркиндов. Часть 4. Программирование в .NET Framework Февраль 2007 г. Программировать на языке C# без .NET Framework — это все равно, что писать ручкой без пасты. Как ни води ею по бумаге, написать все равно ничего не удастся. Так что же такое .NET Framework? Это платформа программирования, разработанная корпорацией Microsoft. C# создавался как язык, предназначенный специально для .NET Framework. Платформа .NET Framework состоит из двух частей: 1. Во-первых, она включает огромную библиотеку классов, которые можно вызывать из программ на C#. Это избавляет от необходимости писать все самому. 2. Во-вторых, в ее состав входит среда выполнения, управляющая запуском и работой готовых программ (это происходит незаметно — вам не нужно будет ни о чем беспокоиться). При написании программы на языке C# (или на любом другом языке .NET) наряду с написанием собственного кода вызываются классы, хранящиеся в библиотеке. Библиотека .NET framework содержит такое количество классов, и некоторые из них настолько сложны, что мы даже не будем пытаться рассмотреть здесь все сразу. Вместо этого, в главах раздела мы расскажем только о некоторых классах .NET framework, которые на наш взгляд, будут для вас наиболее полезными. Со временем вы станете замечать, что есть классы, которыми вы пользуетесь чаще, чем другими. Такие классы вы постепенно освоите хорошо, ведь когда часто берешь любимую книгу в библиотеке, то тоже скоро запоминаешь, на какой полке ее искать. Вы должны учесть, что третью часть нужно не просто прочитать. В ней приведено много примеров программ, которые вы должны сами испытать в работе, а затем попытаться изменить их, чтобы добиться новых результатов. Каждый раз, когда, читая эти главы, вы встречаете отрывок текста, перед которым указано «код программы ...», вы можете использовать этот код для запуска в Visual C# Express. Хотя большинство приведенных примеров очень невелики по размеру, все они являются полноценными, действующими программами. Для их запуска обычно необходимо выполнить следующие действия. Запустите Visual C# Express. В меню File(«Файл») выберите команду Open project(«Открыть проект»), а затем перейдите к месту сохранения примеров программ. Откройте любой файл с расширением «.csproj». (Либо можно найти нужный пример с помощью проводника Windows и затем просто дважды щелкнуть файл .csproj.) Чтобы запустить программу, нажмите клавишу F5 (или щелкните мышью кнопку с зеленой стрелкой) Как изменять образцы программ и расширять их возможности Легко сказать: изменить программу, — можете подумать вы, — но откуда мне знать, в каком направлении ее можно изменить? В приведенных примерах используются лишь некоторые методы и классы .NET Framework, и как же узнать, какие еще методы и классы существуют? А узнать это можно несколькими способами. Вы можете просмотреть библиотеку классов .NET Framework SDK, которая включена в справочную систему Visual C# Express. Работая в Visual C# Express, нажмите клавишу «.» сразу после ввода имени пространства имен или класса и увидите список всех доступных для него классов, методов, полей и т.д. Например, введите слово «Console.» и увидите список методов и других членов класса Console. В случае с методом, если поставить открывающую скобку после имени метода, то можно увидеть типы параметров, которые он может принимать. Зачастую в C# используются одноименные методы с различными наборами параметров — в этом случае их список можно прокручивать при помощи клавиш со стрелками вверх и вниз. В следующем примере показано что произойдет, если набрать «Console.WriteLine(». Visual C# Express подскажет вам о существовании 19 различных способов вызова метода WriteLine. Мы прокрутили список при помощи клавиши со стрелкой вниз до 11-ой позиции (см. рисунок ниже). Введение Понятие «консоль» пришло к нам из тех времен, когда были популярны большие компьютеры, называвшиеся мэйнфреймами. У компании был один такой гигантский компьютер, который размещался в каком-нибудь отдельном помещении, а у служащих на рабочих местах устанавливалась только клавиатура и простенький монитор, называвшийся консолью. Клавиатура и монитор подключались к тому самому огромному «монстру», спрятанному от посторонних глаз в отдельной комнате. Такие мониторы не умели отображать графику — только текст. Клавиатура была основным устройством ввода, служащим для передачи информации в компьютер, а консоль являлась основным устройством вывода, позволявшим компьютеру представлять информацию пользователю. Сегодня мониторы большинства компьютеров имеют гораздо более совершенные возможности, умея представлять не только строки текста, но и, например, фотографии. Однако при выполнении многих задач не требуется никаких графических изысков. Например, программа, получающая какие-либо данные с сервера в Интернете и сохраняющая их в файле на вашем компьютере должна уметь выводить только два сообщения: «идет получение данных» и «готово». Зачем тратить время на разработку затейливого пользовательского интерфейса, который к тому же занимает много памяти? Именно по этой причине в библиотеку .NET включен класс для быстрого написания консольных приложений. Не стоит относиться снисходительно к консольным приложениям и считать их слишком примитивными. В действительности оказывается, что настоящие программисты предпочитают не тратить усилия на интерфейсные ухищрения и работают в основном с консольными приложениями. Конечно, если вашей программой будет пользоваться кто-то еще, то вы, вероятно, окажете ему большую услугу, сделав интерфейс чуть более дружественным, чем в консольном приложении. Некоторые полезные методы 1. Console.ReadLine — считывает строку символов, введенную с клавиатуры (или иного устройства ввода) 2. Console.Read — считывает число, введенное с клавиатуры (или иного устройства ввода) 3. Console.WriteLine — выводит текст на экран (или иное устройство вывода), начиная с новой строки 4. Console.Write — выводит на экран ряд символов без перехода на новую строку Пример программы 1 Следующая программа просто выводит на экран слово «Yo!» и после этого ожидает нажатия клавиши ВВОД. Код программы 1 using System; class PleaseSayYo { static void Main() { // Выводим слово на экран Console.Write("Yo!"); // Ожидаем нажатия клавиши ВВОД Console.ReadLine(); } } Пример программы 2 Следующая программа: просит пользователя ввести с клавиатуры слово; спрашивает пользователя, сколько раз это слово должно быть выведено на экран; выводит на экран введенное слово указанное количество раз, причем каждый раз с новой строки. Код программы 2 using System; class WriteAFewLines { static void Main() { Console.WriteLine("Type in your favourite word : "); // Сохраняем в строковой переменной введенное пользователем слово string favouriteWord = Console.ReadLine(); Console.WriteLine("How many times shall I write it? "); // Сохраняем в целочисленной переменной введенное число // (При неправильном вводе числа произойдет ошибка) int numberOfTimes = Convert.ToInt32(Console.ReadLine()); // Выводим на экран слово указанное количество раз for (int i = 0; i < numberOfTimes; i++) { Console.WriteLine(favouriteWord); } // Ожидаем нажатия клавиши ВВОД Console.ReadLine(); } } Введение Если вы хотите писать программы, похожие на привычные приложения Windows, вам наверняка понадобятся классы из пространства имен System.Windows.Forms. Они позволяют использовать кнопки, списки, текстовые поля, меню, окна сообщений и множество других «элементов управления». Элементы управления — это то, что вы помещаете на форму. Они могут использоваться для вывода информации, например текстовой (элемент управления Label) или графической (элемент управления PictureBox) или для выполнения определенных действий, например выбора значения или перехода к другой форме после нажатия кнопки. Возможно, вы будете использовать классы из System.Windows.Forms в большинстве своих программ на C#. Очевидно, что понятие «формы» в программировании родственно понятию «формы анкеты» или «формы документа» в обычной жизни. Форма — это то, на чем можно расположить различные элементы (текст, картинки, поля для заполнения и т.д.) в определенном порядке. Работая с формой, мы обычно читаем некоторую информацию, а затем заполняем определенные области. Понятие формы в программировании во многом похоже по смыслу: форма позволяет размещать текст, изображения, поля ввода, кнопки и т.п., добиваясь их точного расположения на экране. В консольном же приложении на экран могут выводиться только строки текста. Корпорация Microsoft предоставила в составе библиотеки классов .NET Framework огромное количество «элементов управления», которые можно помещать на формы. С помощью них вы сможете очень быстро создавать эффектные приложения, как только научитесь размещать их на форме. Некоторые полезные классы из пространства имен System.Windows.Forms Ниже приведены примеры классов элементов управления, которые можно размещать на формах. Label (Метка) Button (Кнопка) ListBox (Список) CheckBox (Флажок) RadioButton (Переключатель) MessageBox (Окно сообщений) Menu (Меню) TabControl (Управление вкладками) Toolbar (Панель инструментов) TreeView (Дерево) DataGrid (Сетка данных) PictureBox (Изображение) RichTextBox (Текстовое поле с поддержкой формата RTF) Примечание. Работа с примерами программ Windows Forms в Visual C# Express Чтобы поэкспериментировать с приведенными ниже примерами в C# Express, можно выбрать в меню File(«Файл») команду Open project(«Открыть проект») и открыть один из примеров программ в виде файла .csproj. Если же вы все-таки предпочтете набрать их самостоятельно «с нуля», вы должны учесть, что при создании нового проекта «Windows Application» (Приложение Windows) C# Express создает два файла .cs (с именами Form1.cs и Program.cs) и вставляет несколько исходных строк кода на языке C#. Таким образом, вы получаете простейшую, но полноценную программу. Чтобы упростить работу с приведенными ниже примерами, возможно, понадобится выполнить следующие действия. Удалите файл Form1.cs. Замените код в файле Program.cs на код примера, с которым вы работаете. В этом нет необходимости, если вы открываете программы с помощью команды «Открыть проект» в меню «Файл». Пример программы 3 Перед вами пример простейшего приложения Windows Forms. Все, что оно умеет, это создавать новую форму и выводить определенный текст в заголовок окна. Код программы 3 using System.Windows.Forms; class SimpleWindowsForm : Form { // Метод-конструктор нашего класса public SimpleWindowsForm() { // Указываем заголовок окна this.Text = "A really Simple Form"; } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new SimpleWindowsForm()); } } Пример программы 4 Этот пример тоже достаточно прост, но здесь мы делаем шаг вперед — размещаем на форме кнопку Код программы 4 using System.Windows.Forms; class SimpleWindowsFormWithButton : Form { Button button1; // Метод-конструктор нашего класса public SimpleWindowsFormWithButton() { // Указываем заголовок окна this.Text = "A simple form with a button"; // Добавляем кнопку в коллекцию элементов управления формы // Хотя на кнопке написано: "Click me" (Нажми меня), при нажатии ничего не происходит! button1 = new Button(); button1.Text = "Click me"; this.Controls.Add(button1); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new SimpleWindowsFormWithButton()); } } Пример программы 5 То, что мы поместили на форму кнопку, уже хорошо, но в предыдущем примере при нажатии на кнопку ничего не происходит. Это неинтересно. Нам нужно описать метод, который будет выполнять какое-либо действие при нажатии кнопки. Пусть при этом будет меняться текст в заголовке окна. Поскольку такой метод отслеживает наступление некоторого события (в нашем случае это нажатие кнопки) и затем каким-то образом обрабатывает его, он называется «обработчик события». Кроме того, нам понадобится привязать обработчик события к соответствующему событию, то есть к нажатию кнопки. Код программы 5 using System; using System.Windows.Forms; using System.Drawing; class FormWithWorkingButton : Form { // Метод-конструктор нашего класса public FormWithWorkingButton() { // Указываем заголовок окна this.Text = "Simple form with working button"; // Добавляем кнопку и привязываем ее к обработчику события Button button1 = new Button(); button1.Text = "Click me"; button1.Click += new System.EventHandler(Button1_Click); this.Controls.Add(button1); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new FormWithWorkingButton()); } // Обработчик события, срабатывающий при нажатии кнопки void Button1_Click(object sender, EventArgs e) { // Изменяем заголовок окна this.Text = "Button was clicked"; } } Пример программы 6 Прекрасно, наша программа умеет выполнять основные действия. Теперь добавим на форму несколько новых элементов управления, аккуратно разместим их и немного поработаем с ними. Возьмем элементы управления 4-х типов: Button, ListBox, MessageBox и PictureBox. Обратите внимание, что кроме System.Windows.Forms в этом примере упоминается пространство имен System.Drawing. Это необходимо из-за использования элемента управления PictureBox — для работы с изображениями требуются классы Drawing. Код программы 6 using System.Windows.Forms; using System.Drawing; class MyForm : Form { // Объявим элемент ListBox вне определения методов, поскольку // нам придется обращаться к нему из разных методов ListBox listBox1; // Метод-конструктор нашего класса public MyForm() { // Создадим элемент PictureBox, поместим в него изображение и добавим его на форму PictureBox pictureBox1 = new PictureBox(); pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; Bitmap image1 = new Bitmap("../../images/seashore.jpg"); pictureBox1.ClientSize = new Size(this.Width, 100); pictureBox1.Image = (Image)image1; this.Controls.Add(pictureBox1); // Создаем объект Button, определяем некоторые из его свойств и добавляем его на форму Button button1 = new System.Windows.Forms.Button(); button1.Location = new System.Drawing.Point(10, 120); button1.Text = "Click me"; button1.Click += new System.EventHandler(button1_Click); this.Controls.Add(button1); // Создаем объект ListBox, определяем некоторые из его свойств и добавляем его на форму listBox1 = new System.Windows.Forms.ListBox(); listBox1.Location = new System.Drawing.Point(10, 160); listBox1.Items.Add("Sparkly"); listBox1.Items.Add("Dull"); listBox1.Items.Add("Vivacious"); listBox1.Items.Add("Plain"); listBox1.Items.Add("Outstanding"); listBox1.SelectedIndex = 3; this.Controls.Add(listBox1); } // Обработчик события, срабатывающий при нажатии кнопки void button1_Click(object sender, System.EventArgs e) { // Выводим сообщение с указанием выбранного в списке пункта MessageBox.Show(this, "You have selected " + listBox1.SelectedItem, "Notification", MessageBoxButtons.OK); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new MyForm()); } } Пример программы 7 Итак, настала пора испытать свои силы. Чтобы продемонстрировать использование ряда новых полезных элементов управления, мы попробуем написать одну действительно большую программу. Объем кода может показаться вам пугающе большим, но эта программа может оказаться полезной в дальнейшем, когда вам нужно будет вспомнить, как использовать тот или иной элемент управления. При этом необязательно вчитываться в текст всей программы досконально, но когда у вас возникнут вопросы по использованию, например, элемента CheckBox, вы можете вернуться к ней и просмотреть ту часть, которая касается соответствующего элемента. Обратите внимание, что для того чтобы использовать элементы управления PictureBox и DataGridView особым образом, потребуются пространства имен System.Drawing, System.Data и System.Xml. Код программ ы7 using using using using using System; System.Windows.Forms; System.Drawing; System.Data; System.Xml; class FormWithManyControls : Form { TreeView treeView1; Panel panel1; CheckBox checkBox1, checkBox2; RadioButton radiobutton1, radioButton2; ListBox listBox1; // Метод-конструктор нашего класса public FormWithManyControls() { // Указываем размеры и заголовок окна this.Text = "A form dealing with many types of controls"; this.Width = 700; this.Height = 500; // Добавляем элемент TreeView в качестве своеобразного меню treeView1 = new TreeView(); treeView1.BackColor = Color.BurlyWood; treeView1.Dock = DockStyle.Left; treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(treeView1_AfterSelect); TreeNode tn = new TreeNode("Элементы"); tn.Expand(); tn.Nodes.Add(new TreeNode("[Очистить]")); tn.Nodes.Add(new TreeNode("Label")); tn.Nodes.Add(new TreeNode("Button")); tn.Nodes.Add(new TreeNode("CheckBox")); tn.Nodes.Add(new TreeNode("RadioButton")); tn.Nodes.Add(new TreeNode("ListBox")); tn.Nodes.Add(new TreeNode("TextBox")); tn.Nodes.Add(new TreeNode("TabControl")); tn.Nodes.Add(new TreeNode("DataGridView")); tn.Nodes.Add(new TreeNode("MainMenu")); tn.Nodes.Add(new TreeNode("ToolBar")); tn.Nodes.Add(new TreeNode("PictureBox")); tn.Nodes.Add(new TreeNode("RichTextBox")); treeView1.Nodes.Add(tn); this.Controls.Add(treeView1); // Добавляем панель для размещения остальных элементов управления panel1 = new Panel(); panel1.Dock = DockStyle.Right; panel1.BorderStyle = BorderStyle.Fixed3D; panel1.Width = this.Width treeView1.Width; this.Controls.Add(panel1); } // Обработчик событий, срабатывающий при выборе одного из узлов дерева TreeView private void treeView1_AfterSelect (object sender, System.Windows.Forms.TreeViewEventArgs e) { // Выполнение соответствующего действия при выборе любого из узлов if (e.Node.Text == "[Wipe clean]") { // Удаляем с панели все элементы управления panel1.Controls.Clear(); } else if (e.Node.Text == "Button") { // Добавляем на панель кнопку Button button1 = new Button(); button1.Text = "I'm a button"; button1.Location = new Point(150, 80); button1.Click += new EventHandler(button1_Click); panel1.Controls.Add(button1); } else if (e.Node.Text == "Label") { // Добавляем на панель метку Label label1 = new Label(); label1.Text = "This is a label. Labels are simply used " + "to write text at a point on the screen"; label1.Location = new Point(150, 10); label1.Width = 400; label1.Click += new EventHandler(label1_Click); panel1.Controls.Add(label1); } else if (e.Node.Text == "CheckBox") { // Добавляем на панель несколько флажков checkBox1 = new CheckBox(); checkBox1.Text = "I am sharp"; checkBox1.Location = new Point(10, 50); checkBox1.CheckedChanged += new EventHandler(CheckBox_CheckedChanged); panel1.Controls.Add(checkBox1); checkBox2 = new CheckBox(); checkBox2.Text = "I am modest"; checkBox2.Location = new Point(10, 70); checkBox2.CheckedChanged += new EventHandler(CheckBox_CheckedChanged); panel1.Controls.Add(checkBox2); } else if (e.Node.Text == "RadioButton") { // Добавляем на панель несколько переключателей radiobutton1 = new RadioButton(); radiobutton1.Text = "I am sharp"; radiobutton1.Location = new Point(370, 210); radiobutton1.CheckedChanged += new EventHandler(RadioButton_CheckedChanged); panel1.Controls.Add(radiobutton1); radioButton2 = new RadioButton(); radioButton2.Text = "I am dimwitted"; radioButton2.Location = new Point(370, 230); radiobutton1.CheckedChanged += new EventHandler(RadioButton_CheckedChanged); panel1.Controls.Add(radioButton2); } else if (e.Node.Text == "ListBox") { // Добавляем на панель список listBox1 = new ListBox(); listBox1.Items.Add("Green"); listBox1.Items.Add("Beige"); listBox1.Items.Add("White"); listBox1.Location = new Point(200, 180); listBox1.SelectedIndexChanged += new EventHandler(listBox1_SelectedIndexChanged); panel1.Controls.Add(listBox1); } else if (e.Node.Text == "TextBox") { // Добавляем на панель текстовое поле TextBox TextBox1 = new TextBox(); TextBox1.Text = "You can type here"; TextBox1.Location = new Point(10, 110); panel1.Controls.Add(TextBox1); } else if (e.Node.Text == "DataGridView") { // Добавляем на панель таблицу, заполненную данными из файла xml DataSet dataSet1 = new DataSet("A sample DataSet"); dataSet1.ReadXml("../../data/grades.xml"); DataGridView dataGridView1 = new DataGridView(); dataGridView1.Width = (panel1.Width / 2) - 10; dataGridView1.Height = 150; dataGridView1.Location = new Point(2, panel1.Height dataGridView1.Height - 5); dataGridView1.DataSource = dataSet1; dataGridView1.DataMember = "subject"; panel1.Controls.Add(dataGridView1); } else if (e.Node.Text == "TabControl") { // Добавляем на панель элемент управления вкладками // и наполняем каждую вкладку содержимым TabControl tabControl1 = new TabControl(); tabControl1.Location = new Point(10, 150); tabControl1.Size = new Size(165, 146); TabPage tabPage1 = new TabPage("Robynne"); PictureBox pictureBox1 = new PictureBox(); pictureBox1.Image = new Bitmap("../../images/robynne.jpg"); pictureBox1.Size = new Size(160, 120); tabPage1.Controls.Add(pictureBox1); tabControl1.TabPages.Add(tabPage1); TabPage tabPage2 = new TabPage("Her Dog"); PictureBox pictureBox2 = new PictureBox(); pictureBox2.Image = new Bitmap("../../images/chocolate.jpg"); pictureBox2.Size = new Size(160, 120); tabPage2.Controls.Add(pictureBox2); tabControl1.TabPages.Add(tabPage2); TabPage tabPage3 = new TabPage("Info"); tabPage3.BackColor = Color.White; Label label1 = new Label(); label1.Text = "Robynne lives in Cape Town, South Africa.\n\n" + "She has a dog named Chocolate, from the planet Woof," + " rapidly growing into her oversized ears."; label1.Dock = DockStyle.Fill; tabPage3.Controls.Add(label1); tabControl1.TabPages.Add(tabPage3); panel1.Controls.Add(tabControl1); } else if (e.Node.Text == "PictureBox") { // Добавляем на панель изображение PictureBox pictureBox1 = new PictureBox(); pictureBox1.Image = new Bitmap("../../images/ocean.jpg"); pictureBox1.BorderStyle = BorderStyle.Fixed3D; pictureBox1.Location = new Point(250, 25); pictureBox1.Size = new Size(300, 130); panel1.Controls.Add(pictureBox1); } else if (e.Node.Text == "RichTextBox") { // Добавляем поле для ввода текста с форматированием // Загружаем в него данные из файла XML RichTextBox richTextBox1 = new RichTextBox(); richTextBox1.LoadFile("../../data/grades.xml", RichTextBoxStreamType.PlainText); richTextBox1.WordWrap = false; richTextBox1.BorderStyle = BorderStyle.Fixed3D; richTextBox1.BackColor = Color.Beige; richTextBox1.Size = new Size((panel1.Width / 2) - 10, 150); richTextBox1.Location = new Point((panel1.Width / 2) + 10, panel1.Height - richTextBox1.Height 5); panel1.Controls.Add(richTextBox1); } else if (e.Node.Text == "MainMenu") { // Добавляем классическое «меню» (появляется в верхней части окна) MainMenu mainMenu1 = new MainMenu(); MenuItem menuItem1 = new MenuItem("File"); menuItem1.MenuItems.Add("Exit", new EventHandler(mainMenu1_Exit_Select)); mainMenu1.MenuItems.Add(menuItem1); MenuItem menuItem2 = new MenuItem("Background"); menuItem2.MenuItems.Add("Choose", new EventHandler(mainMenu1_ColorOwn_Select)); menuItem2.MenuItems.Add("White", new EventHandler(mainMenu1_ColorWhite_Select)); mainMenu1.MenuItems.Add(menuItem2); this.Menu = mainMenu1; MessageBox.Show("A main menu has been added at the top " + "left of the window. Try it out after clicking OK."); } else if (e.Node.Text == "ToolBar") { // Добавляем на панель элемент "панель управления" с кнопками быстрого вызова ToolBar toolBar1 = new ToolBar(); ImageList imageList1 = new ImageList(); imageList1.Images.Add(new Bitmap("../../images/open.gif")); imageList1.Images.Add(new Bitmap("../../images/close.gif")); imageList1.Images.Add(new Bitmap("../../images/undo.gif")); toolBar1.ImageList = imageList1; ToolBarButton toolBarbutton1 = new ToolBarButton("Open"); toolBarbutton1.ImageIndex = 0; toolBar1.Buttons.Add(toolBarbutton1); ToolBarButton toolBarbutton2 = new ToolBarButton("Close"); toolBarbutton2.ImageIndex = 1; toolBar1.Buttons.Add(toolBarbutton2); ToolBarButton toolBarButton3 = new ToolBarButton("Huh"); toolBarButton3.ImageIndex = 3; toolBar1.Buttons.Add(toolBarButton3); toolBar1.ButtonClick += new ToolBarButtonClickEventHandler(toolBar1_Click); panel1.Controls.Add(toolBar1); } } /* Обработчики событий для добавленных выше элементов управления */ // Обработчик события, срабатывающий при щелчке мышью на метке void label1_Click(object sender, System.EventArgs e) { MessageBox.Show ("Yes, labels can be clicked, although it's not normal to do so."); } // Обработчик события, срабатывающий при нажатии кнопки void button1_Click(object sender, System.EventArgs e) { MessageBox.Show("Yup, you were supposed to click me"); } // Обработчик события, срабатывающий при установке или снятии флажка void CheckBox_CheckedChanged(object sender, System.EventArgs e) { if (checkBox1.Checked && checkBox2.Checked) { MessageBox.Show("Good for you"); } else if (checkBox1.Checked) { MessageBox.Show("It's not good to be sharp without being modest"); } else if (checkBox2.Checked) { MessageBox.Show("Modesty is good. Pity you're not sharp too."); } else { MessageBox.Show("Oh dear, neither sharp nor modest eh?"); } } // Обработчик события, срабатывающий при нажатии переключателя void RadioButton_CheckedChanged(object sender, System.EventArgs e) { if (radiobutton1.Checked) { MessageBox.Show("Glad to hear it"); } else if (radioButton2.Checked) { MessageBox.Show("What a shame"); } } // Обработчик события, срабатывающий при выборе одного из пунктов списка void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) { if (listBox1.SelectedItem.ToString() == "Green") { treeView1.BackColor = Color.LightSeaGreen; } else if (listBox1.SelectedItem.ToString() == "Beige") { treeView1.BackColor = Color.Beige; } else if (listBox1.SelectedItem.ToString() == "White") { treeView1.BackColor = Color.White; } } // Обработчик события, срабатывающий при выборе в меню пункта "White" void mainMenu1_ColorWhite_Select(object sender, System.EventArgs e) { treeView1.BackColor = Color.White; } // Обработчик события, срабатывающий при выборе в меню цвета void mainMenu1_ColorOwn_Select(object sender, System.EventArgs e) { ColorDialog colorDialog1 = new ColorDialog(); colorDialog1.Color = treeView1.BackColor; colorDialog1.ShowDialog(); treeView1.BackColor = colorDialog1.Color; } // Обработчик события, срабатывающий при выборе в меню пункта "exit" void mainMenu1_Exit_Select(object sender, System.EventArgs e) { if ( MessageBox.Show("Are you sure you want to exit?", "Exit confirmation", MessageBoxButtons.YesNo) == DialogResult.Yes ) { this.Dispose(); } } // Обработчик события, срабатывающий при нажатии кнопки на панели инструментов void toolBar1_Click (object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { if (e.Button.Text == "Open") { MessageBox.Show("This could have opened a file, for example"); } else if (e.Button.Text == "Close") { MessageBox.Show("This could have closed a file, for example"); } else if (e.Button.Text == "Huh") { MessageBox.Show("Huh?"); } } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new FormWithManyControls()); } } Введение Классы, объединенные в пространство имен Drawing, позволяют работать с различными изображениями. Существует два основных типа компьютерных изображений: Точечные или растровые; Векторные. Растровые изображения представляют собой набор точек. Примером их могут служить фотографии и значки. Векторная графика — это изображения, составленные из определенных геометрических фигур: линий, окружностей, прямоугольников и т.д. Например, план дома удобно представлять в виде векторного изображения. Для начала продемонстрируем работу с растровой графикой. На компьютере часто приходится выполнять обработку изображений, например, работать с фотографиями. В библиотеке классов .NET Framework имеется для этого немало полезных средств. Пример программы 8 Эта программа находит изображение (в данном случае файл JPEG) на диске и отображает его на форме. Для этого нам понадобится какой-либо элемент управления, позволяющий отображать изображения. PictureBox прекрасно подходит для этой цели. Код программы 8 using System; using System.Windows.Forms; using System.Drawing; class PictureDisplayer : Form { Bitmap image1; PictureBox pictureBox1; // Метод-конструктор нашего класса public PictureDisplayer() { // Указываем размеры и заголовок окна this.Text = "Picture Displayer"; this.Size = new Size(302, 240); // Подготавливаем поле для размещения изображения pictureBox1 = new PictureBox(); pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; pictureBox1.BorderStyle = BorderStyle.Fixed3D; pictureBox1.ClientSize = new Size(300, 196); // Добавляем изображение в элемент PictureBox image1 = new Bitmap(@"../../images/lama.jpg"); pictureBox1.Image = (Image)image1; // Добавляем PictureBox (с изображением) на форму this.Controls.Add(pictureBox1); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new PictureDisplayer()); } } Пример программы 9 Следующая программа загружает фотографию с диска и после нажатия кнопки «flip» (Перевернуть) позволяет получить ее зеркальное отражение по горизонтали. Код программы 9 using System; using System.Windows.Forms; using System.Drawing; class PictureFlipper : Form { Button button1; Bitmap image1; PictureBox pictureBox1; // Метод-конструктор нашего класса public PictureFlipper() { // Указываем размеры и заголовок окна this.Text = "Picture Flipper"; this.Size = new Size(302, 240); // Добавляем на форму кнопку button1 = new Button(); button1.Text = "Flip"; button1.Location = new Point(100, 150); button1.Click += new System.EventHandler(button1_Click); this.Controls.Add(button1); // Добавляем элемент PictureBox на форму pictureBox1 = new PictureBox(); pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; pictureBox1.BorderStyle = BorderStyle.Fixed3D; pictureBox1.ClientSize = new Size(300, 196); // Добавляем изображение в элемент PictureBox image1 = new Bitmap(@"../../images/elephants.jpg"); pictureBox1.Image = (Image)image1; // Добавляем элемент PictureBox (с изображением) на форму this.Controls.Add(pictureBox1); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new PictureFlipper()); } // Обработчик события, срабатывающий при нажатии кнопки void button1_Click(object sender, EventArgs e) { // Flip the image along the X axis (horizontally) image1.RotateFlip(RotateFlipType.RotateNoneFlipX); // Повторно вставляем изображение в элемент PictureBox pictureBox1.Image = (Image)image1; // Обновляем заголовок окна this.Text = "Picture was flipped"; } } Теперь перейдем к примерам работы с векторной графикой — изображениям, составленным из отдельных геометрических фигур. Во всех этих примерах будет создаваться кнопка и обработчик событий, перехватывающий ее нажатие. Работа с графикой будет начинаться только после нажатия кнопки. Необходимо усвоить несколько важных принципов. Они вполне логичны, но не зная их, можно испытать некоторые затруднения. 1. В обычном мире чтобы нарисовать линию, окружность, прямоугольник или иную фигуру, необходимо сначала выбрать карандаш нужного цвета и толщины. Аналогично, для отрисовки на компьютере простейших фигур требуется предварительно создать объект Pen (Перо). Например, с помощью данного фрагмента кода создается объект Pen, рисующий зеленую линию толщиной 3 пикселя: Pen myGreenPen = new Pen( Color.Green, 3 ); 2. Для рисования фигур с заливкой потребуется нечто вроде кисти с красками. Для создания фигур с заливкой на компьютере требуется предварительно создать объект Brush (Кисть) и выбрать цвет заливки. Имеются различные типы кисти. В следующем фрагменте кода создается объект SolidBrush (Сплошная кисть) голубого цвета: SolidBrush myBlueBrush = new SolidBrush( Color.Blue ); Пример программы 10 В этой программе с помощью метода DrawSomeShapes рисуется линия, прямоугольник и эллипс («сплюснутая» окружность). Код программы 10 using System; using System.Windows.Forms; using System.Drawing; class SimpleShapeMaker : Form { // Метод-конструктор нашего класса public SimpleShapeMaker() { // Меняем цвет фона формы на белый this.BackColor = Color.White; // Добавляем на форму кнопку и привязываем ее к обработчику событий Button button1 = new Button(); button1.Text = "click me"; button1.Location = new Point(110, 10); button1.BackColor = Color.SteelBlue; button1.Click += new System.EventHandler(button1_Click); this.Controls.Add(button1); } // Обработчик события, срабатывающий при нажатии кнопки void button1_Click(object o, System.EventArgs e) { // Выполнение описанного нами метода DrawSomeShapes(); } // Метод для отрисовки на поверхности формы нескольких фигур void DrawSomeShapes() { // Подготовка области рисования на форме Graphics g = this.CreateGraphics(); // Подготавливаем перо, рисующее красную линию толщиной 3 пикселя Pen redPen = new Pen(Color.Red, 3); // С помощью пера рисуем прямую линию, прямоугольник и овал g.DrawLine(redPen, 140, 170, 140, 230); g.DrawRectangle(redPen, 50, 60, 50, 60); g.DrawEllipse(redPen, 150, 100, 100, 60); // Очистка g.Dispose(); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run( new SimpleShapeMaker() ); } } Пример программы 11 Попробуем теперь поиграть с мышкой. Работать с графикой обычно удобнее при помощи мыши, а не клавиатуры. В этом примере мы будем работать как с растровыми, так и с векторными изображениями, используя некоторые события мыши. Постараемся освоить некоторые новые действия с графикой, а именно с точечными рисунками. Мы не станем писать об этом целый очерк, но небольшое вступление просто необходимо, чтобы понять принципы работы приведенного ниже кода. Компьютерные программы формируют изображение на экране монитора, управляя цветом и яркостью маленьких точек, называемых пикселями. Цвет пикселя определяется тремя цветовыми компонентами: красной (red), зеленой (green) и синей (blue) (в языках программирования часто используется сокращение RGB). Цвет и яркость пикселя управляется путем изменения интенсивности компонент RGB, обычно в пределах от 0 до 255. Например: если red=255, green=0, blue=0, цвет пикселя будет ярко-красным; если red=255, green=255, blue=0, цвет пикселя будет желтым. Компьютер может отслеживать положение курсора мыши, определяемое координатами X и Y (горизонтальная и вертикальная координаты). Так, верхний левый угол экрана имеет координаты X=0 и Y=0. Код программы 11 using System; using System.Windows.Forms; using System.Drawing; class FunWithTheMouse : Form { // Объявляем объекты, к которым будем обращаться из разных методов PictureBox pictureBox1; Label label1; Point spotClicked; // Метод-конструктор нашего класса public FunWithTheMouse() { // Задаем размеры окна this.Size = new Size(640, 480); // Загружаем фотографию в элемент PictureBox и добавляем его на форму pictureBox1 = new PictureBox(); pictureBox1.Image = (Image)new Bitmap(@"../../images/foal.jpg"); pictureBox1.SizeMode = PictureBoxSizeMode.Normal; pictureBox1.Dock = DockStyle.Fill; this.Controls.Add(pictureBox1); // Добавляем метку с инструкциями в нижнюю часть экрана label1 = new Label(); label1.BackColor = Color.Wheat; label1.Dock = DockStyle.Bottom; label1.Text = "Drag rectangle with left mouse button (draw) or another " + "mouse button (lighten). To scribble, hold SHIFT and move mouse."; label1.TextAlign = ContentAlignment.MiddleCenter; this.Controls.Add(label1); // Привязываем PictureBox к обработчикам событий мыши this.pictureBox1.MouseDown += new MouseEventHandler(MouseButtonIsDown); this.pictureBox1.MouseUp += new MouseEventHandler(MouseButtonIsUp); this.pictureBox1.MouseMove += new MouseEventHandler(TheMouseMoved); } // Обработчик событий, срабатывающий при ПЕРЕМЕЩЕНИИ мыши public void TheMouseMoved(object sender, MouseEventArgs e) { // Если на клавиатуре нажата клавиша SHIFT if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) { // Подготовка области рисования на изображении System.Drawing.Graphics g = this.pictureBox1.CreateGraphics(); // Используем желтое перо System.Drawing.Pen yellowPen = new System.Drawing.Pen(Color.Yellow, 3); // Рисуем окружность (эллипс, вписанный в квадрат) // Верхний левый угол квадрата имеет координаты X и Y текущего положения мыши g.DrawEllipse(yellowPen, e.X, e.Y, 40, 40); // Очистка g.Dispose(); } } // Обработчик событий, срабатывающий при НАЖАТИИ кнопки мыши public void MouseButtonIsDown(object sender, MouseEventArgs e) { // Запоминаем точку, в которой произошло нажатие кнопки мыши. Когда // кнопка будет отпущена, нам понадобятся ее координаты spotClicked.X = e.X; // spotClicked.Y = e.Y; // горизонтальная координата вертикальная координата } // Обработчик событий, срабатывающий при ОТЖАТИИ кнопки мыши public void MouseButtonIsUp(object sender, MouseEventArgs e) { /* Пользователь отпустил кнопку мыши! */ // Создаем прямоугольник (пока он еще не виден), ограничивающий // область изображения, с которой пользователь будет работать Rectangle r = new Rectangle(); // Левый верхний угол соответствует точке, в которой была нажата кнопка мыши // Мы сохранили ее координаты с помощью описанного выше метода r.X = spotClicked.X; r.Y = spotClicked.Y; // Ширина и высота прямоугольника вычисляется // путем вычитания начальных координат мыши (в точке нажатия) // из текущих координат (в точке отжатия кнопки) r.Width = e.X - spotClicked.X; r.Height = e.Y - spotClicked.Y; if (e.Button == MouseButtons.Left) { /* Если была нажата и отпущена левая кнопка мыши рисуем видимый контур прямоугольника */ // Подготовка области рисования на изображении Graphics g = this.pictureBox1.CreateGraphics(); // Рисуем красный контур прямоугольника Pen redPen = new Pen(Color.Red, 2); g.DrawRectangle(redPen, r); } else { // Если была нажата другая кнопка, вызываем более сложный // метод, подсвечивающий область изображения ChangeLightness(r); } } // Метод, увеличивающий яркость выбранного участка изображения // путем увеличения яркости каждого пикселя этого участка public void ChangeLightness(Rectangle rect) { int newRed, newGreen, newBlue; Color pixel; // Копируем изображение, загруженное в PictureBox System.Drawing.Bitmap picture = new Bitmap(this.pictureBox1.Image); // Поскольку операция увеличения яркости может занять много времени, // необходимо предупредить об этом пользователя, если выбран большой участок if ( (rect.Width>150) || (rect.Height>150 ) ) { DialogResult result = MessageBox.Show ( "The area you selected is large and may take a long time to lighten", "Warning", MessageBoxButtons.OKCancel ); // При нажатии кнопки Cancel (Отмена) выходим из метода // и возвращаемся к месту его вызова if ( result == DialogResult.Cancel ) return; } /* Перебираем последовательно все пиксели данного участка и удваиваем значение яркости компонент RGB пикселей */ // Перебор по горизонтали слева направо ... for (int x = rect.X; x < rect.X + rect.Width; x++) { // и по вертикали сверху вниз ... for (int y = rect.Y; y < (rect.Y + rect.Height); y++) { // Считываем текущий пиксель pixel = picture.GetPixel(x, y); // Увеличиваем яркость цветовых компонент пикселя newRed = (int)Math.Round(pixel.R * 2.0, 0); if (newRed > 255) newRed = 255; newGreen = (int)Math.Round(pixel.G * 2.0, 0); if (newGreen > 255) newGreen = 255; newBlue = (int)Math.Round(pixel.B * 2.0, 0); if (newBlue > 255) newBlue = 255; // Присваиваем пикселю новые цветовые значения picture.SetPixel ( x, y, Color.FromArgb((byte)newRed,(byte)newGreen, (byte)newBlue) ); } } // Помещаем измененную копию изображения в PictureBox // чтобы изменения отобразились на экране this.pictureBox1.Image = picture; } static void Main() { // Создаем экземпляр класса формы Application.Run(new FunWithTheMouse()); } } Введение На практике большинство приложений должны работать с базами данных. Любой программист из крупной компании, подтвердит вам, насколько велика роль баз данных в мире вычислительных технологий. Программист, умеющий оперировать с базами данных, способен создавать самые разнообразные и действительно полезные приложения. Возможно, у вас на компьютере уже установлена система управления базами данных, например Microsoft Access. В качестве альтернативы можно установить Microsoft SQL Server Express Edition. С его помощью можно научиться работать с базами данных SQL Server, используемыми во многих крупнейших компаниях по всему миру. SQL Server Express включен в пакет установки Visual C# Express, так что возможно вы уже установили его. Для работы с базами данных в библиотеке .NET Framework предусмотрены классы пространства имен System.Data. База данных в корне отличается от таких объектов, как изображения или документы текстовых редакторов, которые часто называют неструктурированными. Информация в базе данных более структурирована. Обычно она содержит строки, содержащие данные одинакового типа и объединяемые в блоки, называемые таблицами. Таблица состоит из одного или нескольких столбцов , содержащих определенную информацию для каждой строки. Строки иногда называют записями, а столбцы — полями. Ниже представлена таблица базы данных, содержащая информацию о планетах. Она имеет следующие столбцы: PlanetName (название планеты), DistanceFromSun (расстояние до Солнца) и Inhabitants (обитатели). PLANET PlanetName DistanceFromSun Inhabitants Mercury 57909 Mercurians Venus 108200 Venusians Earth 149600 Earthlings Mars 227940 Martians Jupiter 778400 Jupiterians Znock 7208100 Znockers Saturn 1423600 Saturnians Uranus 2867000 Uranians Neptune 4488400 Neptunians Pluto 5909600 Plutonians Из приведенной таблицы видно, например, что Венера находится на расстоянии 108 200 тысяч километров от Солнца, а существа, ее населяющие, именуются венерианцами. Вот еще одна таблица, в которой содержатся сведения о ежегодной численности обитателей разных планет. Это совершенно секретная информация, нигде ранее не обнародованная. Она была получена с инопланетного звездолета, потерпевшего крушение в самой глубине пустыни Гоби. Это большая честь — иметь возможность ознакомиться с ней. Очевидно, представители инопланетной цивилизации тоже используют базы данных SQL Server Express, что и дало нам возможность привести здесь эти сведения в качестве примера. POPULATION PlanetName Year Population Mercury 2000 40000 Venus 2000 25 Earth 2000 6000000000 Mars 2000 325000 Jupiter 2000 8426300200 Znock 2000 550000 Saturn 2000 1000000 Uranus 2000 753425370 Neptune 2000 <NULL> Pluto 2000 <NULL> Mercury 2001 35000 Venus 2001 3 Earth 2001 6500000000 Mars 2001 326000 Jupiter 2001 8426300202 Znock 2001 8700 Saturn 2001 75000 Uranus 2001 844360002 Neptune 2001 <NULL> Pluto 2001 <NULL> Вы можете видеть, что в таблице есть две строки, касающиеся Венеры. Обратите внимание, что в 2000 году на Венере обитало 25 венерианцев, а в 2001 году их численность сократилось до трех. Вероятно, это произошло в результате извержений вулканов. Не следует путать базы данных с электронными таблицами. Хотя способ представления данных в электронных таблицах внешне похож на тот, что использовался в приведенных выше примерах, обработка данных происходит в них совершенно иначе. Язык SQL Существует множество разнообразных систем управления базами данных: Microsoft Access, Oracle, DB2, Microsoft SQL Server, Informix, mySQL, и это далеко не полный список. Итак, как же происходит обращение к базе данных из программы на языке C#? Как «объяснить» базе данных, что нам требуется? Самым простым решением является включение в код C# фрагмента на «языке базы данных», с помощью которого и осуществляется запрос к базе данных на получение нужных столбцов и строк. (По правде говоря, существует еще промежуточный уровень, называемый ADO.NET, но мы не станем сейчас заострять на нем внимание) Много лет назад специалисты по базам данных собрались и договорились об использовании «единого языка баз данных», который понимали бы большинство существующих баз данных. Этот язык называется SQL (от англ. Structured Query Language — язык структурированных запросов). Не следует путать язык SQL c системой управления базами данных SQL Server, разработанной корпорацией Microsoft. Язык SQL поддерживается СУБД от самых разных производителей. Прежде чем начинать разговор о работе с базами данных с использованием C#, давайте познакомимся с основами языка SQL. Ниже приведены примеры операторов на языке SQL и описывается результат их выполнения. Три основных команды SQL это: SELECT для просмотра некоторых данных, INSERT для добавления новых данных и UPDATE для изменения существующих данных. Приведем примеры каждой из них. Как правило, оператор SELECT записывается следующим образом: SELECT <имена извлекаемых столбцов> FROM <соответствующие таблицы базы данных> WHERE <условие, которое должно выполняться> SELECT * FROM PLANET Он возвращает все строки и все столбцы из базы данных PLANET (звездочка * означает выбор всех столбцов таблицы). PlanetName DistanceFromSun Inhabitants Mercury 57909 Mercurians Venus 108200 Venusians Earth 149600 Earthlings Mars 227940 Martians Jupiter 778400 Jupiterians Znock 7208100 Znockers Saturn 1423600 Saturnians Uranus 2867000 Uranians Neptune 4488400 Neptunians Pluto 5909600 Plutonians SELECT PlanetName, Inhabitants FROM PLANET Этот запрос возвращает столбцы «PlanetName» и «Inhabitants» со всеми строками из таблицы PLANET. PlanetName Inhabitants Mercury Mercurians Venus Venusians Earth Earthlings Mars Martians Jupiter Jupiterians Znock Znockers Saturn Saturnians Uranus Uranians Neptune Neptunians Pluto Plutonians SELECT PlanetName, Inhabitants FROM PLANET WHERE PlanetName='Venus' Такой запрос возвращает столбцы «PlanetName» и «Inhabitants» из таблицы PLANET. В результат его выполнения будут включены только те строки, которые содержат значение «Venus» в столбце PlanetName. PlanetName Inhabitants Venus Venusians SELECT PlanetName, Population FROM POPULATION WHERE Population<100000 Данный запрос возвращает строки столбцов PlanetName и Population из таблицы POPULATION, для которых значение столбца Population меньше 100000. PlanetName Population Mercury 40000 Venus 25 Neptune <NULL> Pluto <NULL> Mercury 35000 Venus 3 Saturn 75000 Neptune <NULL> Pluto <NULL> Как правило, оператор INSERT записывается следующим образом: INSERT INTO <таблица базы данных, к которой добавляются строки> (<столбцы, в которые будут добавляться значения>) INSERT INTO PLANET (PlanetName, DistanceFromSun, Inhabitants) VALUES ('Fluff', 23500000, 'Fluffies') Оператор INSERT добавляет в таблицу PLANET новую строку. Он не возвращает какого-либо результата в программу, но ради наглядности мы покажем, какой вид примет таблица после его выполнения. PLANET PlanetName DistanceFromSun Inhabitants Mercury 57909 Mercurians Venus 108200 Venusians Earth 149600 Earthlings Mars 227940 Martians Jupiter 778400 Jupiterians Znock 7208100 Znockers Saturn 1423600 Saturnians Uranus 2867000 Uranians Neptune 4488400 Neptunians Pluto 5909600 Plutonians Fluff 23500000 Fluffies Как правило, оператор UPDATE записывается следующим образом: UPDATE < таблица базы данных, в которую вносятся изменения> SET <столбцы, в которые необходимо внести изменения> = <новые значения> UPDATE PLANET SET PlanetName='Stuff', Inhabitants='Stuffies' WHERE PlanetName='Fluff' Оператор изменяет некоторые значения в строке, в которой столбец PlanetName имеет значение «Fluff» и тоже не возвращает какой-либо результат в программу. Далее показано, какой вид примет таблица после его выполнения. PLANET PlanetName DistanceFromSun Inhabitants Mercury 57909 Mercurians Venus 108200 Venusians Earth 149600 Earthlings Mars 227940 Martians Jupiter 778400 Jupiterians Znock 7208100 Znockers Saturn 1423600 Saturnians Uranus 2867000 Uranians Neptune 4488400 Neptunians Pluto 5909600 Plutonians Stuff 23500000 Stuffies Связи и объединение Если вы обратите внимание, то заметите, что между таблицами PLANET и POPULATION существует связь. В обеих таблицах есть столбец с именем «PlanetName». Мы говорим, что эти таблицы связаны по столбцу «PlanetName». Благодаря этой связи можно собрать всю информацию об определенной планете. Например, можно выбрать из обеих таблиц все строки, касающиеся Венеры ... PLANET PlanetName Venus POPULATION DistanceFromSun 108200 и объединить их в одну большую таблицу ... Inhabitants PlanetName Year Population Venusians Venus 2000 25 Venus 2001 3 SELECT * FROM PLANET INNER JOIN POPULATION ON PLANET.PlanetName=POPULATION.planetName WHERE PlanetName='Venus' PLANETS_AND_POPULATIO N PlanetName DistanceFromSun Inhabitants PlanetName Year Population Venus 108200 Venusians Venus 2000 25 Venus 108200 Venusians Venus 2001 3 Используемая СУБД Существуют причины, по которым в некоторых ситуациях для работы с определенной СУБД требуются особые программные решения. Так, в среде .NET существуют разные способы работы с данными. Например, если используется база данных Microsoft SQL Server, можно использовать специальные объекты, позволяющие значительно ускорить выполнение SQL-запросов. Однако для обращения к базе данных Microsoft Access эти объекты использовать нельзя. Поэтому код программ будет немного различаться в зависимости от того, используете ли вы SQL Server. Поэтому мы поступили следующим образом. Три примера программ, работающих с базами данных, в этом разделе (12a, 13a и 14a) предполагают использование SQL Server Express (или иной версии SQL Server). В противном случае вы можете найти на диске примеры соответствующих программ, рассчитанных на работу с Microsoft Access (12b, 13b и 14b). Для их запуска вообще не требуется установка какойлибо СУБД. Тем не менее, мы бы рекомендовали вам рано или поздно установить SQL Server Express, обеспечивающий более широкие возможности программирования. Кроме того, умение работать с SQL Server гораздо выше ценится в деловой сфере, и чем скорее вы освоите SQL Server, тем лучше. Загрузить его можно бесплатно по адресу: http://www.microsoft.com/express/ru/sql/download/default.aspx. Если у вас установлен и готов к работе Microsoft SQL Server Express, используйте примеры 12a, 13a и 14a. В противном случае, если у вас возникают проблемы с запуском этих программ, можно воспользоваться примерами 12b, 13b и 14b, аналогичными по сути, но не требующими установки СУБД. Обращение к базе данных из программы на языке C# В следующих образцах программ на C# будут применены классы SqlConnection и SqlCommand, обеспечивающие взаимодействие с базой данных SQL Server Express под названием «Planets». Эти классы предназначены специально для работы с базами данных Microsoft SQL Server любой версии. Далее мы будем работать с данными двумя различными способами: 1. С использованием класса SqlDataReader, который обеспечивает разнообразные возможности программного управления базой данных, позволяя построчно перебирать данные и выполнять различные действия над выбранными значениями. 2. С использованием классов SqlDataAdapter и DataSet (набор данных) для организации автоматического отображения строк данных с помощью таких элементов управления, как DataGrid. При таком подходе для извлечения информации из базы данных потребуется приличный объем кода, но ее вывод можно реализовать без особых усилий, поскольку такие «умные» элементы управления, как DataGridView легко привязываются к набору данных. Пример программы 12 Следующая программа подключается к базе данных SQL Server Express и посылает ей SQL-запрос. После получения результата запроса производится перебор строк и вывод каждого значения из столбца PlanetName в элемент управления Label с новой строки. Код программ ы 12a (для работы с SQL Express; для работы с Microsoft Access требуется версия 12b, имеющаяс я на диске) using System.Windows.Forms; using System.Data; using System.Data.SqlClient; Пространство имен для работы с базами данных SQL Server // class SimpleDataAccess : Form { public SimpleDataAccess() { // Указываем заголовок окна this.Text = "A simple databasing program"; // Указываем физический путь к базе данных PLANETS string dbLocation = System.IO.Path.GetFullPath("../../../database/SqlServer/planets.mdf "); // Добавляем метку и растягиваем ее на всю поверхность формы Label label1 = new Label(); label1.Dock = DockStyle.Fill; this.Controls.Add(label1); // Подключаемся к базе данных SQL Server SqlConnection connection1 = new SqlConnection ( @"data source=.\SQLEXPRESS;" + "User Instance=true;Integrated Security=SSPI;AttachDBFilename=" + @dbLocation ); connection1.Open(); // Формируем запрос к базе данных запрашиваем информацию о планетах string sql = "SELECT * FROM PLANET"; SqlCommand command1 = new SqlCommand(sql, connection1); SqlDataReader dataReader1 = command1.ExecuteReader(); // Организуем циклический перебор полученных записей и выводим название каждой планеты на метку while (dataReader1.Read()) { label1.Text = label1.Text + dataReader1["PlanetName"] + "\n"; } // Очистка dataReader1.Close(); connection1.Close(); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new SimpleDataAccess()); } } Пример программы 13 В следующей программе нужно вывести на экран несколько столбцов данных. Элемент управления Label слишком неудобен для этого, поэтому воспользуемся элементом DataGridView. Программа выполняет тот же запрос, что и в предыдущем примере, но помещает результат в объект DataSet, который подключается к элементу управления DataGridView и в результате тот автоматически отображает все данные. Подключение какого-либо скрытого источника данных к визуальному элементу управления называется привязкой данных. Код программ ы 13a (для работы с SQL Express; для работы с Microsoft Access требуется версия 13b, имеющаяс я на диске) using System.Windows.Forms; using System.Data; using System.Data.SqlClient; Пространство имен для работы с базами данных SQL Server // class DataInGrid : Form { public DataInGrid() { // Указываем заголовок окна this.Text = "One-Way Database Grid Binding"; // Указываем физический путь к базе данных PLANETS string dbLocation = System.IO.Path.GetFullPath("../../../database/SqlServer/planets.mdf "); // Добавляем элемент DataGridView на форму DataGridView DataGridView1 = new DataGridView(); DataGridView1.Width = this.Width; DataGridView1.Height = 250; DataGridView1.DataMember = "Table"; DataGridView1.Dock = DockStyle.Fill; this.Controls.Add(DataGridView1); // Подключаемся к базе данных SQL Server SqlConnection connection1 = new SqlConnection ( @"data source=.\SQLEXPRESS; + "User Instance=true;Integrated Security=SSPI;AttachDBFilename=" + @dbLocation ); connection1.Open(); // DataSet сохраняет данные в памяти с помощью таблиц данных DataTable DataSet dataSet1 = new DataSet(); // Объект DataAdapter является посредником при взаимодействии базы данных и объекта DataSet SqlDataAdapter sqlDataAdapter1 = new SqlDataAdapter(); // Указываем объекту DataAdapter, какие данные он должен получить и откуда sqlDataAdapter1.SelectCommand = new SqlCommand("SELECT * FROM PLANET", connection1); // Теперь заполняем находящийся в памяти объект DataSet данными sqlDataAdapter1.Fill(dataSet1); // Привязываем элемент DataGridView (визуальную таблицу) к хранящимся в памяти данным DataGridView1.DataSource = dataSet1; // Закрываем подключение к базе данных connection1.Close(); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new DataInGrid()); } } Пример программы 14 То, что мы научились отображать данные с помощью элемента управления DataGridView, уже хорошо, но вы, наверное, обратили внимание, что при изменении данных они не сохраняются в базе данных. Поэтому изменим подход и произведем «двухстороннюю привязку данных». Здесь мы пустимся на маленькую хитрость (это называется «повысить производительность своего труда») и не станем сами прописывать операторы UPDATE и INSERT. Пространство имен System.Data содержит «маленький, да удаленький» класс CommandBuilder, который умеет самостоятельно создавать команды SQL и автоматически их выполнять. Код программ ы 14a (для работы с SQL Express; для работы с Microsoft Access требуется версия 14b, имеющаяс я на диске) using System.Windows.Forms; using System.Data; using System.Data.SqlClient; Пространство имен для работы с базами данных SQL Server // class PlanetsForm : Form { // Объявляем объекты, к которым будем обращаться из разных методов DataGridView dg; SqlDataAdapter da; public PlanetsForm() // Метод-конструктор класса PlanetsForm { // Указываем заголовок окна this.Text = "Two-way Database Grid Binding"; // Указываем физический путь к базе данных PLANETS string dbLocation = System.IO.Path.GetFullPath("../../../database/SqlServer/planets.mdf "); // Подготавливаем подключение к базе данных SQL Server string connectionString = @"data source=.\SQLEXPRESS;" + "User Instance=true;Integrated Security=SSPI;AttachDBFilename=" + @dbLocation; /* Добавляем на форму кнопку "Save" (Сохранить) */ Button btnSave = new Button(); btnSave.Text = "Save"; btnSave.Click += new System.EventHandler(BtnSave_Click); btnSave.Dock = DockStyle.Top; this.Controls.Add(btnSave); /* Добавляем элемент DataGridView на форму */ dg = new DataGridView(); dg.Width = this.Width; dg.Height = 250; dg.Dock = DockStyle.Fill; this.Controls.Add(dg); /* Создаем экземпляры объектов для "умной" работы с данными и используем их вместе для привязки элемента DataGridView к требуемому источнику данных */ // Объект DataAdapter выступает в роли посредника при взаимодействии базы данных и хранящегося в памяти объекта DataTable da = new SqlDataAdapter("SELECT * FROM PLANET", connectionString); // Объект CommandBuilder автоматически выполняет команды UPDATE и INSERT SqlCommandBuilder cb = new SqlCommandBuilder(da); // Объект DataTable отслеживает и сохраняет в памяти изменения DataTable dt = new DataTable(); // Теперь заполняем объект DataTable данными da.Fill(dt); // Привязываем элемент управления DataGridView к объекту DataTable dg.DataSource = dt; } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run(new PlanetsForm()); } private void BtnSave_Click(object sender, System.EventArgs e) { // При нажатии кнопки "Save" в базе данных сохраняются любые изменения, // внесенные в источник данных, связанный с DataGridView, которым // в данном случае является объект DataTable da.Update((DataTable)dg.DataSource); MessageBox.Show ("Data has been saved", "For your information", MessageBoxButtons.OK); } } Поэкспериментируйте с изменением существующих и вводом новых значений. Нажмите кнопку «Save» и закройте форму. Перезапустив программу, можно убедиться, что измененные и добавленные значения были сохранены в базе данных. Что делать, если SQL Server Express не установлен? Если у вас не установлен Microsoft SQL Server и вы пользуетесь другой СУБД, вам потребуется внести в код небольшие поправки. Во-первых, необходимо изменить строку подключения с описанием расположения базы данных, ее типа и т.д. Строка подключения для базы данных SQL Server Express выглядит примерно так: string connectionString = "Integrated Security=SSPI;Persist Security Info=False; Initial Catalog=Northwind;Data Source=localhost"; или так, если подключение производится непосредственно к файлу базы данных (как в примерах из этой книги): string connectionString = @"data source=.\SQLEXPRESS;Integrated Security=SSPI; AttachDBFilename=c:\C#4#KIDS\examples\database\SqlServer\planets.mdf; User Instance=true”; (Ввиду нехватки места строка подключения разорвана здесь на несколько строк, но в действительности выражение, заключенное в кавычки, должно записываться в одну строку.) Строка подключения для базы данных Microsoft Access будет выглядеть примерно так: string connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source c:\C#4#KIDS\examples\database\Access\planets.mdb;"; (Как объяснялось ранее, выражение, стоящее в кавычках, должно записываться в одну строку.) Для других баз данных приняты свои особые форматы строки подключения. Примеры можно найти в справочной документации по Visual C# Express или в документации по используемой вами СУБД. Помимо изменения строки подключения потребуется заменить классы «SQL» на классы «OleDb». Для начала необходимо включить пространство имен System.Data.OleDb вместо пространства имен System.Data. SqlClient. Оно содержит классы, предназначенные для работы с разнообразными базами данных. using System.Data.OleDb; Затем произведите замену классов для работы с данными, как указано ниже: SQL Server General Databases SqlCommand OleDbCommand SqlCommandBuilder OleDbCommandBuilder SqlDataAdapter OleDbDataAdapter SqlConnection OleDbConnection Введение Классы пространства имен System.Xml позволяют работать с XML-данными разными способами. Наиболее часто выполняются следующие задачи: открытие XML-документа; чтение фрагмента XML-данных для извлечения некоторых значений; сохранение XML-файла на диск. Краткое введение в XML Язык XML получил в наши дни крайне широкое распространение, и, скорее всего, вы уже слышали о нем. XML представляет собой прекрасный пример языка, рассчитанного как на человеческое, так и на компьютерное восприятие. Как правило, данные, с которыми работают различные вычислительные системы, кажутся обычным людям китайской грамотой, но XML-документы записываются в виде обычного текста. Например, можно составить XML-документ для хранения на диске некоторых географических данных: <?xml version="1.0" encoding="utf-8" ?> South America Chile Каждый XML-документ начинается с такой строки, встречая которую программа будет знать, что имеет дело именно с XML Внешний блок Вложенный блок (с отступом), содержащий информацию о материке Южная Америка Блоки более глубокого уровня вложения, содержащие информацию о двух странах в Азии Argentina Asia Iraq India XML весьма похож на HTML, но в XML вы можете определять собственные теги. Давайте обсудим два термина, значение которых вы должны ясно представлять для дальнейшего изучения языка XML. Элементы XML-данные содержатся в элементах. У элемента есть имя и обычно имеется значение. В приведенном ниже примере элемент «Country» имеет значение «Argentina». elementname elementvalue <Country> Argentina </Country> Как можно заметить, элемент ограничивается открывающим и закрывающим тегами. Имя закрывающего тега должно совпадать с именем открывающего тега, но перед именем добавляется косая черта "/". Для записи элемента, не имеющего значения, допускается применять один тег, в котором после имени элемента ставится косая закрывающая черта. elementname <Country /> Атрибуты У элемента могут быть дополнительные свойства, которые необходимо описать. Например, нам может потребоваться указать столицу страны, для чего мы можем создать атрибут элемента с именем «capital». В приведенном ниже примере у элемента «Country» со значением «Argentina» атрибут «capital» имеет значение «Buenos Aires». elementname attributename <Country capital attributevalue = elementvalue "Buenos Aires" > Argentina </Country> Человеку очень просто читать подобные документы, а поскольку они имеют четкую структуру, можно предположить, что и компьютер можно легко этому научить. Например, составить такой набор инструкций: «Начать просмотр документа; при нахождении символа "<" — читать имя элемента. При нахождении символа ">" — читать значение элемента ... и так далее. Пример программы 15 Следующая программа считывает географические данные из XML-файла и отображает их на форме. В ней используются три класса из пространства имен System.Xml: XmlDocument (создает объект, загружающий XML-данные для последующей работы); XmlNodeList (запоминает список элементов, читаемых из файла); XmlNode (запоминает один XML-элемент). Программа позволяет пользователю вводить так называемое выражение xPath для указания того, какие именно элементы необходимо извлечь из XML-файла. Выражение xPath вида «//earth/continent/country» означает: «найти все элементы с именем "country", вложенные в элемент с именем "continent", вложенный в свою очередь в элемент с именем "earth"». Код программ ы 15 using using using using System; System.Windows.Forms; System.Drawing; System.Xml; // Пространство имен для работы XML-данными using System.Xml.XPath; // Пространство имен для работы с выражениями xPath class XmlRetriever : Form { ComboBox comboBox1; Button button1; ListBox listBox1; RichTextBox richTextBox1; XmlDocument xmlDoc; // Метод-конструктор нашего класса public XmlRetriever() { // Указываем высоту и заголовок окна this.Text = "XML Retrieval"; this.Size = new Size(400, 400); // Загружаем XML-файл с диска в находящийся в памяти объект XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load("../../data/earthData.xml"); // Готовим объект TextBox большого размера для вывода данных richTextBox1 = new RichTextBox(); richTextBox1.Dock = DockStyle.Top; richTextBox1.AcceptsTab = true; richTextBox1.Height = 180; richTextBox1.ReadOnly = true; richTextBox1.BackColor = Color.Silver; // Помещаем XML-данные в элемент TextBox и добавляем его на форму richTextBox1.Text = xmlDoc.OuterXml; this.Controls.Add(richTextBox1); // Добавляем ComboBox, позволяющий вводить // новое или выражение Xpath. Добавим несколько // выражений их написания. // (ComboBox сочетание элементов TextBox и ListBox) элемент управления выбирать существующее XPath в качестве примера представляет собой comboBox1 = new ComboBox(); comboBox1.Location = new Point(0, 200); comboBox1.Width = 300; comboBox1.Items.Add("//Earth/Continent"); comboBox1.Items.Add("//Earth/Continent/Country"); comboBox1.Items.Add("//Earth/Continent/Country[@capital='Nairobi']" ); comboBox1.SelectedIndex = 0; this.Controls.Add(comboBox1); // Добавляем кнопку, нажатие которой приводит к отображению соответствующих элементов button1 = new Button(); button1.Text = "Get data"; button1.Location = new Point(310, 200); button1.Click += new EventHandler(Button1_Click); this.Controls.Add(button1); // Добавляем элемент ListBox для отображения элементов listBox1 = new ListBox(); listBox1.Dock = DockStyle.Bottom; listBox1.Location = new Point(10, 10); this.Controls.Add(listBox1); } static void Main() { // Запускаем новый экземпляр приложения Windows Forms при помощи вышеописанного класса Application.Run( new XmlRetriever() ); } // Обработчик события, срабатывающий при нажатии кнопки void Button1_Click(object sender, EventArgs e) { XmlNodeList xmlNodes; XmlNode xmlElement; string elementValue; // Используем блок try-catch для обработки исключений, вызванных ошибками // в выражениях XPath, и предотвращения сбоев приложения try { // Выбираем из XML-документа элементы, соответствующие // выражению XPath, заданному в ComboBox xmlNodes = xmlDoc.SelectNodes(comboBox1.Text); listBox1.Items.Clear(); // Производим циклический перебор найденных элементов, // добавляя каждый элемент в ListBox for (int i = 0; i < xmlNodes.Count; i++) { xmlElement = xmlNodes[i]; if (xmlElement.HasChildNodes) { elementValue = xmlElement.FirstChild.Value.Trim(); listBox1.Items.Add(elementValue); } } } catch (XPathException ex) { // При обнаружении ошибки XPath, выводим сообщение и продолжаем работу программы MessageBox.Show ( "No matching data was found. Try another xPath expression.", "Error Encountered. " + ex.Message ); } } } Программа отображает исходный XML-файл в элементе управления RichTextBox. Далее под выражением xPath, вводимым или выбираемым пользователем, отображаются результаты выборки. После запуска программы поэкспериментируйте с вводом и выбором выражения xPath, после чего нажмите кнопку «Получить данные».