# 000151. Запуск другой формы из основной, ListBox, MenuStrip

Запуск другой формы из основной, ListBox, MenuStrip||wmysterio|wmysterio|<wmysterio@yandex.ru>|/||Всем осветительный привет! Мы продолжаем изучать формы Windows и сегодня мы будем учится запускать новую форму из окна основной, а также рассмотрим такие элементы, как ListBox и MenuStrip.\
\
Начнём с того, что будем создавать обычное меню, которое можно наблюдать в стандартных окнах. Для этого найдите компонент MenuStrip и переместите его в макет программы:\
[![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/s81769077.jpg)](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/81769077.png)\
По-умолчанию меню выводится сверху, как видно на рисунке выше. Это визуальный редактор меню. В поле "Вводить здесь" нам предлагают ввести имя ячейки каждого пункта нашей навигации. При нажатии на элемент в нас выскакивают дополнительные места для будущих команд:\
![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/74422300.png)\
Соответственно всё, что будет написано справа будет горизонтальной навигацией, а что внизу - подпункты выделенной категории. Для начала создадим 3 пункта меню: "**Файл**", "**Команды**" и "**Справка**". В меню файл создадим 2 пункта: "**Сохранить HTML**" и "**Выход**":\
![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/96864589.png)\
В меню "Команды" добавим только 1 пункт - "**История**", а в Справку закинем "**Об авторе**" и "**О программе**".\
\
Наше меню готово, осталось только написать логику этих пунктов. Но прежде всего предлагаю сместить объект кнопка, браузер и ТекстБокс на небольшое расстояние от панели меню. Если это сделать, то получится эффект, когда меню частично закрывает кнопку и поле ввода, что намекает на криворукость программиста ![smile](http://s49.ucoz.net/sm/15/smile.gif) Думаю при компиляции без изменений сразу будет видим этот эффект. Ваша задача - подкорректировать элементы макета под более удобный вид. \
\
В этом уроке мы рассмотрим выполнение 2-х функций - "Выход" и "История". Знаю, что выйти с программы можно и нажав красный крестик, но я предлагаю сделать это программно, вдруг кому-то пригодится ![smile](http://s49.ucoz.net/sm/15/smile.gif) Мы должны сделать обработчик события "Клик" по пункту меню. Два раза нажимаем ЛКМ по нужной ячейке меню, чтобы компилятор автоматически создал подписку и обработчик события:\
![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/53095560.png)\
и в обработчике пишем всего одну команду:<br>

Кодprivate void выходToolStripMenuItem\_Click( object sender, EventArgs e ) {\
&#x20;    Application.Exit();\
}С историей посещённый сайтов немного сложнее. Здесь есть куда разный вариантов. Я предлагаю немного усовершенствовать библиотеку, которую мы создали в предыдущем уроке.\
\
Заходим в проект BrowserLib и нажимаем комбинацию CTRL+SHIFT+A, и добавляем новую форму, назвав её, например, History:\
[![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/s75561017.jpg)](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/75561017.png)\
Теперь мы можем визуализировать нашу новую форму. Предлагаю эту форму сделать фиксированной, размером 400х350, режим авто-размера: **GrowAndShrink**. Добавим туда новый компонент - **ListBox** и разместим его на форме. Добавим ещё 2 кнопки: "удалить запись", "очистить". Если пользователь нажмёт 2 раза ЛКМ по элементу списка, то браузер перейдёт по сохранённой ссылке. Кроме этого создадим лейбл, который будет иметь имя "Нет". Этот компонент будет отображать дату записи, которую мы указали в момент добавления записи лога. В итоге мы должны получить такое окно:\
[![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/s29877193.jpg)](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/29877193.png)\
**ListBox** сам-по-себе не очень сложный в освоении, и очень поход на обычные списки, только в визуальном оформлении. Прежде всего нужно заполнить список информацией. По-задумке, в форму будет передан некий массив строк с которых будет формироваться список. Перейдём в режим редактирования(клавиша F7). В конструктор впишем аргумент - string\[] LINKS. Чтобы мы могли выводить дату, мы должны хранить её где-нить, по-этому предлагаю добавить **private** поле со списком:\
Кодprivate List\<string> DATE = new List\<string>();Так, как одна из функций данного окна является переход по ссылке, мы должны вернуть эту ссылку в виде строки. Пишем:\
Кодpublic string retLink = "";Дальше уже идёт процесс заполнения. После процедуры **InitializeComponent()** добавим следующий код:\
Кодpublic History( string\[] LINKS ) {\
&#x20;   InitializeComponent();\
\
&#x20;   if ( LINKS == null ) {\
&#x20;       button1.Enabled = false;\
&#x20;       button2.Enabled = false;\
&#x20;       return;\
&#x20;   }\
\
&#x20;   foreach ( string Temp in LINKS ) {\
&#x20;       string\[] s = Temp.Split( '|' );\
&#x20;       DATE.Add(s\[0]);\
&#x20;       listBox1.Items.Add(s\[1]);\
&#x20;   }\
}Рассмотрим первый блок. По логике, если нам передали пустой или несуществующий массив, то значит никаких ссылок в логе у нас нет. По этому все действия будут бесполезны, по этому зададим активность наших кнопок в режим "спячки" и сражу же выходим с конструктора с помощи **return**. Если массив всё-таки существует и имеет записи, то условие не выполняется и идёт второй блок. Мы в цикле обходим все строки в массиве. Каждую строчку мы разделяем сепаратором Split( '|' ). Суть этого метода в том, что он берёт каждую подстроку и записывает в массив, каждая строка является новым элементом. То есть если у нас есть строка "111|222|333|444|555", то на выходе мы получим массив с элементами:\
КодArrey\
\[0]111\
\[1]222\
\[2]333\
\[3]444\
\[4]555Символ '|' является сепаратором и в массив не входит. Так как наша строка имеет всего один символ сепаратора, то на выходе мы получим массив с 2-х элементов: даты и ссылки. Нулевой элемент мы запишем в список DATE, а первый в ListBox.\
\
Процесс заполнения окончен.Дальше уже идёт реализация. Создадим обработчик события для кнопки "Удалить запись". Прежде всего нам необходимо узнать текущий выбранный элемент:\
КодlistBox1.SelectedIndexА потом его удалить командой:\
КодlistBox1.Items.RemoveAt( INDEX );Так, как мы должны удалить и в списке дат элемент, то более оптимально будет вынести индекс в переменную, чтобы не делать лишних запросов:\
Кодint Index = listBox1.SelectedIndex;\
listBox1.Items.RemoveAt( Index );\
DATE.RemoveAt( Index );\
listBox1.SelectedIndex = 0;Однако есть исключение - нельзя отнимать несуществующие элементы, по этому мы должны поставить условия, что если к-во элементов списка больше нуля, то удалять, иначе отключим кнопки:\
Кодprivate void button2\_Click( object sender, EventArgs e ) {\
&#x20;   if ( listBox1.Items.Count > 0 ) {\
&#x20;       int Index = listBox1.SelectedIndex;\
&#x20;       listBox1.Items.RemoveAt( Index );\
&#x20;       DATE.RemoveAt(Index);\
&#x20;       if ( 1 > listBox1.Items.Count ) {\
&#x20;           button1.Enabled = false;\
&#x20;           button2.Enabled = false; \
&#x20;       }\
&#x20;   } else {\
&#x20;       button1.Enabled = false;\
&#x20;       button2.Enabled = false;           \
&#x20;   }\
}Процесс очистки всех элементов ещё проще. Можно конечно было бы делать циклы и тому подобное, но Майкрософт уже сделали это за нас, создав отличный метод очистки:\
Кодprivate void button1\_Click( object sender, EventArgs e ) {\
&#x20;   listBox1.Items.Clear();\
&#x20;   DATE.Clear();\
&#x20;   button1.Enabled = false;\
&#x20;   button2.Enabled = false; \
}Итак, мы научили список удалятся 2-мя способами: по одному и всё вместе. Он умеет получать данные из вне. Теперь разберёмся с датами. Здесь проще простого. Выбираем наш список в макете программы и ищем событие SelectedIndexChanged, жмём 2 раза на поле и получаем обработчик события:\
[![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/s10715290.jpg)](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/10715290.png)\
Этот блок рекомендую брать в блок try, так как индекс после удаления очищается.\
Кодprivate void listBox1\_SelectedIndexChanged( object sender, EventArgs e ) {\
&#x20;   try {\
&#x20;       label1.Text = DATE\[ listBox1.SelectedIndex ].ToString();\
&#x20;   } catch {\
&#x20;       label1.Text = "НЕТ";\
&#x20;   }\
}Итак, у нас теперь теперь осталась одна задача - переход на ссылке по двойному клику в списке. Здесь тоже нет ничего сложного - наша форма этого делать не будет, это осуществит dll-библиотека, а мы должны только передадим нужное значение. Опять переходим в события ЛистБокса и выбираем событие "**DoubleClick**". В обработчике пишем следующий текст:\
Кодprivate void listBox1\_DoubleClick( object sender, EventArgs e ) {\
&#x20;   retLink = listBox1.Items\[ listBox1.SelectedIndex ].ToString();\
&#x20;   Close();\
}Суть в том, что мы передаём через публичную переменную ссылку а затем просто закрываем форму, используя процедуру **Close()**.\
\
Казалось весь функционал формы уже сделан, но есть один интересный факт: удаляли мы только со списка, но не с файла. Нам нужно осуществить перезапись файла. Это делается не очень сложно. Для начала зайдём в события самой формы и выбираем "**FormClosing**". Это событие возникает перед самим закрытием формы, а не после, дающий возможности воспользоваться данными формы:\
Кодprivate void History\_FormClosing( object sender, FormClosingEventArgs e ) {\
&#x20;   string text = "";\
&#x20;   int j = DATE.Count;\
&#x20;   if ( j > 0 ) {\
&#x20;       for ( int i = 0; i < j; i++ ) {\
&#x20;           text += DATE\[ i ].ToString() + "|" + listBox1.Items\[ i ].ToString() + "\r\n";\
&#x20;       }\
&#x20;   }\
&#x20;   File.WriteAllText( "log.txt", text );\
}Если к-во элементов списка больше нуля, то идёт цикл, который прибавляет строку с нужным форматом к переменной **text**. Дальше идёт переход к записи файла. Если есть записи, то запишутся все. Если нет, то файл будет только пустым.\
\
На этом мы завершаем проектировать форму и переходим к dll-ке. Добавим новый статический метод:\
Кодpublic static string DysplayHistory() {\
&#x20;       // Здесь будет код\
}Чтобы открыть новую форму, нам понадобится подключить библиотеку:\
Кодusing System.Windows.Forms;Дальше нужно объявить переменную - нашу форму. Делается это так:\
КодНАЗВАНИЕ\_ФОРМЫ переменная = new НАЗВАНИЕ\_ФОРМЫ(атрибуты);Так как форму я завал **History**, то конструктор будет следующим:\
КодHistory frm = new History( GetMessages() );В качестве параметра я передаю массив строк, который получаю ранее записанным методом **GetMessages**. Дальше нам нужно вызвать форму на экран. Есть 2 способа:\
1\) Вызов модального окна - форма будет поверх всех окон, а остальные будут недоступны(напоминает MessageBox).\
2\) Обычный вызов окна - будет создано окно, но мы может осуществлять и другие действия в проекте, вне создаваемой формы.\
Я предпочитаю модальный способ:\
Кодfrm.ShowDialog();Для обычного:\
Кодfrm.Show();Итак, окно открылось, пользователь сделал какие-то действия и закрыл его. Наша процедура возвращает строку, но какую? Так как в окне истории мы предполагали, что пользователь может нажать дабл-клик по списку, то будет переход на тот сайт. Так вот эту стоку метод будет возвращать. Весь метод будет выглядеть так:\
Кодpublic static string DysplayHistory() {\
&#x20;   History frm = new History( GetMessages() );\
&#x20;   frm.ShowDialog();\
&#x20;   return frm.retLink;\
}Мы крутые! Форма и библиотека полностью функциональна! Теперь нам остался последний штрих! вызвать эту процедуру с главного окна программы. Для этого дважды кликнем на шаблон ячейки меню "История" и добавим следующий код:\
Кодprivate void историяToolStripMenuItem\_Click( object sender, EventArgs e ) {\
&#x20;   string a = BrowserLog.DysplayHistory();\
}Делаем тест:\
[![](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/s42902911.jpg)](https://github.com/wmysterio/scm-scripting-lessons/blob/main/_pu/2/42902911.png)\
Наша задача выполнена. наш браузер научился хранить историю! :)\
\
Теперь сделаем одну махинацию, чтобы легче и экономнее было использовать наш браузер, а именно создать процедуру, которая будет принимать 1 строковый параметр а сама выполнять переход в браузер:\
Кодpublic void FollowLink( string URL ) {\
&#x20;   try {\
&#x20;       webBrowser1.Url = new Uri( URL );\
&#x20;       BrowserLog.AddMessage( URL );\
&#x20;       label1.Text = "";\
&#x20;   } catch {\
&#x20;       label1.Text = "Неверное имя ссылки!";\
&#x20;   }       \
}И использовать эту функцию в обработчике кнопки:\
Кодprivate void button1\_Click( object sender, EventArgs e ) {\
&#x20;    FollowLink( textBox1.Text );              \
}Теперь мы можем воспользоваться этой процедурой и в обработчике кнопки меню "**История**":\
Кодprivate void историяToolStripMenuItem\_Click( object sender, EventArgs e ) {\
&#x20;    string a = BrowserLog.DysplayHistory();\
&#x20;    if ( a.Length > 0 ) {\
&#x20;         FollowLink( a );\
&#x20;    }\
}Таким образом мы осуществили такое действие: пользователь дважды нажал на ссылку в списке и осуществился переход на эту ссылку.\
\
Ну и чтобы Вас окончательно утомить, давайте осуществим просьбу одного пользователя - чтобы префикс "http\://" автоматически приклеивался, а то постоянно вводить адрес как-то непривычно и не функционально. Реализовать такое просто.Достаточно написать условие:\
Кодif ( str.ToLower().IndexOf( "http\://" ) == -1 ) {\
&#x20;    str = "http\://" + str;\
}а метод FollowLink будет иметь вид:\
Кодpublic void FollowLink( string URL ) {\
&#x20;    try {\
&#x20;         if ( URL.ToLower().IndexOf( "http\://" ) == -1 ) {\
&#x20;              URL = "http\://" + URL;\
&#x20;         }\
&#x20;         webBrowser1.Url = new Uri( URL );\
&#x20;         BrowserLog.AddMessage( URL );\
&#x20;         label1.Text = "";\
&#x20;    } catch {\
&#x20;         label1.Text = "Неверное имя ссылки!";\
&#x20;    }       \
}Суть условия проста. Ищем в строке подстроку "http\://". Если такая не нашлась(результат вернул -1), то просто дописываем эту приставку к строке. Чтобы поиск был корректным, нужно перевести строку в нижний регистр, чем мы воспользовались: ToLower().\
\
Итак, из этого урока вы извлекли:\
\- Вызов формы из другой формы;\
\- Применили основные команды ListBox и также List;\
\- Научились передавать данные между формой и библиотекой(этот вариант катит и при обмене с другими формами, классами и.т.п.);\
\- Узнали некоторые методы работы со строками Split, ToLower, IndexOf;\
\- Узнали о понятии меню и как писать команды при нажатия кнопки;\
\
На этом, окончательно, урок окончен. Спасибо за внимание ;)\
|3469|1|0|81769077`png`581`160`400`110\|74422300`png`235`96|96864589`png`180`93\|53095560`png`320`168|75561017`png`587`189`400`128\|29877193`png`416`367`400`352|10715290`png`561`356`400`253\|42902911`png`600`450`400`300||zapusk\_drugoj\_formy\_iz\_osnovnoj\_listbox\_menustrip|1399455340


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lessons.sannybuilder.com/00300/00100/000151.-zapusk-drugoi-formy-iz-osnovnoi-listbox-menustrip.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
