F# WPF:处理 ListBox 中的点击事件

F# WPF: Handling click events in ListBox

我正在尝试使用 F# 和 WPF 创建一个简单的任务计划程序。它基本上只是一个任务列表,其中每个任务都有一个 'Delete' 按钮。处理列表外的按钮点击不是问题——这可以用常规命令处理。然而处理按钮单击 in 列表项并不简单。我尝试使用 here with binding routed to parent, but the sender object is always null (I expected it to be the task object from the collection). Also tried attaching a property as recommended here 中描述的 RelayCommand,但无法正常工作。

如何分配一个事件处理程序,通过单击删除按钮获取任务对象?

这里是 App.fs:

namespace ToDoApp

open System
open System.Windows
open System.Collections.ObjectModel
open System.Windows.Input
open FSharp.ViewModule
open FSharp.ViewModule.Validation
open FsXaml

type App = XAML<"App.xaml">
type MainView = XAML<"MainWindow.xaml">

type Task(str) = 
    member x.Description with get() = str

type MainViewModel() as self = 
    inherit ViewModelBase()  

    let tasks = new ObservableCollection<Task>()

    let addTaskCommand() =
        let descr = sprintf "Do something at %A" (DateTime.Now.AddMinutes(30.0))
        tasks.Add <| new Task(descr)

    member this.Tasks with get() = tasks
    member this.AddTask = this.Factory.CommandSync addTaskCommand

module main =
    [<STAThread>]
    [<EntryPoint>]
    let main argv =
        App().Run()

MainWindow.xaml:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ToDoApp;assembly=ToDoApp"
    xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
    Title="Simple ToDo app" Height="200" Width="400">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button Name="newJobButton" Command="{Binding AddTask}" Width="100" Height="32" Margin="5, 5, 5, 5" HorizontalAlignment="Left">New task</Button>
        <ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <ListBox Name="lstBox" ItemsSource="{Binding Tasks}" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="80" />
                            </Grid.ColumnDefinitions>
                            <Label Grid.Column="0" Content="{Binding Description}" Margin="5 5 0 0"/>
                            <!-- OnClick ??? -->
                            <Button Grid.Column="1">Delete</Button>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </ScrollViewer>
    </Grid>
</Window>

App.xaml 是微不足道的,所以我不在这里展示。

最好坚持使用命令:

ViewModel

member __.DelTask = 
    __.Factory.CommandSyncParam 
        (fun task -> tasks.Remove task |> ignore)

XAML

<Button Grid.Column="1" 
        Command="{Binding DataContext.DelTask, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 
        CommandParameter="{Binding}"
        >Delete</Button>

在 XAML 中使用事件处理程序会导致意大利面条代码更难测试和维护(即关注点分离,将业务逻辑问题与 UI 问题分开处理)。