Showing posts with label F#. Show all posts
Showing posts with label F#. Show all posts

Friday, February 7, 2014

Взаимодействие F# с Windows Forms и WPF

Здравствуйте, уважаемые читатели моего блога. Сегодня мы поговорим о взаимодействии F#-приложения с Windows Forms и WPF. В последнее время я активно занимаюсь изучением F# и предлагаю вам окунуться вместе со мной в возможности этого языка. Как говорится, меньше слов – больше дела. Для начала создадим проект с названием TestFSharp для примера. Как это сделать, можно увидеть на рисунке ниже.
Затем нужно удалить точку входа в нашу программу, которая по умолчанию будет у Вас создана.
[<EntryPoint>]
let main argv =
    printfn "%A" argv
    0 // return an integer exit code
Эта точка входа обозначает, что Ваше приложение будет запущено на Console Application. Нам же нужно будет запустить наше приложение как приложение Windows Application. Для этого переходим в окно настроек нашего проекта и изменяем запускаемый проект с на Console Application на Windows Application, как показано на рисунке ниже.
А сейчас немножко "поколдуем" над нашим кодом. Для того, чтобы наши контролы заработали, нужно добавить ссылки на System.Windows.Forms и
System.Drawing.
После того, как мы всё сделали, можно спокойно перейти к реализации. В первую очередь необходимо указать пространства имен, которые мы будем использовать. Для этого добавим в наш проект таки строки:
open System
open System.Windows.Forms
open System.Drawing
Мы говорим нашей исполняющей среде, что будем использовать контролы и классы с данного пространства имен. Давайте для примера создадим простенькую форму, в которую добавим кнопочку, при нажатии на которую выведем сообщение через MessageBox.Show(). Полный исходный код приведен ниже.
open System
open System.Windows.Forms
open System.Drawing

let form = new Form(Text = "F# Program")
let button = new Button(Text = "Button Click")
button.Location <- new Point( 100, 100)
form.Controls.Add(button)

button.Click.Add(fun evArgs -> MessageBox.Show("Click me") |> ignore)

Application.Run(form)
С помощью строк
let form = new Form(Text = "F# Program")
let button = new Button(Text = "Button Click")
создаем форму, затем создаем кнопку и добавляем ее на форму. Затем чтобы наш контрол не выводился с верхнего левого края, зададим ему отступ справа и слева на 100 пикселей.
button.Location <- new Point( 100, 100)
Это делается с помощью оператора <-. Этот оператор позволяет изменять только свойства, помеченные как mutable. По умолчанию свойства в F# помечены как неизменяемые, поэтому помните, когда будете писать код, что для свойств нужно указывать явно, что они являются изменяемыми. Строка
form.Controls.Add(button)
добавляет созданную кнопку на форму.
button.Click.Add(fun evArgs -> MessageBox.Show("Click me") |> ignore)
Код, приведенный выше, создает обработчик события нажатия на кнопку. Этот обработчик создан с помощью лямбда-выражения
fun evArgs -> MessageBox.Show("Click me")
Поскольку нам не важен результат, который возвращает метод MessageBox.Show, мы указываем игнорировать данный результат с помощью строки
|> ignore
После нажатия на клавишу F5 видим результат, подобный рисунку ниже.

F# и WPF
Приведем еще один пример использования F# совместно с WPF. Поскольку пример с созданием контролов мы уже видели в предыдущем примере, рассмотрим вариант с использованием XAML-файла. Вы не сможете создать XAML-файл в самой среде Visual Studio 2012, поскольку эти типы файлов недоступны для проекта на языке F#.
Нам необходимо предпринять маленькую хитрость и создать XAML-окно в отдельном редакторе, например, в той же студии или в Expression Blend, а затем добавить его в наш проект. Признаюсь, что для реального проекта это не очень удобно. Но у нас тестовый пример, и сведения о том, как запустить WPF-приложение с F#, думаю, будут интересными. Для работы с нашим WPF-приложением необходимо добавить следующие библиотеки в проект:

WindowsBase.dll
PresentationCore.dll
PresentationFramework.dll
System.Xaml.dll
Пример показан на рисунке ниже.
Простой пример без использования XAML-файла можно написать так:
open System
open System.Windows


let win = new Window(Title = "Hello WPF")
win.Show() |> ignore

#if COMPILED
[<STAThread()>]
do
    let app =  new Application()
    app.Run() |> ignore
#endif
Но похожий пример мы уже создавали, когда написали Windows Forms-приложение в начале статьи. Поэтому пойдем более сложным путем. Выберем созданный уже заранее приготовленный шаблон и добавим его в студию, нажав на проекте правой клавишей мыши и выбрав пункт меню Add->Existing Item…
Выбираем All Files (*.*) и добавляем наше окно, которое хотим отобразить на экране.
Вот какой вид имеет наше окно:
<Window x:Class="WpfApplicationTestFSharpProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel Orientation="Vertical">
            <TextBox x:Name="FSharpBook" Text="Test"></TextBox>
            <Button x:Name="PressButton" Content="Click Me"></Button>
        </StackPanel>
    </Grid>
