3

I have tried to create a "UserControl" "By-Code" only, using F# (Vers. 12.0.30815.0 + Framework 4.5) since I prefer this "way" (no mix between C# and F#).

NOTE: I have to show multiple properties of a single Class in an Input Form, so this UserControl need to implement Binding capabilities to connect the "output" to the Class's Properties.

I tried to modify the numeric values inside the window (i.e. 1200 or 400) or moving the focus between the controls using the TAB key located on the keyboard, but everiting seem locked.

No update of the numerical value can be achieved. I can only delete one or more single figures, without any updates.

Perhaphs there is a conceptual error inside the Event "dataChangedEvent" that I have implemented inside the UserControls (i.e. "Data" property of the "ucData" Control - see the Code). It is something like: "INotifyPropertyChanged".

NOTE: perhaphs this error in the implementation of the Event is also present with the "Description" and "UnitOfMeasure" Porperties of the UserControl.

Can somebody can hel me to discover where is/are my error/erros in the here below reported Lines of Codes?

many Thanks in advance.

ANN

DEFINE THE USER CONTROL

type ucData() as this =
    inherit Windows.Controls.UserControl()

    static let  OnDataPropertyChanged (sender:DependencyObject) (e:DependencyPropertyChangedEventArgs) =
        let control = unbox<ucData>(sender)
        let newValue = unbox<double>(e.NewValue)
        let oldValue = unbox<double>(e.OldValue)
        System.Console.WriteLine
            (sprintf 
                ">>> OnPropertyChanged 'ucData':'Data': Control Name: %s; Value: %f --> %f <<<" 
                control.Name oldValue newValue )
        let argsEvent = new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)
        argsEvent.RoutedEvent <- ucData.DataChangedEvent  // I get an ERROR here!!!!
        control.RaiseEvent(argsEvent)


    static let OnCoerceDataProperty (sender:DependencyObject) (data:obj) =
        let control = unbox<ucData>(sender)
        let value = unbox<double>(data)
        System.Console.WriteLine
            (sprintf 
                ">>> OnCoerceValue 'ucData':'Data': Control Name: %s; Value: : %f <<<" 
                control.Name value )
        box(value)

    static let OnValidateDataProperty (data:obj) =
        System.Console.WriteLine
            (sprintf 
                ">>> OnValidateValue 'ucData':'Data': Data %s <<<" 
                (data.ToString()) )
        data.GetType() = typeof<double>

    static let dpData = 
        DependencyProperty.Register("Data",typeof<double>, typeof<ucData>, 
            new FrameworkPropertyMetadata( 0.0,
                FrameworkPropertyMetadataOptions.Inherits,
                new PropertyChangedCallback(OnDataPropertyChanged),
                new CoerceValueCallback(OnCoerceDataProperty) ),
            new ValidateValueCallback(OnValidateDataProperty) )

    static let reDataChangedEvent =
        EventManager.RegisterRoutedEvent
            ("DataChanged", RoutingStrategy.Bubble, 
                typeof<RoutedPropertyChangedEventHandler<double>>, typeof<ucData>)

    let dataChangedEvent = 
        let e = new  Event<RoutedPropertyChangedEventHandler<double>,RoutedPropertyChangedEventArgs<double>>() 
        // Equialent to: 
        //    public event RoutedPropertyChangedEventHandler<double> DataChanged
        //    {
        //       add { AddHandler(DataChangedEvent, value); }
        //       remove { RemoveHandler(DataChangedEvent, value); }
        //    }
        // where DataChangedEvent is so defined:
        //    public static readonly RoutedEvent DataChangedEvent;        
        e

    let grid = 
        let c = new Grid()
        let colData = new ColumnDefinition()
        colData.MinWidth <- 70.
        c.ColumnDefinitions.Add( colData )
        c

    let data= 
        let c = new TextBox()
        c.Margin<- new Thickness(3.0)
        c.SetValue(Grid.ColumnProperty,1)
        c

    do        

        grid.Children.Add(data) |> ignore
        this.AddChild(grid) |> ignore

        let bData = new Binding()
        bData.Path <- new PropertyPath("Data")
        bData.StringFormat <- "N"
        bData.ConverterCulture <- System.Globalization.CultureInfo.InvariantCulture
        bData.Mode <- BindingMode.TwoWay 
        bData.RelativeSource <- new RelativeSource(RelativeSourceMode.FindAncestor,typeof<ucData>,1)

        data.SetBinding(TextBox.TextProperty, bData) |> ignore

    static member DataProperty = dpData

    [<Description("Specify the Numerical Data"); Category("UserData")>]
    member x.Data
        with get() = 
            let res = x.GetValue(ucData.DataProperty) 
            (res :?> double)
        and set (v:double) = 
            x.SetValue(ucData.DataProperty, v )

    [<CLIEvent>]
    static member DataChangedEvent with get() = reDataChangedEvent   // I get an ERROR here!!!

    [<CLIEvent>]
    member x.DataChanged = dataChangedEvent.Publish 

