Coder Social home page Coder Social logo

fsharp.viewmodule's Introduction

FSharp.ViewModule NuGet Status

Library providing MVVM and INotifyPropertyChanged support for F# projects

Maintainer(s)

The default maintainer account for projects under "fsprojects" is @fsprojectsgit - F# Community Project Incubation Space (repo management)

fsharp.viewmodule's People

Contributors

bengtrosenberger avatar forki avatar fsgit avatar fsprojectsgit avatar marcinjuraszek avatar reedcopsey avatar sergey-tihon avatar tihan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fsharp.viewmodule's Issues

Command with multibinding parameter

I want to initialize multiple FSharp.Charting charts on window "Loaded" event via binding theirs WindowsFormsHosts to the command parameter like this:

<ia:InvokeCommandAction Command="{Binding InitializeCommand}" >
                <ia:InvokeCommandAction.CommandParameter>
                    <MultiBinding Converter="{StaticResource MultiChartInitCommandConverter}">
                        <Binding ElementName="chart0"/>
                        <Binding ElementName="chart1"/>
                    </MultiBinding>
                </ia:InvokeCommandAction.CommandParameter>
                </ia:InvokeCommandAction>

I'm creating command like this:

member self.InitializeCommand = self.Factory.CommandSyncParamChecked(handler, fun _ -> true)

Converter:

type MultiChartInitCommandConverter() = 
  interface IMultiValueConverter with  
    member x.Convert(vs, _, _, _) = 
      [| vs.[0] :?> WindowsFormsHost
         vs.[1] :?> WindowsFormsHost |] :> obj    
    member x.ConvertBack(_, _, _, _) = failwith "cannot convertBack"

And handler:

let handler (charts : obj) = //...

The problem is that the handler doesn't get executed. It seems like a problem with type signature: InitializeCommand.canExecute((*...*)) always returns false no matter what I pass(obj/obj[]/WindowsFormsHost[]).

Also, when I try to pass multiple strings, it works fine. Is this a possible issue or just I'm being new to WPF?

Thank you.

XAML editor indicates DataContext does not support view model's type

Description

Using a view-model that inherits ViewModelBase and setting the data context to that type, Visual Studio reports Property 'DataContext' does not support values of type 'MainViewModel'.

Repro steps

Please provide the steps required to reproduce the problem

  1. Point a view to a view-model that inherits from ViewModelBase via <Window.DataContext><local:MainViewModel /></Window.DataContext>
  2. Rebuild solution
  3. Close/reopen Visual Studio, or kill XDesProc.exe via Task Manager to get the WPF design view to load the latest binary

Expected behavior

Everything should load normall

Actual behavior

The Error List in Visual Studio shows the error mentioned above, and provides a colored squiggly underneath <local:MainViewModel />

Known workarounds

  • Not inheriting from ViewModelBase and then doing the rebuild & close/open dance with Visual Studio makes the error go away, but makes the view-model much less useful
  • Ignoring the error works fine, as the application still builds and runs fine

Related information

  • Running on Windows 10 Pro v 1511 with Visual Studio 2015 w/ Update 2
  • Using NuGet package version 0.9.9.3
  • Targeting .NET 4.5 and F# 4.4.0.0, local system has .NET 4.6.1

Unable to load the FSharp.ViewModule project in Visual Studio 2015 update 1

The FSharp.ViewModule project fsproj file fails to load with an error at Line 62:

Error The imported project "C:\Program Files (x86)\Microsoft SDKs\F#\3.1\Framework\v4.0\Microsoft.Portable.FSharp.Targets" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk. FSharp.ViewModule.Wpf C:...\FSharp.ViewModule-master\src\FSharp.ViewModule\FSharp.ViewModule.fsproj 62

After commenting out this line, I was still unable to get the project to load.

Should be able to execute a CommandAsyncChecked-generated command multiple times

Description

When I create a WPF Command with Factory.CommandAsyncChecked(asyncWorkflow, canExecute), and bind that command to a button, and click the button, the button becomes disabled and does not become re-enabled until the asyncWorkflow completes. It does this even if canExecute always returns true.