</Window>
Нам необходимо для прикрепленного окна MainWindow.xaml установить в окне Properties свойство Build Action в Resources. После того, как мы проделали необходимую работу, посмотрим, как будет выглядеть наше решение.
open System
open System.Windows
open System.Windows.Controls

[<STAThread>]
[<EntryPoint>]
let main(_) =
  let window = Application.LoadComponent(new Uri("MainWindow.xaml", UriKind.Relative)) :?> Window
  let button = window.FindName("PressButton") :?> Button
  let textBox = window.FindName("FSharpBook") :?> TextBox
  textBox.Text <- "Hello F#"
  button.Click.Add(fun _ ->
    textBox.Text <- textBox.Text.ToUpper())

  window.Show() |> ignore

  let application = new Application()
  application.Run()
Для нашей программы укажем точку входа и то, что наша программа должна запускаться в основном потоке. Это делается с помощью строк
[<STAThread>]
[<EntryPoint>]
let main(_) =
Строка _ как параметр функции main указывает на то, что нам не важно, какие параметры будут переданы в функцию, так как функция принимает параметры командной строки, мы можем указать args как аргумент функции. Следующим шагом мы загружаем наше окно с помощью функции
let window = Application.LoadComponent(new Uri("MainWindow.xaml", UriKind.Relative))
и приводим результат, который возвращает нашу функцию к типу Window (Примечание: функция Application.LoadComponent возвращает Object). Операция приведения в F# очень простая и легко запоминается:

:?>

Следующим этапом находим наши контролы: TextBox и Button по имени.
let button = window.FindName("PressButton") :?> Button
let textBox = window.FindName("FSharpBook") :?> TextBox
Для примера изменим текст, который был в TextBox, и напишем новый текст "Hello F#". Как это сделать, посмотрите ниже:
textBox.Text <- "Hello F#"
Для кнопки мы создадим обработчик события с помощью лямбда-выражения по такому же принципу, как делали для примера Windows Forms. Только для данного  примера мы в обработчике изменим текст в TextBox, который нашли выше, на текст верхнего регистра.
button.Click.Add(fun _ ->
textBox.Text <- textBox.Text.ToUpper())
Осталось каких-то "два штриха". Нужно показать наше созданное окно и запустить основное приложение с помощью метода application.Run()

Итоги
В данной статье мы рассмотрели возможности взаимодействия F#-проекта с Windows Forms и WPF-контролами. Надеюсь, Вам понравился экскурс в мир F#. Жду ваши комментарии и с удовольствием на них отвечу.   

Monday, February 3, 2014

Введение в F#

Совсем недавно мне довелось познакомиться с таким уникальным языком разработки, как F#. Его гибкость и возможности поразили меня. Признаюсь, знакомство с F# началось у меня около года назад, но тогда этот язык просто не "зацепил" меня. Наконец я попробовал снова сосредоточить на нем внимание, изменив при этом свой подход к изучению. Основная проблема, которая иногда у меня проявляется при знакомстве с новым языком, кроется в попытке проецировать возможности известного языка разработки на изучаемый. Следует рассматривать язык как уникальную единицу, призванную решать свои задачи, и если пробовать нагружать на него специфику известного языка, станут заметны несоответствия и может сложиться мнение, что изучаемый язык "недоразвит". Проблема кроется зачастую не в языке, а в нас: мы зачастую пытаемся проецировать то, что есть в нашей памяти, на все новое, пытаясь найти аналогии, и это касается не только разработки. Такое поведение можно наблюдать, например, при изучении иностранных языков. 
Отойдем от рассуждений и попробуем беспристрастно взглянуть на этот язык. Мне понравилась книга "Chad Fowler, The Passionate Programmer", советы из которой стараюсь применять каждый день, чтобы повышать свой уровень. Один из таких советов звучит: "Чтобы понять, действительно ли Вы знаете что-либо, попробуйте обучить этому кого-то еще". Нет лучшего способа закрепить свои знания, чем заставить себя ясно сформулировать их кому-то еще. Обычное формулирование чего-либо является популярным средством для "очищения" разума. Общение с куклами или другими неодушевленными предметами как способ решения проблем является довольно известным элементом программистского фольклора (источник). Поэтому давайте попробуем учиться вместе. Надеюсь, что данный язык Вам понравится своими возможностями и мои старания не прошли даром. Для начала создадим новое приложение, как показано на рисунке ниже.
После этого увидим код: 
[<EntryPoint>]
let main argv =
    printfn "%A" argv
    0 // return an integer exit code