CREATE THE WINDOW

    type TestWindow() as this =
        inherit Window()

    let c1 = new ucData()   
    let c2 = new ucData()   

    do
        c1.Name <- "Data1"
        c2.Name <- "Data2"

        this.Width <- 300.
        this.Height <- 300.

        let sp = new StackPanel()

        c1.Data <- 1200.
        c2.Data <- 400.

        sp.Children.Add(c1) |> ignore
        sp.Children.Add(c2) |> ignore

        c1.DataChanged.Add( fun args ->
            MessageBox.Show("The button labeled \"" + c1.Data.ToString()) |>ignore )

        c2.DataChanged.Add( fun args ->
            MessageBox.Show("The button labeled \"" + c1.Data.ToString()) |>ignore )

        this.Content <- sp

RUN THE TEST WINDOW

let w = new TestWindow()
w.Show()

1 Answers1

0

As per this answer, you need to start a WPF event loop in FSI. I had to tweak it slightly due to being old. Here's a complete working version of your script:

#I @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5"
#r @"WindowsBase.dll"
#r @"PresentationCore.dll"
#r @"PresentationFramework.dll"
#r @"System.Xaml.dll"
#r @"UIAutomationProvider.dll"
#r @"UIAutomationTypes.dll"


module WPFEventLoop =     
    open System    
    open System.Windows    
    open System.Windows.Threading    
    open Microsoft.FSharp.Compiler.Interactive    
    open Microsoft.FSharp.Compiler.Interactive.Settings    

    type RunDelegate<'b> = delegate of unit -> 'b     
    let Create() =         
        let app  =             
            try                 
                // Ensure the current application exists. This may fail, if it already does.                
                let app = new Application() in                 
                // Create a dummy window to act as the main window for the application.                
                // Because we're in FSI we never want to clean this up.                
                new Window() |> ignore;                 
                app              
            with :? InvalidOperationException -> Application.Current        
        let disp = app.Dispatcher        
        let restart = ref false        
        { new IEventLoop with             
            member x.Run() =                    
                app.Run() |> ignore                 
                !restart             

            member x.Invoke(f) =                  
                try 
                    disp.Invoke(DispatcherPriority.Send,new RunDelegate<_>(fun () -> box(f ()))) |> unbox                 
                with e -> eprintf "\n\n ERROR: %O\n" e; reraise()             

            member x.ScheduleRestart() =   ()                 
            //restart := true;                 
            //app.Shutdown()        
         }     

    let Install() = fsi.EventLoop <-  Create()

WPFEventLoop.Install()

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //

open System
open System.Windows
open System.Windows.Data 
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media  
open System.ComponentModel 

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //

