F # / WPF привязка событий - PullRequest
4 голосов
/ 11 ноября 2010

Я блуждаю, если есть способ подключить событие, определенное в XAML, к функции-члену F #?Конечно, я мог бы сделать это схематично, но это немного неудобно.

Ответы [ 4 ]

9 голосов
/ 11 ноября 2010

Полагаю, вопрос в том, можете ли вы указать член F # в качестве обработчика событий, используя разметку XAML:

<Button x:Name="btnClick" Content="Click!" Click="button1_Click" />

Насколько я знаю, ответ: Нет .

Способ, которым это работает в C #, заключается в том, что регистрация обработчика событий выполняется в коде C # (частичный класс), сгенерированном дизайнером (вы можете видеть это в каталоге obj в файлах с именем, например, MainForm.g.cs).F # не имеет прямой поддержки дизайнера WPF, поэтому он не может сгенерировать это для вас.Вам нужно будет написать код, чтобы прикрепить обработчики событий вручную (но это довольно просто).

У меня есть несколько примеров в моем лондонском выступлении о Silverlight .Вы можете реализовать оператор ?, чтобы получить хороший доступ к элементам XAML:

 type MainPage() as this =
   inherit UserControl()
   let uri = new System.Uri("/App;component/MainPage.xaml", UriKind.Relative)
   do Application.LoadComponent(this, uri)

   // Get button using dynamic access and register handler
   let btn : Button = this?btnClick
   do btnClick.Click.Add(fun _ -> (* ... *))

Объявление оператора ?, которое я использовал:

let (?) (this : Control) (prop : string) : 'T = // '
  this.FindName(prop) :?> 'T
4 голосов
/ 25 августа 2011

Возможно добавить привязку к команде, например.используя свойство Command = "..." в кнопке.

Таким образом, в вашем XAML вы можете иметь:

<Button Command="{Binding MyCommandHandler}">

Тогда в вашем коде ViewModel, если у вас есть член с именем MyCommandHandler, он будет связан с вышеуказанной кнопкой.Так что в вашем F # что-то вроде:

module ViewModel =
    type FuncCommand (canExec:(obj -> bool), doExec:(obj -> unit)) =     
        let theEvent = new DelegateEvent<EventHandler>()     
        interface ICommand with         
            [<CLIEvent>]         
            member x.CanExecuteChanged = theEvent.Publish         
            member x.CanExecute arg = canExec(arg)         
            member x.Execute arg = doExec(arg)

    type MyViewModel() =
        member this.MyCommandHandler =          
            new FuncCommand(             
                (fun _ -> ... SOME CODE WHICH RETURNS TRUE OR FALSE ...),             
                (fun _ -> ... SOME CODE TO HANDLE THE CLICK ...)
            )
2 голосов
/ 08 марта 2017

Это теперь поддерживается в новых версиях FsXaml , хотя работает немного иначе, чем в C #.

Используя FsXaml, вы можете определить свой Xaml и указать свой обработчик событий,Например, в окне с именем «MyWindow» вы можете сделать:

<Button Content="Click!" Click="button1_Click" />

В вашем файле с кодом вы обработаете это так:

type MyWindowBase = XAML<"MyWindow.xaml">
type MyWindow () =
    inherit MyWindowBase

    override this.button1_Click (_,_) = () // Handle event here
2 голосов
/ 07 августа 2016

Вы можете сделать это, используя вложенное свойство:

namespace Foo
open System.Windows
open System.Windows.Controls
open System.Windows.Controls.Primitives
open System.Windows.Media

module Register = 
    // http://stackoverflow.com/a/14706890/1069200
    type internal Marker = interface end

    let ClickHandlerProperty = DependencyProperty.RegisterAttached(
                                "ClickHandler", 
                                typeof<RoutedEventHandler>, 
                                typeof<Marker>.DeclaringType, 
                                PropertyMetadata(null))

    let SetClickHandler (element: UIElement, value : RoutedEventHandler) = 
        element.SetValue(ClickHandlerProperty, value)

    let GetClickHandler (element: UIElement) : RoutedEventHandler = 
        element.GetValue(ClickHandlerProperty) :?> _

    let private OnClick (sender : obj) args = 
        let button = sender :?> UIElement
        let handler = GetClickHandler button
        if not (obj.ReferenceEquals(handler, null)) then
            handler.Invoke(sender, args)

    let private initialize =
        EventManager.RegisterClassHandler(
            typeof<FrameworkElement>, 
            ButtonBase.ClickEvent, 
            RoutedEventHandler(OnClick))

Затем используйте его в xaml следующим образом:

<Window ...
        xmlns:foo="clr-namespace:Foo;assembly=Foo">
    <Button Content="Click!" foo:Register.ClickHandler="{x:Static foo:Bar.OnClicked}" />
</Window>

Где бар:

namespace Foo
open System.Windows

module Bar =
    let OnClicked =
        let onClick _ _ = MessageBox.Show "clicked" |> ignore
        RoutedEventHandler(onClick)

Я не знаю f #, так что приведенный выше код, вероятно, можно много почистить.

Для события click предложение Дэвида связать команду, вероятно, самое приятное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...