Repro steps

  1. Create a new F# project
  2. Reference FSharp.ViewModule.Core, 0.9.9.2 and FsXaml.Wpf, 0.9.9.
  3. Use the code below.
  4. Run the program. Press the button.
  5. Observe that the button becomes disabled for three seconds and then becomes re-enabled.

MainWindow.xaml

<Window xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Button Width="200" Height="100"
            Command="{Binding DoSomethingAsync}"
            Content="DoSomethingAsync" />
</Window>

Program.cs

open System
open System.Windows
open FSharp.ViewModule

type MainWindow = FsXaml.XAML< "MainWindow.xaml", true >

type MainWindowViewModel() as this = 
    inherit ViewModelBase()
    let asyncWorkflow _ =
        async {
            do! Async.Sleep(3000)
            System.Diagnostics.Debug.WriteLine("asyncWorkflow completed.")
        }
    let canExecute _ = true
    member val DoSomethingAsync =
        this.Factory.CommandAsyncChecked(asyncWorkflow, canExecute)

[<STAThread; EntryPoint>]
let main argv = 
    if SynchronizationContext.Current = null then
        DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher)
        |> SynchronizationContext.SetSynchronizationContext
    let win = MainWindow()
    win.Root.DataContext <- MainWindowViewModel()
    let app = Application()
    app.Run(win.Root)

Expected behavior

I expected that the button would never become disabled.

Actual behavior

The button becomes disabled for three seconds.

Known workarounds

None.

Related information

  • Windows 10
  • Visual Studio 2015
  • .NET Framework 4.5.2, FSharp.Core 4.4.0.0

Constructor fails inside an array under xUnit

type AccidentDataModel (accidentId,displayText,code) as self =
    inherit FSharp.ViewModule.ViewModelBase()

    let accidentId      = self.Factory.Backing  (<@ self.AccidentID @>,         accidentId)
    let displayText     = self.Factory.Backing  (<@ self.DisplayText @>,        displayText)
    let code            = self.Factory.Backing  (<@ self.Code @>,               code)

    new () = AccidentDataModel(0,String.Empty,String.Empty)

    member x.AccidentID with get() = accidentId.Value and set v = accidentId.Value <- v
    member x.DisplayText with get() = displayText.Value and set v = displayText.Value <- v
    member x.Code with get() = code.Value and set v = code.Value <- v

Succeeding test

open global.Xunit
open FsCheck
open FsCheck.Xunit
            [<Property>]
            let createAccidentModelTest x y z = 
                let model = Pm.Dal.DataModels.AccidentDataModel(x,y,z)
                Assert.NotNull model 
                Assert.Equal(x,model.AccidentID)
                Assert.Equal(y, model.DisplayText)
                Assert.Equal(z, model.Code)

Failing test for both cases 1, and 100

            [<Theory>]
            [<InlineData(1)>]
            [<InlineData(100)>]
            let createAccidentList length = 
                //generate a single instance of 't
                let generateOne() = FsCheck.Arb.generate<'a>.Sample(1,1) |> Seq.head
                let generateAdm () = Pm.Dal.DataModels.AccidentDataModel(generateOne() , generateOne(), generateOne())
                let items = 
                    [0..length-1]
                    |> Seq.map (ignore >> generateAdm)
                    |> Array.ofSeq
                Assert.NotNull items
                Assert.Equal(items.Length,length)

exception

Test Name: Pm.Tests.IntegrationTests.IntegrationTestModule+DataAccess+Accidents.createAccidentList(length: 100)
Test FullName: Pm.Tests.IntegrationTests.IntegrationTestModule+DataAccess+Accidents.createAccidentList
Test Source: C:\TFS\PracticeManagement\dev\PracticeManagement\Pm.Tests\Integration.XUnit.fs : line 263
Test Outcome: Failed
Test Duration: 0:00:00.229

Result StackTrace:
at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.FailInit()
at FSharp.ViewModule.Internal.ViewModelUntyped.Validate(String propertyName)
at FSharp.ViewModule.ValidationTracker.validatePropertiesInternal(String propertyName)
Result Message: System.InvalidOperationException : The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized.

RaiseCanExecuteChanged does not fire?