type ucData() as this =
    inherit Windows.Controls.UserControl()

    // ---------------------------------------------------------------------------------------------------------------- //

    static let dpDescription = 
        DependencyProperty.Register("Description", typeof<string>, typeof<ucData>)

    static let dpData = 
        DependencyProperty.Register("Data", typeof<double>, typeof<ucData>)

    static let dpUnitOfMeasure = 
        DependencyProperty.Register("UnitOfMeasure", typeof<string>, typeof<ucData>) 

    // ---------------------------------------------------------------------------------------------------------------- //

    let descriptionChangedEvent = new  Event<RoutedEventHandler, RoutedEventArgs>()

    let dataChangedEvent = new Event<RoutedEventHandler, RoutedEventArgs>()

    let unitOfMeasureChangedEvent = new Event<RoutedEventHandler, RoutedEventArgs>()

    // ---------------------------------------------------------------------------------------------------------------- //

    let grid = 
        let c = new Grid()
        let colDesc = new ColumnDefinition()
        colDesc.MinWidth <- 100.
        colDesc.Width <- System.Windows.GridLength(100.,GridUnitType.Star)
        c.ColumnDefinitions.Add( colDesc )
        let colData = new ColumnDefinition()
        colData.MinWidth <- 70.
        colDesc.Width <- System.Windows.GridLength(70.,GridUnitType.Star)
        c.ColumnDefinitions.Add( colData )
        let colUnit = new ColumnDefinition()
        colUnit.MinWidth <- 70.
        colUnit.Width <- System.Windows.GridLength(70.,GridUnitType.Star)
        c.ColumnDefinitions.Add( colUnit )
        c

    // ---------------------------------------------------------------------------------------------------------------- //

    let desc = 
        let c = new Label()
        c.Margin<- new Thickness(3.0)
        c.SetValue(Grid.ColumnProperty,0)
        c

    // ---------------------------------------------------------------------------------------------------------------- //

    let data= 
        let c = new TextBox()
        c.Margin<- new Thickness(3.0)
        c.SetValue(Grid.ColumnProperty,1)
        c

    // ---------------------------------------------------------------------------------------------------------------- //

    let unit = 
        let c = new Label()
        c.Margin<- new Thickness(3.0)
        c.SetValue(Grid.ColumnProperty,2)
        c

    // ---------------------------------------------------------------------------------------------------------------- //

    let condassign a b c = 
        match a with
        |   true -> b
        | false -> c

    // ---------------------------------------------------------------------------------------------------------------- //

    do        
        grid.Children.Add(desc) |> ignore
        grid.Children.Add(data) |> ignore
        grid.Children.Add(unit) |> ignore
        //this.Content <- grid |> ignore
        this.AddChild(grid) |> ignore

        // ---------------------------------------------------------------------------------------------------------------- //

        this.Data <- 0.00
        this.Description <- "<Description>"
        this.UnitOfMeasure <- "<UM>"

        // ---------------------------------------------------------------------------------------------------------------- //

        this.DataContext <- this 

        // ---------------------------------------------------------------------------------------------------------------- //

        let bDesc = new Binding()
        bDesc.Path <- new PropertyPath("Description")
        bDesc.Mode <- BindingMode.OneWay
        bDesc.RelativeSource <- new RelativeSource(RelativeSourceMode.FindAncestor,typeof<ucData>,1)

        let bData = new Binding()
        bData.Path <- new PropertyPath("Data")
        bData.Mode <- BindingMode.TwoWay 
        bData.RelativeSource <- new RelativeSource(RelativeSourceMode.FindAncestor,typeof<ucData>,1)

        let bUnit = new Binding()
        bUnit.Path <- new PropertyPath("UnitOfMeasure")
        bUnit.Mode <- BindingMode.OneWay
        //bUnit.RelativeSource <- new RelativeSource(RelativeSourceMode.FindAncestor,typeof<ucData>,1) 
        bUnit.Source <- this

        // ---------------------------------------------------------------------------------------------------------------- //

        desc.SetBinding(Label.ContentProperty, bDesc) |> ignore
        data.SetBinding(TextBox.TextProperty, bData) |> ignore
        unit.SetBinding(Label.ContentProperty, bUnit) |> ignore

    // ---------------------------------------------------------------------------------------------------------------- //

    static member DescriptionProperty = dpDescription

    [<Description("Description of the Numerical Data"); Category("UserData")>]
    member x.Description
        with get() =
            let res = x.GetValue(ucData.DescriptionProperty) 
            (res :?> string)
        and set (v:string) = 
            x.SetValue(ucData.DescriptionProperty, (condassign (v=null) " " v) )

    // ---------------------------------------------------------------------------------------------------------------- //

    [<CLIEvent>]
    member x.DescriptionChanged = descriptionChangedEvent.Publish 

    static member DescriptionChangedEvent =
        EventManager.RegisterRoutedEvent
            ("DescriptionChanged", RoutingStrategy.Bubble, 
                typeof<RoutedEventHandler>, typeof<ucData>)

    member x.OnDescriptionChangedEvent() =
        let argsEvent = new RoutedEventArgs()
        argsEvent.RoutedEvent <- ucData.DescriptionChangedEvent 
        argsEvent.Source <- x
        descriptionChangedEvent.Trigger(this, argsEvent)

    // ---------------------------------------------------------------------------------------------------------------- //

    static member DataProperty =dpData

    [<Description("Specify the Numerical Data"); Category("UserData")>]
    member x.Data
        with get() = 
            let res = x.GetValue(ucData.DataProperty) 
            (res :?> double)
        and set (v:double) = 
            x.SetValue(ucData.DataProperty, v )

    // ---------------------------------------------------------------------------------------------------------------- //

    [<CLIEvent>]
    member x.DataChanged = dataChangedEvent.Publish 

    static member DataChangedEvent =
        EventManager.RegisterRoutedEvent
            ("DataChanged", RoutingStrategy.Bubble, 
                typeof<RoutedEventHandler>, typeof<ucData>)

    member x.OnDataChangedEvent() =
        let argsEvent = new RoutedEventArgs()
        argsEvent.RoutedEvent <- ucData.DataChangedEvent 
        argsEvent.Source <- x
        dataChangedEvent.Trigger(this, argsEvent)

    // ---------------------------------------------------------------------------------------------------------------- //

    static member UnitOfMeasureProperty = dpUnitOfMeasure

    [<Description("Specify the 'Unit of Measure'"); Category("UserData")>]
    member x.UnitOfMeasure
        with get() = 
            let res = x.GetValue(ucData.UnitOfMeasureProperty) 
            (res :?> string)
        and set (v:string) = 
            x.SetValue(ucData.UnitOfMeasureProperty, (condassign (v=null) " " v) )

    // ---------------------------------------------------------------------------------------------------------------- //

    [<CLIEvent>]
    member x.UnitOfMeasureChanged = unitOfMeasureChangedEvent.Publish 

    static member UnitOfMeasureChangedEvent=
        EventManager.RegisterRoutedEvent
            ("UnitOfMeasureChanged", RoutingStrategy.Bubble, 
                typeof<RoutedEventHandler>, typeof<ucData>)

    member x.OnUnitOfMeasureChangedEvent() =
        let argsEvent = new RoutedEventArgs()
        argsEvent.RoutedEvent <- ucData.UnitOfMeasureChangedEvent
        argsEvent.Source <- x
        unitOfMeasureChangedEvent.Trigger(this, argsEvent)

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //

type TestWindow() as this =
    inherit Window()

    let c1 = new ucData()   
    let c2 = new ucData()   

    do
        this.Width <- 300.
        this.Height <- 300.

        let sp = new StackPanel()

        c1.Description <- "Pippo"
        c1.Data <- 1200.
        c1.UnitOfMeasure <- "mm"
        c1.BorderThickness <- new Thickness(2.0)

        c2.Description <- "Pippo2"
        c2.Data <- 400.
        c2.UnitOfMeasure <- "MPa"
        c2.BorderThickness <- new Thickness(2.0)

        sp.Children.Add(c1) |> ignore
        sp.Children.Add(c2) |> ignore

        this.Content <- sp

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //

let w = new TestWindow()
w.Show()
Community
  • 1
  • 1
Robert Jeppesen
  • 7,837
  • 3
  • 35
  • 50