F#/ WPF事件绑定

时间:2010-11-10 21:21:15

标签: wpf f#

如果有办法将XAML中定义的事件挂钩到成员的F#函数,我会徘徊吗?当然,我可以用图解方式来做,但它有点不方便。

4 个答案:

答案 0 :(得分:9)

我想问的是你是否可以使用XAML标记将F#成员指定为事件处理程序:

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

据我所知,答案是

这在C#中的工作方式是事件处理程序的注册是在设计者生成的C#代码(部分类)中完成的(您可以在名为{{1}的文件的obj目录中看到})。 F#对WPF设计器没有任何直接支持,因此无法为您生成此内容。你必须编写代码来手动附加事件处理程序(但这很容易)。

我的London talk about Silverlight中有一些例子。您可以实现MainForm.g.cs运算符以获得对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 _ -> (* ... *)) 运算符声明是:

?

答案 1 :(得分:4)

可以为命令添加绑定,例如。使用按钮中的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 :(得分:2)

您可以使用附加属性来执行此操作:

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

然后在xaml中使用它:

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))

栏位于:

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

我不知道f#所以上面的代码可能会被清理很多。

对于click事件,David建议绑定命令可能是最好的。

答案 3 :(得分:1)

现在FsXaml的较新版本支持此版本,但它与C#中的版本略有不同。

使用FsXaml,您可以定义Xaml并指定事件处理程序。例如,在名为&#34; MyWindow&#34;的窗口中,您可以执行以下操作:

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

在您的&#34;代码背后&#34;文件,你会这样处理:

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

    override this.button1_Click (_,_) = () // Handle event here