Это пример обычного консольного приложения, в котором [<EntryPoint>] – это точка входа в программу.
let main argv =
Указываем, что функция main является точкой в программу, argv – это аргументы этой функции. Как можно увидеть в Intellicence, функция возвращает int.
Функция printfn аналогична функциям из языка C и Python, только в C# она называется иначе. Например, ту же логику, какую делает эта функция, можно проделать на C# функцией string.Format или для консольного приложения передать необходимые параметры в функцию Console.WriteLine.
Попробуем написать простенькую функцию, которая будет суммировать два числа, и одну функцию, которая будет воспроизводить число в квадрате.
open System

let square x = x * x

let add x y =
    x + y

[<EntryPoint>]
let main argv =
    let z = 5
    printfn "Test square function %i" (square 5)
    printfn "An int: %i" z
    printf "Test add function %i" (add 5 7)
    Console.ReadLine() |> ignore
    0 // return an integer exit code
Для паузы в программе я использовал функцию Console.ReadLine(), но поскольку мне не важен результат, который возвращает данная функция, я указал для данной функции игнорировать результат возвращаемого значения.
Console.ReadLine() |> ignore
Открываем пространство имен System для F# с помощью кода
open System
Теперь поближе остановимся на самих функциях
let square x = x * x
Функция square принимает на вход один параметр. Посмотрите на рисунок ниже, чтобы понять, как работают функции в F#.
Соответственно, функция add принимает на вход два аргумента – x, y – и возвращает сумму этих аргументов. Если мы переносим результат выполнения функции на новую строку, то у нас должен быть с левой стороны как минимум один пробел, так как F# не будет понимать, что Вы создали функцию.
Рассмотрим, как нам можно создать кастомный тип данных.
type Person = { Name: string; Age: int}

let mass = [|
                {Name = "Test"; Age = 32};
                {Name = "Test2"; Age = 25}
                |]
Выше мы создали тип данных Person, который имеет два члена Name и Age. А ниже – двумерный массив нашего типа Person. К сожалению, типы данных в C# являются неизменяемыми, поэтому мы не можем написать код на подобии такого для изменения некоего значения данного поля.
let person = {Name = "Test"; Age = 32}
person.Name <- "1234567890"
Но мы легко можем создать новый тип Person, в котором обновить нужное значение.
let person = {Name = "Test"; Age = 32}
let partDeux = { person with Name = "Expert F# 2.0" }
Давайте попрактикуемся в создании функций в отдельных модулях. Для этого выберем в Visual Studio создание нового файла, как показано на рисунке ниже.
Затем перенесем наши функции в этот файл.
module Calculation
    let square x = x * x

    let add x y =
        x + y
После этого нужно немного модифицировать код, чтобы можно использовать эти функции.
open System

[<EntryPoint>]
let main argv =
    let z = 5
    printfn "Test square function %i" (Calculation.square 5)
    printfn "An int: %i" z
    printf "Test add function %i" (Calculation.add 5 7)

  
    Console.ReadLine() |> ignore
    0 // return an integer exit code
Но как Вы увидите, Intellicence будет подчеркивать данный код как ошибку. Все дело в том, что порядок следования файлов в языке F# имеет значение. Нам достаточно выделить добавленный нами файл и нажать Move Up, как показано на рисунке ниже.
Вам необходимо переместить добавленный файл Calculation.fs, чтобы он оказался перед файлом Program.fs. После запуска мы можем запустить приложение и увидеть, что оно работает.
Что же делать, если нам нужно несколько модулей, как разделить их, ведь в одном файле они работать не будут. Для этого в F# существует разделение по пространствам имен namespace. Добавим еще один модуль в файл Calculation.fs и назовем его Multiply. Затем те два модуля, которые у нас получились, добавим в пространство имен CalculationOperation.Code. Пример приведен ниже.
namespace CalculationOperation.Code
    module Calculation =
        let square x = x * x

        let add x y =
            x + y
    module Multiply =
        let div x y = x / y

Теперь посмотрим, как использовать то, что мы написали. J
open System
open CalculationOperation.Code

[<EntryPoint>]
let main argv =
    let z = 5
    printfn "Test square function %i" (Calculation.square 5)
    printfn "An int: %i" z
    printf "Test add function %i" (Calculation.add 5 7)
    printf "Test div function %i" (Multiply.div 4 2)
  
    Console.ReadLine() |> ignore
    0 // return an integer exit code

Итоги
В приведенной статье мы рассмотрели простенькие примеры использования возможностей языка F#. Надеюсь, что примеры были несложные и это не отпугнет Вас от дальнейшего изучения данного языка. В следующей статье попробуем создать на F# приложение Windows Forms и WPF. Для первого раза, думаю, будет достаточно, чтобы не отбить у читателей охоту изучать F# в дальнейшем.