Close dialog with FSharp.ViewModule

In my previous question, “Enabling the OK dialog using FSharp.ViewModule,” I went so far that the OK dialog button was only enabled when the validators for the dialog fields were true and the ViewModule IsValid property became true. But after that, I ran into several problems:

1) Clicking the OK button did not close the dialog box, even if I installed it IsDefault="true"in XAML.

2) When the OK button is clicked, sometimes I want to do more checks than those provided by ViewModule validators (for example, checking email addresses). Then I want to stop closing the dialog box if this custom check fails.

But I do not know how to do this when using F # and MVVM. At first I tried to put XAML in a C # project and view model code in the F # library. And then I used the OK button. Click "Handler" in the code to close the window. This is fixed 1), but not 2).

So this is my XAML:

<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="OK" IsEnabled="{Binding IsValid}" IsDefault="true" Command="{Binding OkCommand}" 
    <!--Click="OnOK"--> />

And my view model is with a comment in validateto show what I want to do when the OK button is pressed:

let name = self.Factory.Backing( <@ self.Name @>, "", notNullOrWhitespace)
let email = self.Factory.Backing( <@ self.Email @>, "", notNullOrWhitespace)
let dialogResult = self.Factory.Backing( <@ self.DialogResult @>, false )

let isValidEmail (e:string) = e.Length >= 5

member self.Name 
    with get() = name.Value 
    and set value = name.Value <- value
member self.Email 
    with get() = email.Value 
    and set value = email.Value <- value
member self.DialogResult
    with get() = dialogResult.Value
    and set value = dialogResult.Value <- value

member self.OkCommand = self.Factory.CommandSync(fun () ->
                        if not <| isValidEmail(email.Value) then
                            MessageBox.Show("Invalid Email") |> ignore
                        else
                            dialogResult.Value <- true
                        )
+4
source share
1 answer

It is worth noting that MVVM and code are not best friends.

#, , Window code-behind (.. ). - , MVVM. XAML MVVM Commands.

A - , .

, FsXaml ( XAML), .

, XAML, .

UserDialog.xaml

<Button x:Name="butt" ... >

UserDialog.xaml.fs

namespace Views

open FsXaml

type UserDialogBase = XAML<"UserDialog.xaml">

type UserDialog() as dlg =
    inherit UserDialogBase()

    do dlg.butt.Click.Add( fun _ -> dlg.DialogResult <- System.Nullable(true) )

ViewModel, . :

B - MVVM DialogCloser.

(Solution Explorer)

DialogCloser.fs

namespace Views

open System.Windows

type DialogCloser() =    
    static let dialogResultProperty =
        DependencyProperty.RegisterAttached("DialogResult", 
            typeof<bool>, typeof<DialogCloser>, 
                new PropertyMetadata(DialogCloser.DialogResultChanged))

    static member SetDialogResult (a:DependencyObject) (value:string) = 
        a.SetValue(dialogResultProperty, value)

    static member DialogResultChanged 
        (a:DependencyObject) (e:DependencyPropertyChangedEventArgs) =
        match a with
        | :? Window as window
            -> window.DialogResult <- System.Nullable (e.NewValue :?> bool)
        | _ -> failwith "Not a Window"

, WpfApp ( XAML), DialogCloser :

UserDialog.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="clr-namespace:Views;assembly=WpfApp"
    xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
    views:DialogCloser.DialogResult="{Binding DialogResult}"
    >

        ...

</Window>

UserDialog ViewModel Command , dialogResult true.

member __.OkCommand = __.Factory.CommandSync(fun () ->
                         if not <| isValidEmail(email.Value) then
                             System.Windows.MessageBox.Show ("...") |> ignore                       
                         else 
                             // do stuff (e.g. saving data)

                             ...

                             // Terminator
                             dialogResult.Value <- true 
                         )

if/else .

, MainViewModel :

UserDialog.xaml.fs

namespace Views

open FsXaml

type UserDialog = XAML<"UserDialog.xaml">

module UserDialogHandling =    
    /// Show dialog and return result
    let getResult() = 
        let win = UserDialog()
        match win.ShowDialog() with
        | nullable when nullable.HasValue
            -> nullable.Value
        | _ -> false

, "-" ( UserDialog).

+4

Source: https://habr.com/ru/post/1662934/


All Articles