在F#WPF MVVM应用程序中绑定到TextBox的TextChanged事件

时间:2017-01-09 00:47:06

标签: wpf xaml mvvm f# fsxaml

我想绑定到使用FsXaml和FSharp.ViewModule构建的MVVM F#应用程序中的WPF TextBox。我添加了一个名为" SetA"的命令。到描述here的应用程序,并尝试使用以下XAML绑定到它:

<TextBox Text="{Binding Score.ScoreA, Mode=OneWay}" FontFamily="Lucida Console">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged">
            <fsx:EventToCommand Command="{Binding SetA}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

从ScoreA属性正确填充TextBox,但是当我在TextBox中键入新值时,不会调用SetA命令。我对F#很满意,但这是我的第一个WPF MVVM应用程序,所以我不确定我做错了什么。当用户更改文本值时,如何触发我的SetA处理程序?

这是我的ViewModel:

type MainViewModel(controller : Score -> ScoringEvent -> Score) as self = 
    inherit EventViewModelBase<ScoringEvent>()

    let score = self.Factory.Backing(<@ self.Score @>, Score.zero)

    let eventHandler ev =
        score.Value <- controller score.Value ev

    do
        self.EventStream
        |> Observable.add eventHandler

    member this.IncA = this.Factory.EventValueCommand(IncA)
    member this.DecA = this.Factory.EventValueCommandChecked(DecA, (fun _ -> this.Score.ScoreA > 0), [ <@@ this.Score @@> ])
    member this.IncB = this.Factory.EventValueCommand(IncB)
    member this.DecB = this.Factory.EventValueCommandChecked(DecB, (fun _ -> this.Score.ScoreB > 0), [ <@@ this.Score @@> ])
    member this.NewGame = this.Factory.EventValueCommand(New)
    member this.SetA = this.Factory.EventValueCommand(SetA)

    member __.Score = score.Value 

我只添加了定义SetA的一行。

1 个答案:

答案 0 :(得分:6)

最有可能调用Command,但结果不是您所期望的。

出于测试目的,请尝试

member this.SetA = 
   this.Factory.CommandSync(
        fun _ -> System.Windows.MessageBox.Show("TextChanged","RoutedEv") |> ignore)

请注意CommandSync而不是EventValueCommand。无论你如何改变分数,都会弹出一个msgbox。

您还可以保留EventValueCommand

member this.SetA = this.Factory.EventValueCommand(SetA)

并在update有趣的

中执行相同操作
let update score event =
    match event with
    | IncA -> {score with ScoreA = score.ScoreA + 1}
    | DecA -> {score with ScoreA = max (score.ScoreA - 1) 0}
    | IncB -> {score with ScoreB = score.ScoreB + 1}
    | DecB -> {score with ScoreB = max (score.ScoreB - 1) 0}
    | New -> zero 
    | SetA -> System.Windows.MessageBox.Show("TextChanged","RoutedEv") |> ignore ; score

回到而不是您期望的部分,我假设您希望在更改TextBox.Text值时更新记分板中的分数。

正如您已经注意到,没有(纯粹的)方法可以使用当前TextBox.Textupdate值带入controller乐趣。 由于SetX行为未得到修复,因此与IncX,DecX或New事件的情况不同。

选项:

  • 您可以将控制器的签名更改为Score -> ScoringEvent -> string option -> Score,并使用选项值来处理update fun中的SetX事件。

  • 您可以处理ViewModel中的所有内容。 记录的不可变性强迫使用OneWay Binding。我会保持它不可变,但你可以(de)组成ViewModel中的所有内容。

尝试

<强>视图模型

type MainViewModel(controller : Score -> ScoringEvent -> Score) as self = 
    inherit EventViewModelBase<ScoringEvent>()

    let scoreA = self.Factory.Backing(<@ self.ScoreA @>, 0)
    let scoreB = self.Factory.Backing(<@ self.ScoreB @>, 0)

    let updateVM score = 
        scoreA.Value <- score.ScoreA
        scoreB.Value <- score.ScoreB

    let eventHandler ev = 
        updateVM <| controller {ScoreA = scoreA.Value ; ScoreB = scoreB.Value} ev

    do
        self.EventStream
        |> Observable.add eventHandler

    member this.IncA = this.Factory.EventValueCommand(IncA)
    member this.DecA = this.Factory.EventValueCommandChecked(DecA, (fun _ -> this.ScoreA > 0), [ <@@ this.ScoreA @@> ])
    member this.IncB = this.Factory.EventValueCommand(IncB)
    member this.DecB = this.Factory.EventValueCommandChecked(DecB, (fun _ -> this.ScoreB > 0), [ <@@ this.ScoreB @@> ])
    member this.NewGame = this.Factory.EventValueCommand(New)

    member __.ScoreA
        with get() = scoreA.Value 
         and set v = scoreA.Value <- v
    member __.ScoreB
        with get() = scoreB.Value 
         and set v = scoreB.Value <- v

<强> XAML

<DockPanel LastChildFill="True">
    <StackPanel DockPanel.Dock="Bottom" Height="75" Orientation="Horizontal" Background="#FF3A3A3A">
        <Button Height="70" Width="70" Margin="2" Command="{Binding NewGame}">
            <TextBlock Text="New" FontSize="18"></TextBlock>
        </Button>
        <TextBox Text="{Binding ScoreA, UpdateSourceTrigger=PropertyChanged}" 
                 Margin="2" FontSize="18" HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
        </TextBox>
    </StackPanel>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Border Background="#FF024D70" Grid.Column="0">
            <Grid>
                <Viewbox>
                    <Label Content="{Binding ScoreA}" ContentStringFormat="D2" FontFamily="Lucida Console"></Label>
                </Viewbox>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                    <Button Command="{Binding DecA}"  Height="70" Width="70" VerticalAlignment="Bottom" Opacity="0.5" Margin="20">
                        <TextBlock Text="-" FontSize="36" Height="60"></TextBlock>
                    </Button>
                    <Button Command="{Binding IncA}"  Height="70" Width="70" VerticalAlignment="Bottom" Opacity="0.5" Margin="20">
                        <TextBlock Text="+" FontSize="36" Height="60"></TextBlock>
                    </Button>
                </StackPanel>
            </Grid>
        </Border>
        <Border Background="#FF7E0E03" Grid.Column="1">
            <Grid>
                <Viewbox>
                    <Label Content="{Binding ScoreB}" ContentStringFormat="D2"  FontFamily="Lucida Console"></Label>
                </Viewbox>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                    <Button Command="{Binding DecB}"  Height="70" Width="70" VerticalAlignment="Bottom" Opacity="0.5" Margin="20">
                        <TextBlock Text="-" FontSize="36" Height="60"></TextBlock>
                    </Button>
                    <Button Command="{Binding IncB}"  Height="70" Width="70" VerticalAlignment="Bottom" Opacity="0.5" Margin="20">
                        <TextBlock Text="+" FontSize="36" Height="60"></TextBlock>
                    </Button>
                </StackPanel>
            </Grid>
        </Border>
    </Grid>
</DockPanel>

备注

  • Score.ScoreX已更改为ScoreX
  • Text="{Binding Score.ScoreA, Mode=OneWay}"已更改为Text="{Binding ScoreA, UpdateSourceTrigger=PropertyChanged}"
  • 删除了Interaction.Trigger