F#/WPF привязка событий
Я блуждаю, если есть способ подключить событие, определенное в XAML, к функции-члену F#? Конечно, я мог бы сделать это схематично, но это немного неудобно.
4 ответа
Я предполагаю, что вопрос заключается в том, можете ли вы указать член 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
Можно добавить привязку к команде, например. используя свойство 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 ...)
)
Теперь это поддерживается в более новых версиях FsXaml, хотя работает немного иначе, чем в C#.
Используя FsXaml, вы можете определить свой Xaml и указать свой обработчик событий. Например, в окне с именем "MyWindow" вы можете сделать:
<Button Content="Click!" Click="button1_Click" />
В вашем файле code code вы должны обработать это так:
type MyWindowBase = XAML<"MyWindow.xaml">
type MyWindow () =
inherit MyWindowBase
override this.button1_Click (_,_) = () // Handle event here
Вы можете сделать это, используя прикрепленное свойство:
namespace Foo
open System.Windows
open System.Windows.Controls
open System.Windows.Controls.Primitives
open System.Windows.Media
module Register =
// http://stackru.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 предложение Дэвида связать команду, вероятно, самое приятное.