Description

I have two commands. When the first command executes, I want it to explicitly force the second command's CanExecuteChanged to be re-checked by WPF. Inside the first command, I call RaiseCanExecuteChanged() on the second command, but the second command's CanExecuteChanged predicate does not get called.

What am I doing wrong?

Auto-generated UI from F# types

I don't know if this is the right place for it, but there's something I thought about doing but I won't have time for it anytime soon, so I though maybe you would like to try it. The idea would be to take something like this:

type DU = A | B | C of string | D of bool

type Record = { Prop1 : string
                Prop2 : bool
                Prop3: int
                Prop4: DU }

let myFunc (input:Record) -> ..

And have a function like this:

let generateUI (f:'Input->'Output) : Window =

That would use reflection on typeof<'Input>, generate a set of UI controls (TextBox for string, CheckBox for bool, Slider for int, GroupBox with RadioButtons for DU cases, etc...), and wire change notifications on those controls to trigger calling f, and show the results somewhere in the window in a ContentPresenter. This would work both for mathematical functions, for functions returning strings, or event for functions returning a chart from FSharp.Charting. What do you think?

We could start by doing this for WPF, and if it worked nicely we could make it work for other platforms (including web)

AppVeyor build is failing

The AppVeyor buildd is failing, for FSharp.ViewModule.Core.fsproj, looks like a missing DLL reference

https://ci.appveyor.com/project/fsgit/fsharp-viewmodule

