3

I have a simple dialog with two fields for entering a person's name and address, and I only want the OK button to be enabled when they both have values. Here are the relevant parts of my view:

<TextBox Text="{Binding Name}" ... />
<TextBox Text="{Binding Address}" ... />
<Button Content="OK" IsEnabled="{Binding OK, Mode=OneWay}" ... />

And this is my view model:

namespace ViewModels

open FSharp.ViewModule
open FSharp.ViewModule.Validation
open FsXaml

type DialogView = XAML<"Dialog.xaml">

type DialogVM() as self =
    inherit ViewModelBase()  

    let name = self.Factory.Backing( <@ self.Name @>, "", notNullOrWhitespace )
    let address = self.Factory.Backing( <@ self.Address @>, "" )

    let hasValue str = not( System.String.IsNullOrWhiteSpace( str ))

    member x.Name 
        with get() = name.Value 
        and set value = name.Value <- value ; self.RaisePropertyChanged( <@ self.OK @> )
    member x.Address 
        with get() = address.Value 
        and set value = address.Value <- value ; self.RaisePropertyChanged( <@ self.OK @> )
    member x.OK with get() = hasValue x.Name && hasValue x.Address

But the OK button is always enabled. What am I doing wrong?

DenisV
  • 279
  • 1
  • 8

1 Answers1

3

The problem is that the default mode of binding for TextBox.Text = LostFocus. The property is just not updated.

So:

    <TextBox Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}"/>
    <TextBox Text="{Binding Address,UpdateSourceTrigger=PropertyChanged}" />

But there are a few things in FSharp.ViewModule which make your life easier. In your case it is the property IsValid :

let name = self.Factory.Backing( <@ self.Name @>, "", notNullOrWhitespace)
let address = self.Factory.Backing( <@ self.Address @>, "", notNullOrWhitespace)

member x.Name 
    with get() = name.Value 
    and set value = name.Value <- value
member x.Address 
    with get() = address.Value 
    and set value = address.Value <- value

In XAML:

<Button Content="OK" IsEnabled="{Binding IsValid}"  />
FoggyFinder
  • 2,230
  • 2
  • 20
  • 34
  • Thanks for that, but I don't see the IsValid property in the code? – DenisV Dec 02 '16 at 20:11
  • @DenisV it's in ViewModelBase – Reed Copsey Dec 02 '16 at 20:24
  • @ReedCopsey Newbie mistake - I forgot to set the Window.DataContext. Duh! But this validation approach looks really nice and simple - well done. – DenisV Dec 03 '16 at 04:12
  • @ReedCopsey Another question - what's the recommended approach for closing a dialog when the OK button is clicked? – DenisV Dec 03 '16 at 04:13
  • 2
    @denisv set `IsDefault="true"` on the button. – Reed Copsey Dec 03 '16 at 04:17
  • @ReedCopsey IsDefault="true" didn't work. So I ended up putting the Xaml in a C# project and the F# code in a separate library. Then I added a Click handler to the OK button that called Close() in the code behind. I don't know if there's a better way of doing it? – DenisV Dec 05 '16 at 04:06
  • @DenisV Why don't you want to create a new question or ask in the chat? Because the comments to another question is not the best place for such discussions. – FoggyFinder Dec 05 '16 at 09:36