WinForms F# - интерактивное приложение

Windows Forms приложение на языке F#

Приложение Windows Forms на языке программирования F#

Приложение Windows Forms создано на основе консольной F# программы. Код приложения разделён на несколько логических модулей. Модули инициализации элементов управления и формы сгруппированы в отдельной папке. Программный код интерактивности приложения также вынесен в отдельный модуль. Такое построение напоминает разделённый на частичные классы приложение языка C#. В приложение добавлен простейший код F# для демонстрации интерактивности приложения.

На основе предлагаемого приложения можно заниматься изучением языка программирования F#. В оконном приложении гораздо нагляднее смотрятся результаты функций и выражений. Кроме того, на основе данного исходника, можно создавать вполне работоспособные приложения на языке F#.

Перестройка консольного приложения

Изменить файл проекта в Visual Studio

Прежде всего в интегрированной среде программирования MS Visual Studio 2019 создаётся консольное приложение для языка F#. На данный момент для приложения выбрана максимальная целевая платформа .NET5.

Для поддержки функционирования каркаса Windows Forms в файле проекта консольного приложения необходимо сделать соответствующие изменения. В 16 версии Visual Studio нет возможностей визуального добавления поддержки платформы Windows Forms для проектов языка F# (появилась в 17 версии студии), поэтому изменим файл проекта вручную, в встроенном текстовом редакторе. В окне обозревателя решений щелкаем правой кнопкой мыши над именем проекта и в появившемся диалоговом окне вызываем текстовый редактор для файла проекта.

В окне текстового редактора отобразится следующая XML-разметка:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <WarnOn>3390;$(WarnOn)</WarnOn>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

</Project>
Код, заключенный в теге элемента PropertyGroup следует заменить как показано ниже в листинге изменённой XML-разметки файла проекта:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

</Project>

После перезагрузки проекта, в обозревателе решений, в зависимостях проекта, можно увидеть добавленную платформу Microsoft.Windows.Desktop.App.WindowsForms для поддержки оконных элементов.

Обозреватель решений Visual Studio

Структура кода приложения

Структура кода приложения F# Windows Forms

Структура предлагаемого приложения 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)

Исходник Windows Forms F#

Исходник написан в интегрированной среде программирования MS Visual Studio 2019, тестировался в Visual Studio 2022 Preview 4.

Скачать исходник