It could be that profile 259 is not yet supported in the CI build system?

  • C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Common.CurrentVersion.targets(1696,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly "FSharp.Core". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. [C:\projects\fsharp-viewmodule\src\FSharp.ViewModule.Core\FSharp.ViewModule.Core.fsproj]

and

  • C:\projects\fsharp-viewmodule\src\FSharp.ViewModule.Core\unknown(1,1): error FS0078: Unable to find the file 'System.Runtime.InteropServices.dll' in any of� C:\Program Files (x86)\Reference

Execption raises when asynchronous call to a dependent propertie

Description

In my view model I have a dependent property like that with a associated command:

type MyViewModel () as this =
    inherit ViewModelBase ()
    let saveCurrent () = ...
    let dirty = this.Factory.Backing(<@ this.Dirty @>, false)
    let saveCommand =
        this.Factory.CommandSyncChecked(
            saveCurrent,
            (fun () -> this.Dirty),
            [ <@ this.Dirty @> ]
        )
    // ...
    member private this.Dirty
        with get () = dirty.Value
        and set value = dirty.Value <- value
    member this.SaveCommand = saveCommand

If this.Dirty is modified from another thread than the UI thread then an exception is raised :

Unhandled error in web service loop: System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
   at System.Windows.Threading.Dispatcher.VerifyAccess()
   at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
   at System.Windows.Controls.Primitives.ButtonBase.get_Command()
   at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
   at <StartupCode$FSharp-ViewModule>[email protected](INotifyCommand cmd, Unit unitVar0)
   at Microsoft.FSharp.Primitives.Basics.List.iter[T](FSharpFunc`2 f, FSharpList`1 x)
   at ViewModule.DependencyTracker.handleTrackingActions(PropertyChangedEventArgs args)
   at <StartupCode$FSharp-ViewModule>[email protected](PropertyChangedEventArgs args)
   at System.ComponentModel.PropertyChangedEventHandler.Invoke(Object sender, PropertyChangedEventArgs e)
   at <StartupCode$FSharp-ViewModule>[email protected](String arg00)
   at MyViewModel.FileDetailsPartViewModel.SetCurrentFileIds(FSharpList`1 ids) in Part\FileDetailsPartView.fs:line 102
   at System.Reactive.AnonymousSafeObserver`1.OnNext(T value)
   at System.Reactive.Observer`1.OnNext(T value)
   ...

Repro steps

Call this.Dirty from the sample above from another thread than the UI thread. For example the exception stack trace shows a call from an IObservable.

Expected behavior

ViewModel should be independent from the UI thread and therefore not crash should happen.

Actual behavior

It crashes.

Known workarounds

With a dirty code like that it doesn't crash :

    member private this.Dirty
        with get () = dirty.Value
        and set value =
            Application.Current.Dispatcher.Invoke(
                fun () -> dirty.Value <- value
            )

Related information

  • Operating system: Window 7
  • .NET Runtime: 4.6.1 (F# 4.0)
  • FSharp.ViewModule.Core version (from NuGet): v1.0.5

Property set does unnecessary construction

The property setter generated does an extra construction. It would be nice if we could rework it to use a let binding to store the new record once.

Relevent code at: https://github.com/ReedCopsey/FSharp.ViewModule/blob/587115709866bd969a9808cb48ad6726bb950c65/src/FSharp.ViewModule.TypeProvider/Internal.fs#L187-L202

In its current form, the generated property has the form of:

public string Firstname
{
    get
    {
        ViewModels.Home home = this;
        return home.state.Firstname;
    }
    set
    {
        ViewModels.Home home = this;
        string firstname = value;
        if (false == EqualityComparer<Home>.Default.Equals(home.state, new Home(firstname, home.state.Lastname, home.state.ClickCount)))
        {
            home.state = new Home(firstname, home.state.Lastname, home.state.ClickCount);
            home.RaisePropertyChanged("Firstname");
        }
    }
}

This is nearly ideal, except that it would be nice to store the new home value, ie:

public string Firstname
{
    get
    {
        ViewModels.Home home = this;
        return home.state.Firstname;
    }
    set
    {
        ViewModels.Home home = this;
        string firstname = value;
        ViewModels.Home newHome = new Home(firstname, home.state.Lastname, home.state.ClickCount);
        if (false == EqualityComparer<Home>.Default.Equals(home.state, newHome))
        {
            home.state = newHome;
            home.RaisePropertyChanged("Firstname");
        }
    }
}

I'm not sure how to get the let binding required for this to work properly, however.

Factory.EventValueCommand does not handle null properly..(?)

I'm trying to use EventValueCommand method of EventViewModelBase to create a command for a button. My code look like this:

type MainWindowEvents = Invalid

type MainWindowModel() as me =
    inherit EventViewModelBase<MainWindowEvents>()

    let navCommand = me.Factory.EventValueCommand(fun x -> System.Diagnostics.Debug.Print (x.ToString()); Invalid)

    do me.EventStream |> Observable.subscribe (kprintf Debug.Print "get %A") |> ignore

    member x.NavCommand = navCommand

and in my XAML, I have a button that fires that command.

     <Button Command="{Binding NavCommand}" CommandParameter="hello guys!">About sample</Button>

When the button clicks, it does not work. I've debug and found that navCommand.CanExecute return false and the root cause is null value is passed into the handle function.

I'm not sure if I use the function correctly as it is by designed, or is this supposed to be a defect?

Backing fields throwing an exception for duplicated keys

Description

In case of error of duplicated keys - notice that it is only runtime - the whole viewmodule is thrown out.

Repro steps

Just insert two Backing fields with the same key.

Expected behavior

Only the duplicated bindings and related properties should be affected and stop working, not the whole ViewModule. It would be maybe a nicer functional approach to get an option instead of just a NotifyingValue from the member x.Backing?

Actual behavior

The duplicated key is added to the collection without checks, thus compromising the caller execution, in my case (which appears to be the typical usage) it happens in the OnLoad event, where the ViewModel is assigned to the DataContext.

Known workarounds

It is even difficult to spot the error, if one doesn't wrap the DataContext assignment with a try statement: it can be seen as a workaround to (at least) retrieve the basic info about the issue, so to easily fix the root problem in the code.

Related information

Looks like this is a single line fix and it all boils down to this unchecked, unsafe value.Add in the NotifyingValueBackingField c.tor

OperationExecuting probably not working correctly.

I think that OperationExecuting does not work correctly.
Example: a simple countdown timer.
.xaml:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="start async worker" Command="{Binding StartAsyncCommand}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" />
        <TextBlock Grid.Row="0" Text="{Binding Count}" Margin="5" HorizontalAlignment="Right"  VerticalAlignment="Top"></TextBlock>
        <Button  Grid.Row="0" Content="OperationExecuting?" Command="{Binding ExecuteCommand}"  HorizontalAlignment="Center" VerticalAlignment="Top" Margin="5" />
        <ProgressBar Grid.Row="1" IsIndeterminate="{Binding OperationExecuting}" Margin="5" Height="30"></ProgressBar>
    </Grid>

.fs:

type MainViewModel() as me = 
    inherit ViewModelBase() 

    let startInt = 20
    let step = 1000.0

    let count = me.Factory.Backing(<@ me.Count @>, startInt)

    let start _ = 
        let timer = new System.Timers.Timer(step)
        (count.Value, timer.Elapsed)    
        ||> Observable.scan(fun x _ -> x - 1)     
        |> Observable.subscribe
            (fun x -> if x >= 0 then count.Value <- x
                      else count.Value <- startInt; timer.Stop() 
                      me.RaisePropertyChanged(<@ me.Count @>))
        |> ignore
        async { timer.Start() } //|> Async.Start

    let executeCommand = me.Factory.CommandSync(fun () -> MessageBox.Show(me.OperationExecuting |> string) |> ignore)

    member __.ExecuteCommand = executeCommand
    member __.StartAsyncCommand = me.Factory.CommandAsync(start)
    member __.Count with get() = count.Value and set v = count.Value <- v

Screenshot:

0

Regardless of whether the asynchronous operation is performed Operation Execution is always false.

FsEmptyWindowsApp3.zip

Where is this.Factory.SetPropertyDependencies?

Description

I am learning to use FSharp.ViewModule package from the Youtube video: https://www.youtube.com/watch?v=z6R85_X2ivE

At the screenshot of 16:16, you are using self.Factory.SetPropertyDependencies() to manage the case a property depends on two fileds.

However, I don't find such API from this package. Do I miss somthing?

Related information

  • Operating system: Windows 10
  • Branch: From NuGet

EventViewModelBase Backing Field causes XamlObjectWriterException

Description

After updating from FSharp.ViewModule v0.9.9.3 to v1.0.4.0, using a backing field in EventViewModelBase<'a> causes a XamlObjectWriterException.

Repro steps

  1. Step A

    Dummy project runs under FSharp.ViewModule 0.9.9.3.

  2. Step B

    Updating to FSharp.ViewModule 1.0.4.0 and making necessary changes:

In MainViewModel.fs

Changing

open FSharp.ViewModule

To

open ViewModule
open ViewModule.FSharp

Related information

After creating a wrapper for the vm, I get the following output:

Exception thrown: 'System.InvalidCastException' in FSharp.Core.dll
Exception thrown: 'System.Reflection.TargetInvocationException' in mscorlib.dll
Exception thrown: 'System.Reflection.TargetInvocationException' in System.dll
System.Windows.Data Error: 17 : Cannot get 'ViewModel' value (type 'MainViewModel') from '' (type 'Wrapper'). BindingExpression:Path=ViewModel; DataItem='Wrapper' (HashCode=64844482); target element is 'Window' (Name=''); target property is 'DataContext' (type 'Object') TargetInvocationException:'System.Reflection.TargetInvocationException: Property accessor 'ViewModel' on object 'View.Wrapper' threw the following exception:'Unable to cast object of type 'ViewModel.MainViewModel' to type 'ViewModule.ViewModelPropertyFactory'.' ---> System.InvalidCastException: Unable to cast object of type 'ViewModel.MainViewModel' to type 'ViewModule.ViewModelPropertyFactory'.
at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
at ViewModule.FSharp.EventExtensions.IViewModelPropertyFactory.Backing[a](IViewModelPropertyFactory x, FSharpExpr prop, a defaultValue, FSharpOption`1 validate)
at ViewModel.MainViewModel..ctor() in C:\data\F#\Code\Cookie\CookieFactory\CookieFactory\MainViewModel.fs:line 10
at View.Wrapper.get_ViewModel() in C:\data\F#\Code\Cookie\CookieFactory\CookieFactory\MainWindow.xaml.fs:line 8
--- End of inner exception stack trace ---
at System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)
at MS.Internal.Data.ValueTable.GetValue(Object item, PropertyDescriptor pd, Boolean indexerIsNext)
at MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'

