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#. Жду ваши комментарии и с удовольствием на них отвечу.   

No comments:

Post a Comment