xamlf#elmish-wpf

Selecting all text in a textbox using cmdParam (Elmish.WPF/F#)


In order to select all text in a textbox with a click in a WPF app as well as in an Elmish.WPF app, I can use this C#/XAML code:

file SettingsPage.xaml:

<TextBox ....some code... PreviewMouseLeftButtonDown="SelectAllText"> </TextBox>

file Settings.xaml.cs (code behind):

 public partial class Settings : UserControl
    {
        public Settings() => InitializeComponent();

        private void SelectAllText(object sender, MouseButtonEventArgs e)
        {
            TextBox textBox = sender as TextBox;

            if (textBox == null)
            {
                return;
            }

            if (!textBox.IsKeyboardFocusWithin)
            {
                textBox.SelectAll();
                e.Handled = true;
                textBox.Focus();
            }
        }
    }

The code works as expected. But I would like to use F#/Elmish.WPF code instead of C# code. I have tried to create the F# equivalent of the aforementioned code using cmdParam - see below. But it does not work as expected - the text is selected and then it is immediately unselected. Why does it not work and what do I do wrong?

If you need to see more code than shown below, you can find the entire VS solution on GitHub.

file SettingsPage.xaml:

<TextBox  .. some code ... >
    <i:Interaction.Triggers>                    
        <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
            <i:InvokeCommandAction PassEventArgsToCommand="True" Command="{Binding TextBoxClicked}" />
        </i:EventTrigger>                   
    </i:Interaction.Triggers>
</TextBox>

file Settings.fs:

module Settings =    
    //some code          
    type Msg =
        | TextBoxClickedEvent
       //some other messages
    
    let castAs<'T when 'T : null> (o:obj) = 
        match o with
        | :? 'T as res -> res
        | _            -> null //TODO       
    
    let paramTextBoxClickedEvent (p: obj) =  
        let e = castAs<MouseButtonEventArgs>(p)   
        let sender = e.Source
        let textBox = castAs<TextBox>(sender)
        let e =             
            match textBox.IsKeyboardFocusWithin with 
            | true  -> ()
            | false -> 
                      textBox.SelectAll()     
                      e.Handled = true |> ignore
                      textBox.Focus()  |> ignore
        e  
        TextBoxClickedEvent
      
    let update (msg: Msg) (m: Model) : Model * Cmd<Msg> = 
        match msg with   
        | TextBoxClickedEvent  -> m, Cmd.none 
        //some other code

    let bindings(): Binding<Model,Msg> list =
        [ 
           //some code
          "TextBoxClicked" |> Binding.cmdParam paramTextBoxClickedEvent  
        ]      

Solution

  • What was wrong:

    The equivalent of C# code e.Handled = true; is not e.Handled = true in F#, but e.Handled <- true.

    Answer:

    The relevant function is now: `

      let paramTextBoxClickedEvent (p: obj) = 
            let e = castAs<MouseButtonEventArgs>(p) 
            let sender = e.Source
            let textBox = castAs<TextBox>(sender) 
             
            let e =             
                match textBox.IsKeyboardFocusWithin with 
                | true  -> ()
                | false -> 
                          textBox.SelectAll()     
                          e.Handled <- true
                          textBox.Focus() |> ignore
            e  
            TextBoxClickedEvent
    

    This code now works. It may give you more inside into how Binding.cmdParam works. Anyway, do consider Bent Tranberg's comment (related to C#/code behind and separation of logic) first before using the F#/Elmish.WPF solution.