CommandAsyncChecked does not respect CanExecute's return value

Description

When I create a WPF Command with Factory.CommandAsyncChecked(asyncWorkflow, canExecute), and bind that command to a button, and click the button, the button becomes disabled and does not become re-enabled. It does this even if canExecute always returns true.

Repro steps

  1. Create a new F# project
  2. Reference FSharp.ViewModule.Core, 0.9.9.2 and FsXaml.Wpf, 0.9.9.
  3. Use the code below.
  4. Run the program. Press the button.
  5. Observe that the button becomes disabled and never re-enabled.

MainWindow.xaml

<Window xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Button Width="200" Height="100"
            Command="{Binding DoSomethingAsync}"
            Content="DoSomethingAsync" />
</Window>

Program.cs

open System
open System.Windows
open FSharp.ViewModule

type MainWindow = FsXaml.XAML< "MainWindow.xaml", true >

type MainWindowViewModel() as this = 
    inherit ViewModelBase()
    let asyncWorkflow _ =
        async {
            do! Async.Sleep(3000)
            System.Diagnostics.Debug.WriteLine("asyncWorkflow completed.")
        }
    let canExecute _ = true
    member val DoSomethingAsync =
        this.Factory.CommandAsyncChecked(asyncWorkflow, canExecute)

[<STAThread; EntryPoint>]
let main argv = 
    let win = MainWindow()
    win.Root.DataContext <- MainWindowViewModel()
    let app = Application()
    app.Run(win.Root)

