> For the complete documentation index, see [llms.txt](https://lessons.sannybuilder.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lessons.sannybuilder.com/00300/00100/000151.-zapusk-drugoi-formy-iz-osnovnoi-listbox-menustrip.md).

# 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
