Приложение Windows Forms создано на основе консольной F# программы. Код приложения разделён на несколько логических модулей. Модули инициализации элементов управления и формы сгруппированы в отдельной папке. Программный код интерактивности приложения также вынесен в отдельный модуль. Такое построение напоминает разделённый на частичные классы приложение языка C#. В приложение добавлен простейший код F# для демонстрации интерактивности приложения.
На основе предлагаемого приложения можно заниматься изучением языка программирования F#. В оконном приложении гораздо нагляднее смотрятся результаты функций и выражений. Кроме того, на основе данного исходника, можно создавать вполне работоспособные приложения на языке F#.
Прежде всего в интегрированной среде программирования MS Visual Studio 2019 создаётся консольное приложение для языка F#. На данный момент для приложения выбрана максимальная целевая платформа .NET5.
Для поддержки функционирования каркаса Windows Forms в файле проекта консольного приложения необходимо сделать соответствующие изменения. В 16 версии Visual Studio нет возможностей визуального добавления поддержки платформы Windows Forms для проектов языка F# (появилась в 17 версии студии), поэтому изменим файл проекта вручную, в встроенном текстовом редакторе. В окне обозревателя решений щелкаем правой кнопкой мыши над именем проекта и в появившемся диалоговом окне вызываем текстовый редактор для файла проекта.
В окне текстового редактора отобразится следующая XML-разметка:
После перезагрузки проекта, в обозревателе решений, в зависимостях проекта, можно увидеть добавленную платформу Microsoft.Windows.Desktop.App.WindowsForms для поддержки оконных элементов.
Структура предлагаемого приложения Windows Forms на языке F# состоит из четырех файлов-модулей. Три файла - InitControls.fs, FormModule.fs, CodeFormModule.fs - формируют код формы и находятся в одном пространстве имён. Один - Program.fs - это общий файл запуска приложения. Файлы - InitControls.fs, FormModule.fs - в которых происходит инициализация формы и элементов управления сгруппированы в папку.
Файл формы CodeFormModule.fs, предназначенный для рабочего кодирования, вынесен за пределы папки формы. Такое построение структуры модулей позволило отделить код инициализации формы и визуальных элементов управления от программного кода бизнес-логики приложения.
Модуль в контексте языка программирования F# - это основной способ группирования логически связанного программного кода. Модули могут представлять из себя отдельные файлы или же в одном файле можно объявить несколько модулей. Описываемая структура приложения создавалась именно с целью распределить логически родственные коды F# по отдельным модулям приложения.
InitControls.fs - файл-модуль F# объявления и инициализации элементов управления перед добавлением в главную форму приложения. Модуль содержит несколько функций, составные выражения которых возвращают полностью подготовленные элементы для размещения на форме.
Для примера взяты табличный контейнер TableLayoutPanel и четыре кнопки Button, динамически размещаемые в ячейках табличного элемента управления.
Листинг кода модуля InitControls.fs:
namespace MyWindowsForm
open System.Windows.Forms
open System.Drawing
module InitControls =
// --- Объявление и инициализация свойств элементов управления ---
// Виды кнопок.
let standardButton = Size(100, 70)
let plumpButton = Size(200,200)
let tableLayoutPanel =
let tlp = new TableLayoutPanel()
tlp.CellBorderStyle <- TableLayoutPanelCellBorderStyle.Single
tlp.ColumnCount <- 2
tlp.RowCount <- 2
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)) |> ignore
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)) |> ignore
tlp.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)) |> ignore
tlp.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)) |> ignore
tlp.Dock <- DockStyle.Fill
tlp.Name <- "tlp"
tlp
let button1 =
let btn = new Button()
btn.Name <- "button1"
btn.Size <- standardButton
btn.TabIndex <- 0
btn.Text <- "button1"
btn.Anchor <- AnchorStyles.None
btn.BackColor <- Color.BlueViolet
btn
let button2 =
let btn = new Button()
btn.Name <- "button2"
btn.Size <- standardButton
btn.TabIndex <- 1
btn.Text <- "button2"
btn.Anchor <- AnchorStyles.None
btn.BackColor <- Color.Chartreuse
btn
let button3 =
let btn = new Button()
btn.Name <- "button3"
btn.Size <- standardButton
btn.TabIndex <- 2
btn.Text <- "button3"
btn.Anchor <- AnchorStyles.None
btn.BackColor <- Color.CadetBlue
btn
let button4 =
let btn = new Button()
btn.Name <- "button4"
btn.Size <- standardButton
btn.TabIndex <- 3
btn.Text <- "button4"
btn.Anchor <- AnchorStyles.None
btn.BackColor <- Color.BurlyWood
btn
Модуль FormModule.fs предназначен для объявления собственного класса MyForm формы приложения. В этом же модуле создаётся объект MyForm и к нему добавляются элементы управления предварительно инициированные в модуле InitControls.fs.
Для формирования внешнего вида приложения элементы добавляются к форме внутри поля между спаренными методами SuspendLayout() - ResumeLayout(). Данные методы исключают повторы перерасчета макета формы при добавлении каждого элемента и запускают формирование макета только после вызова метода ResumeLayout(). Конечно, если форма состоит из нескольких элементов, спаренные методы заметно не увеличивают производительность. Но с учётом возможного роста создаваемого проекта приложения, такое построение кода создания формы всё же желательно использовать, тем более что накладные расходы при вызовах SuspendLayout() - ResumeLayout() не существенны.
Программный код модуля формы FormModule.fs:
namespace MyWindowsForm
open System.Windows.Forms
open System.Drawing
open InitControls
module FormModule =
// --- Модуль создания и инициализации формы ---
type MyForm() =
inherit Form()
do
base.Text <- "Windows Form F#"
base.StartPosition <- FormStartPosition.CenterScreen
base.Size <- new Size(800, 600)
base.TopMost <- true
// Создаем собственную форму.
let myForm = new MyForm()
// Приостановка работы макетов.
// Приостановка актуальна при большом количестве визуальных элементов.
// Уменьшает время визуализации формы.
tableLayoutPanel.SuspendLayout()
myForm.SuspendLayout()
// Кнопки добавляются в контейнер TableLayoutPanel.
tableLayoutPanel.Controls.Add(button1, 0, 0)
tableLayoutPanel.Controls.Add(button2, 1, 0)
tableLayoutPanel.Controls.Add(button3, 1, 1)
tableLayoutPanel.Controls.Add(button4, 0, 1)
tableLayoutPanel.Controls.Add(button4, 0, 1)
// Код для исследования работы тандема методов
// SuspendLayout() - ResumeLayout()
// Если раскомментировать данный код и
// закомментировать вызовы SuspendLayout(), ResumeLayout(),
// можно заметить задержку показа формы.
// Если только раскомментировать этот код
// визуализация формы происходит значительно быстрее.
(*
for i = 0 to 2000 do
let btn = new Button()
btn.Name <- "button" + (i+10).ToString()
btn.Size <- new Size(220, 120)
btn.Text <- "button4"
btn.Anchor <- AnchorStyles.None
btn.Dock <- DockStyle.None
tableLayoutPanel.Controls.Add(btn, 0, 1)
*)
// Добавляем таблицу на форму.
myForm.Controls.Add(tableLayoutPanel)
// Возобновляем работу макетов.
tableLayoutPanel.ResumeLayout(false);
myForm.ResumeLayout(false);
Модуль F# CodeFormModule.fs предназначен для рабочего кода приложения, это модуль взаимодействия элементов управления и бизнес-логики приложения.
Здесь находятся методы обработки событий компонентов формы, здесь же осуществляется изменение свойств элементов и формы для обеспечения интерактивности программы.
namespace MyWindowsForm
open System
open System.Windows.Forms
open InitControls
open FormModule
open System.Drawing
// --- Модуль рабочего кода формы ---
module Code =
let myCodeForm = myForm
myCodeForm.Text <- "Выполнение модуля CodeFormModule"
button1.Text <- "Первая кнопка"
button2.Text <- "Вторая кнопка"
button3.Text <- "Третья кнопка"
button4.Text <- "Четвертая кнопка"
// Определение вида кнопки.
let isStandart (btn: Button) : bool = btn.Size = standardButton
// Обработчик события нажатия для всех кнопок
let buttons_Click (sender:System.Object) (e:System.EventArgs) : unit =
let button: Button = sender :?> Button
if button = button1 then Console.Beep(300, 300)
if button = button2 then Console.Beep(500, 300)
if button = button3 then Console.Beep(1000, 300)
if button = button4 then Console.Beep(3000, 300)
// Если кнопка стандартная, то делаем её пухлой,
// и наоборот.
if isStandart button then
button.Size <- plumpButton
else
button.Size <- standardButton
// Подключение обработчиков событий
button1.Click.AddHandler( new System.EventHandler(buttons_Click) )
button2.Click.AddHandler( new System.EventHandler(buttons_Click) )
button3.Click.AddHandler( new System.EventHandler(buttons_Click) )
button4.Click.AddHandler( new System.EventHandler(buttons_Click) )
В модуле F# Program.fs находится код настроек и запуска приложения.
open System.Windows.Forms
open MyWindowsForm
// Стили элементов управления для поддержки тем операционной системы.
Application.EnableVisualStyles()
// Режим отображения текста в окнах приложения.
// Улучшает качество отображения строк.
// Повышает графическую производительность приложения.
Application.SetCompatibleTextRenderingDefault(false)
// Запускает стандартный цикл обработки сообщений приложения
// в текущем потоке и делает указанную форму видимой.
Application.Run(Code.myCodeForm)