Expected behavior

I expected that the button would never become disabled.

Actual behavior

The button becomes disabled and does not become re-enabled.

Known workarounds

I found that the button can become re-enabled with this code:

type MainWindowViewModel() as this = 
    inherit ViewModelBase()
    let asyncWorkflow _ =
        async {
            try
                this.OperationExecuting <- true
                do! Async.Sleep(3000)
                System.Diagnostics.Debug.WriteLine("asyncWorkflow completed.")
            finally
                this.OperationExecuting <- false
        }
    let canExecute _ = true
    member val DoSomethingAsync =
        this.Factory.CommandAsyncChecked(asyncWorkflow, canExecute, [ <@ this.OperationExecuting @> ])

I'm not sure if that is the intended use of OperationExecuting. It seems that the library could set and unset OperationExecuting, but it apparently does not.

Regardless, canExecute always returns true, so I expected the command never to become disabled.

Related information

  • Windows 10
  • Visual Studio 2015
  • .NET Framework 4.5.2, FSharp.Core 4.4.0.0

CollectionChanged not triggering the PropertyDependencies of a Factory.CommandSyncChecked

Description

I'm trying to detect when an ObservableCollection is not empty and enable a clear command only in that case

Repro steps

I've tried the following

    let clearRecentsCmd = 
        self.Factory.CommandSyncChecked(
            (fun () -> self.ClearRecents()), 
            (fun () -> self.RecentsNotEmpty  ), 
            [ <@@ self.RecentsNotEmpty @@>;  <@@ self.Recents @@>])
    let recents = self.Factory.Backing(<@ self.Recents @>, ObservableCollection<RecentFile>(getRecent())) 
    do
        self.DependencyTracker.AddPropertyDependencies(<@@ self.RecentsNotEmpty @@>, [ <@@ self.Recents @@> ; ])
    member x.Recents with get() = recents.Value and set value = recents.Value <- value
    member x.Add2Recents r = 
        x.Recents.Insert(0,r)
    member x.ClearRecents() =
        x.Recents.Clear()
    member x.RecentsNotEmpty with get() = x.Recents.Count > 0

Expected behavior

x.Recents.Insert(0,r) should raise a Command CanExecuteChanged

Actual behavior

The command doesn't get enabled if I add a new RecentFile to the empty ObservableCollection.

Known workarounds

I'm forced to use the clumsy x.Recents <- ObservableCollection(r :: (x.Recents |> Seq.toList)) instead of x.Recents.Insert(0,r)

Related information

I'm using .Net Framework 4.6.1 and F# 4.4.3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.