Library providing MVVM and INotifyPropertyChanged support for F# projects
The default maintainer account for projects under "fsprojects" is @fsprojectsgit - F# Community Project Incubation Space (repo management)
Library providing MVVM and INotifyPropertyChanged support for F# projects
License: Apache License 2.0
Library providing MVVM and INotifyPropertyChanged support for F# projects
The default maintainer account for projects under "fsprojects" is @fsprojectsgit - F# Community Project Incubation Space (repo management)
I want to initialize multiple FSharp.Charting charts on window "Loaded" event via binding theirs WindowsFormsHost
s 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.
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'.
Please provide the steps required to reproduce the problem
ViewModelBase
via <Window.DataContext><local:MainViewModel /></Window.DataContext>
XDesProc.exe
via Task Manager to get the WPF design view to load the latest binaryEverything should load normall
The Error List in Visual Studio shows the error mentioned above, and provides a colored squiggly underneath <local:MainViewModel />
ViewModelBase
and then doing the rebuild & close/open dance with Visual Studio makes the error go away, but makes the view-model much less useful0.9.9.3
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.
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.
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)
I expected that the button would never become disabled.
The button becomes disabled for three seconds.
None.
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.229Result 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.
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?
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)
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?
and
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)
...
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.
ViewModel should be independent from the UI thread and therefore not crash should happen.
It crashes.
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
)
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.
Are there any samples of how to use this?
API docs would be helpful too.
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?
In case of error of duplicated keys - notice that it is only runtime - the whole viewmodule is thrown out.
Just insert two Backing fields with the same key.
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
?
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
.
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.
Looks like this is a single line fix and it all boils down to this unchecked, unsafe value.Add in the NotifyingValueBackingField
c.tor
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:
Regardless of whether the asynchronous operation is performed Operation Execution is always false.
so for those of us relying on the type provider, what is the proposed plan? It took me awhile to figure out that the simple version update was a major breaking change, in assembly name, and in what's inside the dll.
The relevant code for this is here:
https://github.com/fsprojects/FSharp.ViewModule/blob/master/src/FSharp.ViewModule/MVVM.fs#L247
and:
https://github.com/fsprojects/FSharp.ViewModule/blob/master/src/FSharp.ViewModule/FunCommand.fs#L43
Automatically adding OperationExecuting as a dependency would make the FunCommand work propertly. See #17 for inspiration.
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?
After updating from FSharp.ViewModule v0.9.9.3 to v1.0.4.0, using a backing field in EventViewModelBase<'a> causes a XamlObjectWriterException.
Step A
Dummy project runs under FSharp.ViewModule 0.9.9.3.
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
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)'
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.
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)
I expected that the button would never become disabled.
The button becomes disabled and does not become re-enabled.
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.
I'm trying to detect when an ObservableCollection is not empty and enable a clear command only in that case
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
x.Recents.Insert(0,r)
should raise a Command
CanExecuteChanged
The command doesn't get enabled if I add a new RecentFile
to the empty ObservableCollection
.
I'm forced to use the clumsy x.Recents <- ObservableCollection(r :: (x.Recents |> Seq.toList))
instead of x.Recents.Insert(0,r)
I'm using .Net Framework 4.6.1 and F# 4.4.3
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.