From f63274104e63515554bd3240a08a604e75185ed9 Mon Sep 17 00:00:00 2001 From: YkTru Date: Thu, 19 Sep 2024 14:21:01 -0400 Subject: [PATCH 1/7] changed/extended SubModelSelectedItem sample --- .../SubModelSelectedItem.Core/FsWPF.fs | 8 + .../SubModelSelectedItem.Core/Program.fs | 355 ++++++++++++++++-- .../SubModelSelectedItem.Core.fsproj | 11 +- src/Samples/SubModelSelectedItem/App.xaml.cs | 22 +- .../SubModelSelectedItem/MainWindow.xaml | 120 ++++-- 5 files changed, 429 insertions(+), 87 deletions(-) create mode 100644 src/Samples/SubModelSelectedItem.Core/FsWPF.fs diff --git a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs new file mode 100644 index 00000000..50cfb3db --- /dev/null +++ b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs @@ -0,0 +1,8 @@ +namespace FsWPF + +open System +open System.Windows.Data +open Elmish.WPF.Samples.SubModelSelectedItem.Program +open Form + +//[toDo] DataTemplateSelector diff --git a/src/Samples/SubModelSelectedItem.Core/Program.fs b/src/Samples/SubModelSelectedItem.Core/Program.fs index c8f970af..b68909b3 100644 --- a/src/Samples/SubModelSelectedItem.Core/Program.fs +++ b/src/Samples/SubModelSelectedItem.Core/Program.fs @@ -1,57 +1,332 @@ -module Elmish.WPF.Samples.SubModelSelectedItem.Program +namespace Elmish.WPF.Samples.SubModelSelectedItem.Program open System open Serilog open Serilog.Extensions.Logging open Elmish.WPF -type Entity = - { Id: int - Name: string } +(* +[toDos] + • *change all [dynamic bindings] to [static bindings] using an upcoming Elmish.WPF revised static bindings approach* + • [?] make "_VM" for each child (cleaner separation) + • [?] would something other than "SubModelSelectItem" be a better option for safety? + • [?] how to better seperate *specific children fields* within dynamic bindings in "Form_VM.Components"? Just comment? Helpers? -type Model = - { Entities: Entity list - Selected: int option } + • refactor: make FormComponent more concrete = TextBox, CheckBox, ComboBox + • add: DataTemplateSelector + • add: get focus after adding + selecting FormComponent (Behavior) -let init () = - { Entities = [0 .. 10] |> List.map (fun i -> { Id = i; Name = sprintf "Entity %i" i}) - Selected = Some 4 } + • refactor: revise all helpers in Form (some were made quick&dirty) + • refactor: make update and VM cleaner (helpers) + • revise naming(?): keep "_Model", "_Msg", "_VM"? IMO it helps seperate better childs visually + better Intellisense experience in Xaml +*) -type Msg = - | Select of int option -let update msg m = - match msg with - | Select entityId -> { m with Selected = entityId } +module FormComponentHelpers = + let generateName (prefix: string) = + let randomNumber () = Random().Next(1000, 10000).ToString() + prefix + randomNumber () -let bindings () : Binding list = [ - "SelectRandom" |> Binding.cmd - (fun m -> m.Entities.Item(Random().Next(m.Entities.Length)).Id |> Some |> Select) - "Deselect" |> Binding.cmd(Select None) +module FormComponentA = - "Entities" |> Binding.subModelSeq( - (fun m -> m.Entities), - (fun e -> e.Id), - (fun () -> [ - "Name" |> Binding.oneWay (fun (_, e) -> e.Name) - "SelectedLabel" |> Binding.oneWay (fun (m, e) -> if m.Selected = Some e.Id then " - SELECTED" else "") - ])) + type Model = { Id: Guid; Name: string } - "SelectedEntity" |> Binding.subModelSelectedItem("Entities", (fun m -> m.Selected), Select) -] + let create () = + { Id = Guid.NewGuid() + Name = FormComponentHelpers.generateName "A_" } -let designVm = ViewModel.designInstance (init ()) (bindings ()) + let init () = create () -let main window = - let logger = - LoggerConfiguration() - .MinimumLevel.Override("Elmish.WPF.Update", Events.LogEventLevel.Verbose) - .MinimumLevel.Override("Elmish.WPF.Bindings", Events.LogEventLevel.Verbose) - .MinimumLevel.Override("Elmish.WPF.Performance", Events.LogEventLevel.Verbose) - .WriteTo.Console() - .CreateLogger() + type Msg = DummyMsg - WpfProgram.mkSimple init update bindings - |> WpfProgram.withLogger (new SerilogLoggerFactory(logger)) - |> WpfProgram.startElmishLoop window + let update msg m = + match msg with + | DummyMsg -> m + + +module FormComponentB = + + type Model = { Id: Guid; Name: string } + + let create () = + { Id = Guid.NewGuid() + Name = FormComponentHelpers.generateName "B_" } + + let init () = + { Id = Guid.NewGuid() + Name = "B_" + Random().Next(10000, 100000).ToString() } + + type Msg = DummyMsg + + let update msg m = + match msg with + | DummyMsg -> m + + +module FormComponentC = + + type Model = { Id: Guid; Name: string } + + let create () = + { Id = Guid.NewGuid() + Name = FormComponentHelpers.generateName "C_" } + + let init () = create () + + type Msg = DummyMsg + + let update msg m = + match msg with + | DummyMsg -> m + + +module Form = + + type Components = + | FormComponentA of FormComponentA.Model + | FormComponentB of FormComponentB.Model + | FormComponentC of FormComponentC.Model + + + type Model = + { Components: Components list + SelectedComponent: Guid option + //• SubModels + FormComponentA_Model: FormComponentA.Model + FormComponentB_Model: FormComponentB.Model + FormComponentC_Model: FormComponentC.Model } + + + let components_Mock = + [ for _ in 1..3 do + yield FormComponentA(FormComponentA.create ()) + yield FormComponentB(FormComponentB.create ()) + yield FormComponentC(FormComponentC.create ()) ] + + + let init () = + { Components = components_Mock + SelectedComponent = None + //• SubModels + FormComponentA_Model = FormComponentA.init () + FormComponentB_Model = FormComponentB.init () + FormComponentC_Model = FormComponentC.init () } + + type Msg = + | Select of Guid option + | AddFormComponentA + | AddFormComponentB + | AddFormComponentC + //• SubMsgs + | TextBoxA_Msg of FormComponentA.Msg + | TextBoxB_Msg of FormComponentB.Msg + | TextBoxC_Msg of FormComponentC.Msg + + [] + module Form = + + let getSelectedEntityIdFromSelectComponent (m: Model) = + match m.SelectedComponent with + | Some selectedId -> selectedId + | None -> Guid.Empty + + let getComponentId component_ = + match component_ with + | FormComponentA a -> a.Id + | FormComponentB b -> b.Id + | FormComponentC c -> c.Id + + let getComponentName component_ = + match component_ with + | FormComponentA a -> a.Name + | FormComponentB b -> b.Name + | FormComponentC c -> c.Name + + let isSelected selectedId component_ = + match selectedId, component_ with + | Some id, FormComponentA a -> a.Id = id + | Some id, FormComponentB b -> b.Id = id + | Some id, FormComponentC c -> c.Id = id + | _ -> false + + let insertComponentAfterSelected selectedComponent newComponent components = + + // sample purpose: make explicit that a new component has been added + let prependNewName component_ = + match component_ with + | FormComponentA a -> FormComponentA { a with Name = "#New# " + a.Name } + | FormComponentB b -> FormComponentB { b with Name = "#New# " + b.Name } + | FormComponentC c -> FormComponentC { c with Name = "#New# " + c.Name } + + let newComponentWithPrependedName = prependNewName newComponent + + match selectedComponent with + | None -> + // If no component is selected, append the new one to the end + components @ [ newComponentWithPrependedName ] + | Some selectedId -> + let rec insertAfterSelected = + function + | [] -> [ newComponentWithPrependedName ] + | comp :: rest when getComponentId comp = selectedId -> comp :: newComponentWithPrependedName :: rest + | comp :: rest -> comp :: insertAfterSelected rest + + insertAfterSelected components + + + let update msg m = + match msg with + | Select entityId -> { m with SelectedComponent = entityId } + + | AddFormComponentA -> + let newComponent = FormComponentA(FormComponentA.create ()) + + let newComponentId = + match newComponent with + | FormComponentA a -> a.Id + | _ -> Guid.Empty + + { m with + Components = insertComponentAfterSelected m.SelectedComponent newComponent m.Components + SelectedComponent = Some newComponentId } + + | AddFormComponentB -> + let newComponent = FormComponentB(FormComponentB.create ()) + + let newComponentId = + match newComponent with + | FormComponentB b -> b.Id + | _ -> Guid.Empty + + { m with + Components = insertComponentAfterSelected m.SelectedComponent newComponent m.Components + SelectedComponent = Some newComponentId } + + | AddFormComponentC -> + let newComponent = FormComponentC(FormComponentC.create ()) + + let newComponentId = + match newComponent with + | FormComponentC c -> c.Id + | _ -> Guid.Empty + + { m with + Components = insertComponentAfterSelected m.SelectedComponent newComponent m.Components + SelectedComponent = Some newComponentId } + + //• SubModels + | TextBoxA_Msg msg -> { m with FormComponentA_Model = FormComponentA.update msg m.FormComponentA_Model } + | TextBoxB_Msg msg -> { m with FormComponentB_Model = FormComponentB.update msg m.FormComponentB_Model } + | TextBoxC_Msg msg -> { m with FormComponentC_Model = FormComponentC.update msg m.FormComponentC_Model } + + +open Form.Form // ugly + +[] +type Form_VM(args) = + inherit ViewModelBase(args) + + new() = Form_VM(Form.init () |> ViewModelArgs.simple) + + //• Properties + // I *really* don't like the stringly-typed nature of this binding + no Intellisense in Xaml for submodel properties + member _.Components = + base.Get + () + (Binding.subModelSeq ( + (fun m -> m.Components), + (fun (e) -> getComponentId e), + (fun () -> + [ "Name" + |> Binding.oneWay (fun (_, e) -> getComponentName e) + "SelectedLabel" + |> Binding.oneWay (fun (m, e) -> + if isSelected m.SelectedComponent e then + " - Selected" + else + "") ]) + )) + + // I don't like the stringly-typed nature of this binding + member _.SelectedEntity + with get () = + base.Get + () + (Binding.subModelSelectedItem ( + "Components", + (fun (m: Form.Model) -> m.SelectedComponent), + Form.Msg.Select + )) + and set (value) = + base.Set + value + (Binding.subModelSelectedItem ( + "Components", + (fun (m: Form.Model) -> m.SelectedComponent), + Form.Msg.Select + )) + + member _.SelectedEntityLog + with get () = + base.Get + () + (Binding.oneWay (fun (m: Form.Model) -> + match m.SelectedComponent with + | Some id -> + let index = + m.Components + |> List.findIndex (fun e -> getComponentId e = id) + + let name = + m.Components + |> List.find (fun e -> getComponentId e = id) + |> getComponentName + + let componentType = + match m.Components + |> List.find (fun e -> getComponentId e = id) + with + | Form.Components.FormComponentA _ -> "Type: A" + | Form.Components.FormComponentB _ -> "Type: B" + | Form.Components.FormComponentC _ -> "Type: C" + + sprintf "Selected: Name = %s, Index = %d, %s" name index componentType + | None -> "No selection")) + and set (value) = base.Set value (Binding.oneWay (fun _ -> "")) + + + //• Commands + member _.AddTextBoxA = base.Get () (Binding.CmdT.setAlways Form.AddFormComponentA) + member _.AddTextBoxB = base.Get () (Binding.CmdT.setAlways Form.AddFormComponentB) + member _.AddTextBoxC = base.Get () (Binding.CmdT.setAlways Form.AddFormComponentC) + + member _.SelectRandom = + base.Get + () + (Binding.cmd (fun (m: Form.Model) -> + let randomEntity = m.Components.Item(Random().Next(m.Components.Length)) + + match randomEntity with + | Form.Components.FormComponentA aModel -> Some aModel.Id + | Form.Components.FormComponentB bModel -> Some bModel.Id + | Form.Components.FormComponentC cModel -> Some cModel.Id + |> Form.Msg.Select)) + + member _.Deselect = + base.Get () (Binding.cmd (fun (m: Form.Model) -> Form.Msg.Select None)) + + +module Program = + let main window = + let logger = + LoggerConfiguration() + .MinimumLevel.Override("Elmish.WPF.Update", Events.LogEventLevel.Verbose) + .MinimumLevel.Override("Elmish.WPF.Bindings", Events.LogEventLevel.Verbose) + .MinimumLevel.Override("Elmish.WPF.Performance", Events.LogEventLevel.Verbose) + .WriteTo.Console() + .CreateLogger() + + WpfProgram.mkSimpleT Form.init Form.update Form_VM + |> WpfProgram.withLogger (new SerilogLoggerFactory(logger)) + |> WpfProgram.startElmishLoop window diff --git a/src/Samples/SubModelSelectedItem.Core/SubModelSelectedItem.Core.fsproj b/src/Samples/SubModelSelectedItem.Core/SubModelSelectedItem.Core.fsproj index 28983b0f..99dee426 100644 --- a/src/Samples/SubModelSelectedItem.Core/SubModelSelectedItem.Core.fsproj +++ b/src/Samples/SubModelSelectedItem.Core/SubModelSelectedItem.Core.fsproj @@ -1,25 +1,20 @@ - - + true - net8.0-windows - + - - - - + \ No newline at end of file diff --git a/src/Samples/SubModelSelectedItem/App.xaml.cs b/src/Samples/SubModelSelectedItem/App.xaml.cs index 15195c44..b02a9c86 100644 --- a/src/Samples/SubModelSelectedItem/App.xaml.cs +++ b/src/Samples/SubModelSelectedItem/App.xaml.cs @@ -3,18 +3,18 @@ namespace Elmish.WPF.Samples.SubModelSelectedItem { - public partial class App : Application - { - public App() + public partial class App : Application { - this.Activated += StartElmish; - } + public App() + { + this.Activated += StartElmish; + } - private void StartElmish(object sender, EventArgs e) - { - this.Activated -= StartElmish; - Program.main(MainWindow); - } + private void StartElmish(object sender, EventArgs e) + { + this.Activated -= StartElmish; + Program.Program.main(MainWindow); + } - } + } } diff --git a/src/Samples/SubModelSelectedItem/MainWindow.xaml b/src/Samples/SubModelSelectedItem/MainWindow.xaml index a2702e7c..63861b95 100644 --- a/src/Samples/SubModelSelectedItem/MainWindow.xaml +++ b/src/Samples/SubModelSelectedItem/MainWindow.xaml @@ -1,29 +1,93 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + \ No newline at end of file From be55d068109c79be514183b22df1c53f76f96d92 Mon Sep 17 00:00:00 2001 From: YkTru Date: Fri, 20 Sep 2024 17:01:44 -0400 Subject: [PATCH 2/7] - Made "FormComponent" more concrete with TextBox, Checkbox, Combobox - renamed all consequentially - updated related xaml --- src/Samples/Samples.generated.sln | 121 +++++++++++ .../SubModelSelectedItem.Core/FsWPF.fs | 8 +- .../SubModelSelectedItem.Core/Program.fs | 195 +++++++++--------- .../SubModelSelectedItem/MainWindow.xaml | 29 ++- .../SubModelSelectedItem/MainWindow.xaml.cs | 7 + 5 files changed, 254 insertions(+), 106 deletions(-) create mode 100644 src/Samples/Samples.generated.sln diff --git a/src/Samples/Samples.generated.sln b/src/Samples/Samples.generated.sln new file mode 100644 index 00000000..67ad07ba --- /dev/null +++ b/src/Samples/Samples.generated.sln @@ -0,0 +1,121 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capabilities", "Capabilities\Capabilities.csproj", "{50BE7B69-76EF-4A2F-956D-040F62645E48}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBindingsAndBehaviors", "EventBindingsAndBehaviors\EventBindingsAndBehaviors.csproj", "{5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDialogs", "FileDialogs\FileDialogs.csproj", "{57352DDA-A661-46B8-A0FE-232A8C72BC1A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDialogsCmdMsg", "FileDialogsCmdMsg\FileDialogsCmdMsg.csproj", "{A1A57765-4218-42C2-9A0B-9DD20945EEE6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiselect", "Multiselect\Multiselect.csproj", "{7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewWindow", "NewWindow\NewWindow.csproj", "{8CD66A83-01AD-433E-B41A-E7772C1A20C5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneWaySeq", "OneWaySeq\OneWaySeq.csproj", "{E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SingleCounter", "SingleCounter\SingleCounter.csproj", "{38238E19-8FC9-4E61-B6E0-4AD42B077574}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sticky", "Sticky\Sticky.csproj", "{097E0802-A94D-4599-84B8-6935EA9FCB01}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModel", "SubModel\SubModel.csproj", "{AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelOpt", "SubModelOpt\SubModelOpt.csproj", "{CC68E5BA-B417-4142-ACF4-4AB267904082}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelSelectedItem", "SubModelSelectedItem\SubModelSelectedItem.csproj", "{C7CF229D-D0A0-402B-818F-999A7F08725E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelSeq", "SubModelSeq\SubModelSeq.csproj", "{13684CFF-E918-47E5-8983-C68A2CB466B7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelStatic", "SubModelStatic\SubModelStatic.csproj", "{61E5C65D-796D-44CF-BFFC-2E7F66681DA2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Threading", "Threading\Threading.csproj", "{9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiBoundCmdParam", "UiBoundCmdParam\UiBoundCmdParam.csproj", "{A1D12300-4F10-42E6-907D-C98D84599FF7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Validation", "Validation\Validation.csproj", "{33A5664F-B468-444B-BB9C-91D6ACF2FCE7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50BE7B69-76EF-4A2F-956D-040F62645E48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50BE7B69-76EF-4A2F-956D-040F62645E48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50BE7B69-76EF-4A2F-956D-040F62645E48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50BE7B69-76EF-4A2F-956D-040F62645E48}.Release|Any CPU.Build.0 = Release|Any CPU + {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Release|Any CPU.Build.0 = Release|Any CPU + {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Release|Any CPU.Build.0 = Release|Any CPU + {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Release|Any CPU.Build.0 = Release|Any CPU + {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Release|Any CPU.Build.0 = Release|Any CPU + {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Release|Any CPU.Build.0 = Release|Any CPU + {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Release|Any CPU.Build.0 = Release|Any CPU + {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Release|Any CPU.Build.0 = Release|Any CPU + {097E0802-A94D-4599-84B8-6935EA9FCB01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {097E0802-A94D-4599-84B8-6935EA9FCB01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {097E0802-A94D-4599-84B8-6935EA9FCB01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {097E0802-A94D-4599-84B8-6935EA9FCB01}.Release|Any CPU.Build.0 = Release|Any CPU + {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Release|Any CPU.Build.0 = Release|Any CPU + {CC68E5BA-B417-4142-ACF4-4AB267904082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC68E5BA-B417-4142-ACF4-4AB267904082}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC68E5BA-B417-4142-ACF4-4AB267904082}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC68E5BA-B417-4142-ACF4-4AB267904082}.Release|Any CPU.Build.0 = Release|Any CPU + {C7CF229D-D0A0-402B-818F-999A7F08725E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7CF229D-D0A0-402B-818F-999A7F08725E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7CF229D-D0A0-402B-818F-999A7F08725E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7CF229D-D0A0-402B-818F-999A7F08725E}.Release|Any CPU.Build.0 = Release|Any CPU + {13684CFF-E918-47E5-8983-C68A2CB466B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13684CFF-E918-47E5-8983-C68A2CB466B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13684CFF-E918-47E5-8983-C68A2CB466B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13684CFF-E918-47E5-8983-C68A2CB466B7}.Release|Any CPU.Build.0 = Release|Any CPU + {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Release|Any CPU.Build.0 = Release|Any CPU + {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Release|Any CPU.Build.0 = Release|Any CPU + {A1D12300-4F10-42E6-907D-C98D84599FF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1D12300-4F10-42E6-907D-C98D84599FF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1D12300-4F10-42E6-907D-C98D84599FF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1D12300-4F10-42E6-907D-C98D84599FF7}.Release|Any CPU.Build.0 = Release|Any CPU + {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EAE8C9E3-06B3-4889-8E00-600FB55BEA5E} + EndGlobalSection +EndGlobal diff --git a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs index 50cfb3db..b3deda90 100644 --- a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs +++ b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs @@ -2,7 +2,13 @@ namespace FsWPF open System open System.Windows.Data +open System.Globalization +open System.Windows +open System.Windows.Controls open Elmish.WPF.Samples.SubModelSelectedItem.Program open Form -//[toDo] DataTemplateSelector +type ComponentsTemplateSelector() = + inherit DataTemplateSelector() + +// Properties for each DataTemplate diff --git a/src/Samples/SubModelSelectedItem.Core/Program.fs b/src/Samples/SubModelSelectedItem.Core/Program.fs index b68909b3..7d0e5ca3 100644 --- a/src/Samples/SubModelSelectedItem.Core/Program.fs +++ b/src/Samples/SubModelSelectedItem.Core/Program.fs @@ -12,7 +12,6 @@ open Elmish.WPF • [?] would something other than "SubModelSelectItem" be a better option for safety? • [?] how to better seperate *specific children fields* within dynamic bindings in "Form_VM.Components"? Just comment? Helpers? - • refactor: make FormComponent more concrete = TextBox, CheckBox, ComboBox • add: DataTemplateSelector • add: get focus after adding + selecting FormComponent (Behavior) @@ -28,13 +27,13 @@ module FormComponentHelpers = prefix + randomNumber () -module FormComponentA = +module TextBoxComponent = - type Model = { Id: Guid; Name: string } + type Model = { Id: Guid; Text: string } let create () = { Id = Guid.NewGuid() - Name = FormComponentHelpers.generateName "A_" } + Text = FormComponentHelpers.generateName "TextBox_" } let init () = create () @@ -45,17 +44,19 @@ module FormComponentA = | DummyMsg -> m -module FormComponentB = +module CheckBoxComponent = - type Model = { Id: Guid; Name: string } + type Model = + { Id: Guid + Label: string + IsChecked: bool } let create () = { Id = Guid.NewGuid() - Name = FormComponentHelpers.generateName "B_" } + Label = FormComponentHelpers.generateName "CheckBox_" + IsChecked = false } - let init () = - { Id = Guid.NewGuid() - Name = "B_" + Random().Next(10000, 100000).ToString() } + let init () = create () type Msg = DummyMsg @@ -64,13 +65,17 @@ module FormComponentB = | DummyMsg -> m -module FormComponentC = +module ComboBoxComponent = - type Model = { Id: Guid; Name: string } + type Model = + { Id: Guid + Name: string // header of GroupBox containing it + Items: string list } let create () = { Id = Guid.NewGuid() - Name = FormComponentHelpers.generateName "C_" } + Name = FormComponentHelpers.generateName "ComboBox_" + Items = [ "Item 1"; "Item 2"; "Item 3" ] } let init () = create () @@ -84,44 +89,41 @@ module FormComponentC = module Form = type Components = - | FormComponentA of FormComponentA.Model - | FormComponentB of FormComponentB.Model - | FormComponentC of FormComponentC.Model - + | TextBox of TextBoxComponent.Model + | CheckBox of CheckBoxComponent.Model + | ComboBox of ComboBoxComponent.Model type Model = { Components: Components list SelectedComponent: Guid option //• SubModels - FormComponentA_Model: FormComponentA.Model - FormComponentB_Model: FormComponentB.Model - FormComponentC_Model: FormComponentC.Model } - + TextBox_Model: TextBoxComponent.Model + CheckBox_Model: CheckBoxComponent.Model + ComboBox_Model: ComboBoxComponent.Model } let components_Mock = [ for _ in 1..3 do - yield FormComponentA(FormComponentA.create ()) - yield FormComponentB(FormComponentB.create ()) - yield FormComponentC(FormComponentC.create ()) ] - + yield TextBox(TextBoxComponent.create ()) + yield CheckBox(CheckBoxComponent.create ()) + yield ComboBox(ComboBoxComponent.create ()) ] let init () = { Components = components_Mock SelectedComponent = None //• SubModels - FormComponentA_Model = FormComponentA.init () - FormComponentB_Model = FormComponentB.init () - FormComponentC_Model = FormComponentC.init () } + TextBox_Model = TextBoxComponent.init () + CheckBox_Model = CheckBoxComponent.init () + ComboBox_Model = ComboBoxComponent.init () } type Msg = | Select of Guid option - | AddFormComponentA - | AddFormComponentB - | AddFormComponentC + | AddTextBox + | AddCheckBox + | AddComboBox //• SubMsgs - | TextBoxA_Msg of FormComponentA.Msg - | TextBoxB_Msg of FormComponentB.Msg - | TextBoxC_Msg of FormComponentC.Msg + | TextBox_Msg of TextBoxComponent.Msg + | CheckBox_Msg of CheckBoxComponent.Msg + | ComboBoxC_Msg of ComboBoxComponent.Msg [] module Form = @@ -133,94 +135,96 @@ module Form = let getComponentId component_ = match component_ with - | FormComponentA a -> a.Id - | FormComponentB b -> b.Id - | FormComponentC c -> c.Id + | TextBox a -> a.Id + | CheckBox b -> b.Id + | ComboBox c -> c.Id let getComponentName component_ = match component_ with - | FormComponentA a -> a.Name - | FormComponentB b -> b.Name - | FormComponentC c -> c.Name + | TextBox a -> a.Text + | CheckBox b -> b.Label + | ComboBox c -> c.Name let isSelected selectedId component_ = match selectedId, component_ with - | Some id, FormComponentA a -> a.Id = id - | Some id, FormComponentB b -> b.Id = id - | Some id, FormComponentC c -> c.Id = id + | Some id, TextBox a -> a.Id = id + | Some id, CheckBox b -> b.Id = id + | Some id, ComboBox c -> c.Id = id | _ -> false - let insertComponentAfterSelected selectedComponent newComponent components = + let insertComponentAfterSelected selectedComponent newComponent components = - // sample purpose: make explicit that a new component has been added - let prependNewName component_ = - match component_ with - | FormComponentA a -> FormComponentA { a with Name = "#New# " + a.Name } - | FormComponentB b -> FormComponentB { b with Name = "#New# " + b.Name } - | FormComponentC c -> FormComponentC { c with Name = "#New# " + c.Name } + // sample purpose: make explicit that a new component has been added + let prependNewName component_ = + match component_ with + | TextBox a -> TextBox { a with Text = "#New# " + a.Text } + | CheckBox b -> CheckBox { b with Label = "#New# " + b.Label } + | ComboBox c -> ComboBox { c with Name = "#New# " + c.Name } - let newComponentWithPrependedName = prependNewName newComponent + let newComponentWithPrependedName = prependNewName newComponent - match selectedComponent with - | None -> - // If no component is selected, append the new one to the end - components @ [ newComponentWithPrependedName ] - | Some selectedId -> - let rec insertAfterSelected = - function - | [] -> [ newComponentWithPrependedName ] - | comp :: rest when getComponentId comp = selectedId -> comp :: newComponentWithPrependedName :: rest - | comp :: rest -> comp :: insertAfterSelected rest + match selectedComponent with + | None -> + // If no component is selected, append the new one to the end + components @ [ newComponentWithPrependedName ] + | Some selectedId -> + let rec insertAfterSelected = + function + | [] -> [ newComponentWithPrependedName ] + | comp :: rest when getComponentId comp = selectedId -> + comp :: newComponentWithPrependedName :: rest + | comp :: rest -> comp :: insertAfterSelected rest - insertAfterSelected components + insertAfterSelected components let update msg m = match msg with | Select entityId -> { m with SelectedComponent = entityId } - | AddFormComponentA -> - let newComponent = FormComponentA(FormComponentA.create ()) + | AddTextBox -> + let newTextBox = TextBox(TextBoxComponent.create ()) - let newComponentId = - match newComponent with - | FormComponentA a -> a.Id + let newTextBoxId = + match newTextBox with + | TextBox a -> a.Id | _ -> Guid.Empty { m with - Components = insertComponentAfterSelected m.SelectedComponent newComponent m.Components - SelectedComponent = Some newComponentId } + Components = insertComponentAfterSelected m.SelectedComponent newTextBox m.Components + SelectedComponent = Some newTextBoxId } - | AddFormComponentB -> - let newComponent = FormComponentB(FormComponentB.create ()) + | AddCheckBox -> + let newCheckBox = CheckBox(CheckBoxComponent.create ()) - let newComponentId = - match newComponent with - | FormComponentB b -> b.Id + let newCheckBoxId = + match newCheckBox with + | CheckBox b -> b.Id | _ -> Guid.Empty { m with - Components = insertComponentAfterSelected m.SelectedComponent newComponent m.Components - SelectedComponent = Some newComponentId } + Components = insertComponentAfterSelected m.SelectedComponent newCheckBox m.Components + SelectedComponent = Some newCheckBoxId } - | AddFormComponentC -> - let newComponent = FormComponentC(FormComponentC.create ()) + | AddComboBox -> + let newComboBox = ComboBox(ComboBoxComponent.create ()) - let newComponentId = - match newComponent with - | FormComponentC c -> c.Id + let newComboBoxId = + match newComboBox with + | ComboBox c -> c.Id | _ -> Guid.Empty { m with - Components = insertComponentAfterSelected m.SelectedComponent newComponent m.Components - SelectedComponent = Some newComponentId } + Components = insertComponentAfterSelected m.SelectedComponent newComboBox m.Components + SelectedComponent = Some newComboBoxId } //• SubModels - | TextBoxA_Msg msg -> { m with FormComponentA_Model = FormComponentA.update msg m.FormComponentA_Model } - | TextBoxB_Msg msg -> { m with FormComponentB_Model = FormComponentB.update msg m.FormComponentB_Model } - | TextBoxC_Msg msg -> { m with FormComponentC_Model = FormComponentC.update msg m.FormComponentC_Model } + | TextBox_Msg msg -> { m with TextBox_Model = TextBoxComponent.update msg m.TextBox_Model } + | CheckBox_Msg msg -> { m with CheckBox_Model = CheckBoxComponent.update msg m.CheckBox_Model } + | ComboBoxC_Msg msg -> { m with ComboBox_Model = ComboBoxComponent.update msg m.ComboBox_Model } +//# ViewModel/Bindings open Form.Form // ugly [] @@ -248,6 +252,7 @@ type Form_VM(args) = "") ]) )) + // I don't like the stringly-typed nature of this binding member _.SelectedEntity with get () = @@ -287,9 +292,9 @@ type Form_VM(args) = match m.Components |> List.find (fun e -> getComponentId e = id) with - | Form.Components.FormComponentA _ -> "Type: A" - | Form.Components.FormComponentB _ -> "Type: B" - | Form.Components.FormComponentC _ -> "Type: C" + | Form.Components.TextBox _ -> "Type: A" + | Form.Components.CheckBox _ -> "Type: B" + | Form.Components.ComboBox _ -> "Type: C" sprintf "Selected: Name = %s, Index = %d, %s" name index componentType | None -> "No selection")) @@ -297,9 +302,9 @@ type Form_VM(args) = //• Commands - member _.AddTextBoxA = base.Get () (Binding.CmdT.setAlways Form.AddFormComponentA) - member _.AddTextBoxB = base.Get () (Binding.CmdT.setAlways Form.AddFormComponentB) - member _.AddTextBoxC = base.Get () (Binding.CmdT.setAlways Form.AddFormComponentC) + member _.AddTextBox = base.Get () (Binding.CmdT.setAlways Form.AddTextBox) + member _.AddCheckBox = base.Get () (Binding.CmdT.setAlways Form.AddCheckBox) + member _.AddComboBox = base.Get () (Binding.CmdT.setAlways Form.AddComboBox) member _.SelectRandom = base.Get @@ -308,9 +313,9 @@ type Form_VM(args) = let randomEntity = m.Components.Item(Random().Next(m.Components.Length)) match randomEntity with - | Form.Components.FormComponentA aModel -> Some aModel.Id - | Form.Components.FormComponentB bModel -> Some bModel.Id - | Form.Components.FormComponentC cModel -> Some cModel.Id + | Form.Components.TextBox aModel -> Some aModel.Id + | Form.Components.CheckBox bModel -> Some bModel.Id + | Form.Components.ComboBox cModel -> Some cModel.Id |> Form.Msg.Select)) member _.Deselect = diff --git a/src/Samples/SubModelSelectedItem/MainWindow.xaml b/src/Samples/SubModelSelectedItem/MainWindow.xaml index 63861b95..e7493ec4 100644 --- a/src/Samples/SubModelSelectedItem/MainWindow.xaml +++ b/src/Samples/SubModelSelectedItem/MainWindow.xaml @@ -3,12 +3,14 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:fsWPF="clr-namespace:FsWPF;assembly=SubModelSelectedItem.Core" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:Elmish.WPF.Samples.SubModelSelectedItem.Program;assembly=SubModelSelectedItem.Core" Title="SubModelSelectedItem+StaticBindings" - Width="600" + Width="480" d:DataContext="{d:DesignInstance Type=vm:Form_VM, IsDesignTimeCreatable=True}" + SizeToContent="Height" WindowStartupLocation="CenterScreen" mc:Ignorable="d"> @@ -24,19 +26,19 @@ @@ -60,28 +62,35 @@ - + + + + - - + + { + if (e.Key == System.Windows.Input.Key.Escape) + { + Close(); + } + }; } } } From e4e77208acfaeab79cab61c11970a27ef1d67d1c Mon Sep 17 00:00:00 2001 From: YkTru Date: Sat, 21 Sep 2024 00:36:32 -0400 Subject: [PATCH 3/7] + DataTemplateSelector (not working) + IValueConverter (don't know if needed) --- .../SubModelSelectedItem.Core/FsWPF.fs | 43 ++++++++++- .../SubModelSelectedItem.Core/Program.fs | 58 ++++++++++----- .../SubModelSelectedItem/MainWindow.xaml | 74 +++++++++++++++---- 3 files changed, 139 insertions(+), 36 deletions(-) diff --git a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs index b3deda90..67e8e06f 100644 --- a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs +++ b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs @@ -11,4 +11,45 @@ open Form type ComponentsTemplateSelector() = inherit DataTemplateSelector() -// Properties for each DataTemplate + member val TextBox_DT: DataTemplate = null with get, set + member val CheckBox_DT: DataTemplate = null with get, set + member val ComboBox_DT: DataTemplate = null with get, set + + override this.SelectTemplate(item: obj, container: DependencyObject) : DataTemplate = + match item with + | :? Form.Components as component_ -> + match component_ with + | Form.Components.TextBox _ -> this.TextBox_DT + | Form.Components.CheckBox _ -> this.CheckBox_DT + | Form.Components.ComboBox _ -> this.ComboBox_DT + | _ -> null + + +// not working; idea taken from "https://github.com/elmish/Elmish.WPF/issues/270#issuecomment-687329493" +type GetComponentsVMToResource() = + let toVM = + function + | TextBox a -> a |> box + | CheckBox a -> a |> box + | ComboBox a -> a |> box + + interface IValueConverter with + member this.Convert + ( + value: obj, + targetType: System.Type, + parameter: obj, + culture: System.Globalization.CultureInfo + ) : obj = + match value with + | :? list as components -> components |> List.map toVM |> box + | _ -> failwith "shouldn't happen" + + member this.ConvertBack + ( + value: obj, + targetType: System.Type, + parameter: obj, + culture: System.Globalization.CultureInfo + ) : obj = + raise (System.NotImplementedException()) \ No newline at end of file diff --git a/src/Samples/SubModelSelectedItem.Core/Program.fs b/src/Samples/SubModelSelectedItem.Core/Program.fs index 7d0e5ca3..a5f95fd0 100644 --- a/src/Samples/SubModelSelectedItem.Core/Program.fs +++ b/src/Samples/SubModelSelectedItem.Core/Program.fs @@ -6,13 +6,15 @@ open Serilog.Extensions.Logging open Elmish.WPF (* -[toDos] - • *change all [dynamic bindings] to [static bindings] using an upcoming Elmish.WPF revised static bindings approach* - • [?] make "_VM" for each child (cleaner separation) - • [?] would something other than "SubModelSelectItem" be a better option for safety? - • [?] how to better seperate *specific children fields* within dynamic bindings in "Form_VM.Components"? Just comment? Helpers? - - • add: DataTemplateSelector +[toDos] + + ("#" = issue, "•" = task) + + # change all [dynamic bindings] to [static bindings] (using an upcoming Elmish.WPF revised static bindings approach?) + • static bindings: make ViewModels for each Component (also working Intellisense for binding submodel in Xalm) + • [?] would something other than "SubModelSelectItem" be a better option? + + # not working: DataTemplateSelector • add: get focus after adding + selecting FormComponent (Behavior) • refactor: revise all helpers in Form (some were made quick&dirty) @@ -69,12 +71,12 @@ module ComboBoxComponent = type Model = { Id: Guid - Name: string // header of GroupBox containing it + Header: string // Header (of GroupBox containing it) Items: string list } let create () = { Id = Guid.NewGuid() - Name = FormComponentHelpers.generateName "ComboBox_" + Header = FormComponentHelpers.generateName "ComboBox_" Items = [ "Item 1"; "Item 2"; "Item 3" ] } let init () = create () @@ -101,6 +103,10 @@ module Form = CheckBox_Model: CheckBoxComponent.Model ComboBox_Model: ComboBoxComponent.Model } + module ModelM = + module TextBox = + let get (m: Model) = m.TextBox_Model + let components_Mock = [ for _ in 1..3 do yield TextBox(TextBoxComponent.create ()) @@ -120,7 +126,7 @@ module Form = | AddTextBox | AddCheckBox | AddComboBox - //• SubMsgs + //• SubModels | TextBox_Msg of TextBoxComponent.Msg | CheckBox_Msg of CheckBoxComponent.Msg | ComboBoxC_Msg of ComboBoxComponent.Msg @@ -143,7 +149,7 @@ module Form = match component_ with | TextBox a -> a.Text | CheckBox b -> b.Label - | ComboBox c -> c.Name + | ComboBox c -> c.Header let isSelected selectedId component_ = match selectedId, component_ with @@ -159,7 +165,7 @@ module Form = match component_ with | TextBox a -> TextBox { a with Text = "#New# " + a.Text } | CheckBox b -> CheckBox { b with Label = "#New# " + b.Label } - | ComboBox c -> ComboBox { c with Name = "#New# " + c.Name } + | ComboBox c -> ComboBox { c with Header = "#New# " + c.Header } let newComponentWithPrependedName = prependNewName newComponent @@ -224,7 +230,7 @@ module Form = | ComboBoxC_Msg msg -> { m with ComboBox_Model = ComboBoxComponent.update msg m.ComboBox_Model } -//# ViewModel/Bindings +// ViewModel/Bindings open Form.Form // ugly [] @@ -233,8 +239,9 @@ type Form_VM(args) = new() = Form_VM(Form.init () |> ViewModelArgs.simple) - //• Properties // I *really* don't like the stringly-typed nature of this binding + no Intellisense in Xaml for submodel properties + //# DataTemplateSelector not working, but SelectedEntity works + //# as ListItem ItemSource = "Elmish.WPF.DynamicViewModel'2" member _.Components = base.Get () @@ -242,7 +249,9 @@ type Form_VM(args) = (fun m -> m.Components), (fun (e) -> getComponentId e), (fun () -> - [ "Name" + [ + //• TextBox + "Text" |> Binding.oneWay (fun (_, e) -> getComponentName e) "SelectedLabel" |> Binding.oneWay (fun (m, e) -> @@ -250,10 +259,20 @@ type Form_VM(args) = " - Selected" else "") ]) + + //• CheckBox + // (add later) + + //• ComboBox )) - // I don't like the stringly-typed nature of this binding + //# DataTemplateSelector seems to work (though I can't bind to subProperties), but SelectedEntity breaks.. + //# as ListItem ItemSource = "ComboBox {Id = 3232 (..) Header = "ComboBox_3232" Items = ["Item 1"; "Item 2"; "Item 3"]}" + member _.Components2 = base.Get() (Binding.oneWaySeq (( _.Components ), (=), id)) + + + // I don't like the stringly-typed nature of this binding either member _.SelectedEntity with get () = base.Get @@ -318,9 +337,8 @@ type Form_VM(args) = | Form.Components.ComboBox cModel -> Some cModel.Id |> Form.Msg.Select)) - member _.Deselect = - base.Get () (Binding.cmd (fun (m: Form.Model) -> Form.Msg.Select None)) - + member _.Deselect = base.Get() (Binding.CmdT.setAlways (Form.Msg.Select None)) + module Program = let main window = @@ -334,4 +352,4 @@ module Program = WpfProgram.mkSimpleT Form.init Form.update Form_VM |> WpfProgram.withLogger (new SerilogLoggerFactory(logger)) - |> WpfProgram.startElmishLoop window + |> WpfProgram.startElmishLoop window \ No newline at end of file diff --git a/src/Samples/SubModelSelectedItem/MainWindow.xaml b/src/Samples/SubModelSelectedItem/MainWindow.xaml index e7493ec4..9be097e0 100644 --- a/src/Samples/SubModelSelectedItem/MainWindow.xaml +++ b/src/Samples/SubModelSelectedItem/MainWindow.xaml @@ -13,6 +13,61 @@ SizeToContent="Height" WindowStartupLocation="CenterScreen" mc:Ignorable="d"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -62,11 +117,12 @@ + d:SelectedItem="{Binding SelectedEntity, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + ItemTemplateSelector="{StaticResource ComponentsTemplateSelector}" + ItemsSource="{Binding Components}"> - - - - - - - - Date: Mon, 23 Sep 2024 05:39:29 -0400 Subject: [PATCH 4/7] - Redmade from scratch - Everything seems to work (except ComboBox item selection that is not implemented yet) --- src/Elmish.WPF/Binding.fs | 6825 +++++++++-------- src/Samples/Samples.generated.sln | 170 +- .../SubModelSelectedItem.Core/FsWPF.fs | 55 - .../SubModelSelectedItem.Core/Program.fs | 655 +- .../SubModelSelectedItem/MainWindow.xaml | 235 +- 5 files changed, 4045 insertions(+), 3895 deletions(-) delete mode 100644 src/Samples/SubModelSelectedItem.Core/FsWPF.fs diff --git a/src/Elmish.WPF/Binding.fs b/src/Elmish.WPF/Binding.fs index c95c56d9..87727208 100644 --- a/src/Elmish.WPF/Binding.fs +++ b/src/Elmish.WPF/Binding.fs @@ -8,2951 +8,765 @@ open System.Collections.ObjectModel module Binding = - open BindingData - - let internal mapData f binding = - { Name = binding.Name - Data = binding.Data |> f } - - /// Boxes the output parameter. - /// Allows using a strongly-typed submodel binding (from a module ending in "T") - /// in a binding list (rather than in a view model class member as normal). - let boxT (binding: Binding<'b, 'msg, 't>) = BindingData.boxT |> mapData <| binding - - /// Unboxes the output parameter - let unboxT (binding: Binding<'b, 'msg>): Binding<'b, 'msg, 't> = BindingData.unboxT |> mapData <| binding - - /// Maps the model of a binding via a contravariant mapping. - let mapModel (f: 'a -> 'b) (binding: Binding<'b, 'msg, 't>) = f |> mapModel |> mapData <| binding - - /// Maps the message of a binding with access to the model via a covariant mapping. - let mapMsgWithModel (f: 'a -> 'model -> 'b) (binding: Binding<'model, 'a, 't>) = f |> mapMsgWithModel |> mapData <| binding - - /// Maps the message of a binding via a covariant mapping. - let mapMsg (f: 'a -> 'b) (binding: Binding<'model, 'a, 't>) = f |> mapMsg |> mapData <| binding - - /// Sets the message of a binding with access to the model. - let setMsgWithModel (f: 'model -> 'b) (binding: Binding<'model, 'a, 't>) = f |> setMsgWithModel |> mapData <| binding - - /// Sets the message of a binding. - let setMsg (msg: 'b) (binding: Binding<'model, 'a, 't>) = msg |> setMsg |> mapData <| binding - - - /// Restricts the binding to models that satisfy the predicate after some model satisfies the predicate. - let addSticky (predicate: 'model -> bool) (binding: Binding<'model, 'msg, 't>) = predicate |> addSticky |> mapData <| binding - - /// - /// Adds caching to the given binding. The cache holds a single value and - /// is invalidated after the given binding raises the - /// PropertyChanged event. - /// - /// The binding to which caching is added. - let addCaching (binding: Binding<'model, 'msg, 't>) : Binding<'model, 'msg, 't> = - binding - |> mapData addCaching - - /// - /// Adds validation to the given binding using INotifyDataErrorInfo. - /// - /// Returns the errors associated with the given model. - /// The binding to which validation is added. - let addValidation (validate: 'model -> string list) (binding: Binding<'model, 'msg, 't>) : Binding<'model, 'msg, 't> = - binding - |> mapData (addValidation validate) - - /// - /// Adds laziness to the updating of the given binding. If the models are considered equal, - /// then updating of the given binding is skipped. - /// - /// Updating skipped when this function returns true. - /// The binding to which the laziness is added. - let addLazy (equals: 'model -> 'model -> bool) (binding: Binding<'model, 'msg, 't>) : Binding<'model, 'msg, 't> = - binding - |> mapData (addLazy equals) - - /// - /// Alters the message stream via the given function. - /// Ideally suited for use with Reactive Extensions. - /// - /// open FSharp.Control.Reactive - /// let delay dispatch = - /// let subject = Subject.broadcast - /// let observable = subject :> System.IObservable<_> - /// observable - /// |> Observable.delay (System.TimeSpan.FromSeconds 1.0) - /// |> Observable.subscribe dispatch - /// |> ignore - /// subject.OnNext - /// - /// // ... - /// - /// binding |> Binding.alterMsgStream delay - /// - /// - /// The function that can alter the message stream. - /// The binding of the altered message stream. - let alterMsgStream (alteration: ('b -> unit) -> 'a -> unit) (binding: Binding<'model, 'a, 't>) : Binding<'model, 'b, 't> = - binding - |> mapData (alterMsgStream alteration) - - - /// - /// Strongly-typed bindings that update the view from the model. - /// - module OneWayT = - - /// Elemental instance of a one-way binding. - let id<'a, 'msg> : string -> Binding<'a, 'msg, 'a> = - OneWay.id - |> createBindingT - - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let opt x : Binding<'a option, 'msg, System.Nullable<'a>> = - x - |> id - |> mapModel Option.toNullable - - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let vopt x : Binding<'a voption, 'msg, System.Nullable<'a>> = - x - |> id - |> mapModel ValueOption.toNullable - - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let optobj<'a, 'msg when 'a : null> : string -> Binding<'a option, 'msg, 'a> = - id<'a, 'msg> - >> mapModel Option.toObj - - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let voptobj<'a, 'msg when 'a : null> : string -> Binding<'a voption, 'msg, 'a> = - id<'a, 'msg> - >> mapModel ValueOption.toObj - - /// - /// Strongly-typed bindings that update the model from the view. - /// - module OneWayToSourceT = - - /// Elemental instance of a one-way-to-source binding. - let id<'model, 'a> : string -> Binding<'model, 'a, 'a> = - OneWayToSource.id - |> createBindingT - - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let optobj<'a, 'model when 'a : null> : string -> Binding<'model, 'a option, 'a> = - id<'model, 'a> - >> mapMsg Option.ofObj - - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let voptobj<'a, 'model when 'a : null> : string -> Binding<'model, 'a voption, 'a> = - id<'model, 'a> - >> mapMsg ValueOption.ofObj - - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let opt x : Binding<'model, 'a option, System.Nullable<'a>> = - x - |> id - |> mapMsg Option.ofNullable - - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let vopt x : Binding<'model, 'a voption, System.Nullable<'a>> = - x - |> id - |> mapMsg ValueOption.ofNullable - - /// - /// Strongly-typed bindings that update both ways - /// - module TwoWayT = - - /// Elemental instance of a two-way binding. - let id<'a> : string -> Binding<'a, 'a, 'a> = - TwoWay.id - |> createBindingT - - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let opt x : Binding<'a option, 'a option, System.Nullable<'a>> = - x - |> id - |> mapMsg Option.ofNullable - |> mapModel Option.toNullable - - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let vopt x : Binding<'a voption, 'a voption, System.Nullable<'a>> = - x - |> id - |> mapMsg ValueOption.ofNullable - |> mapModel ValueOption.toNullable - - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let optobj<'a when 'a : null> : string -> Binding<'a option, 'a option, 'a> = - id<'a> - >> mapModel Option.toObj - >> mapMsg Option.ofObj - - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let voptobj<'a when 'a : null> : string -> Binding<'a voption, 'a voption, 'a> = - id<'a> - >> mapMsg ValueOption.ofObj - >> mapModel ValueOption.toObj - - /// - /// The strongly-typed counterpart of Binding.oneWaySeq with parameter getId. - /// Exposes an ObservableCollection of child items for binding. - /// Allows a more efficient update than would be possible without using ids. - /// - module OneWaySeqT = - - /// - /// Elemental instance of a OneWaySeqT binding - /// - /// Defines whether an item is "equal" and needs to be updated if the ids are the same - /// Unique identifier for each item in the list (for efficient updates). - let id itemEquals (getId: 'a -> 'id) : string -> Binding<_, 'msg, _> = - OneWaySeq.create itemEquals getId - |> createBindingT - - /// - /// Strongly-typed bindings that dispatch messages from the view. - /// - module CmdT = + open BindingData - /// - /// Elemental instance of a Command binding. - /// Creates a Command binding that only passes the CommandParameter) - /// - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - /// Indicates whether the command can execute. - let id<'model> uiBoundCmdParam canExec - : string -> Binding<'model, obj, ICommand> = - Cmd.createWithParam - (fun p _ -> ValueSome p) - canExec - uiBoundCmdParam - |> createBindingT + let internal mapData f binding = + { Name = binding.Name + Data = binding.Data |> f } + + /// Boxes the output parameter. + /// Allows using a strongly-typed submodel binding (from a module ending in "T") + /// in a binding list (rather than in a view model class member as normal). + let boxT (binding: Binding<'b, 'msg, 't>) = BindingData.boxT |> mapData <| binding + + /// Unboxes the output parameter + let unboxT (binding: Binding<'b, 'msg>) : Binding<'b, 'msg, 't> = + BindingData.unboxT |> mapData <| binding + + /// Maps the model of a binding via a contravariant mapping. + let mapModel (f: 'a -> 'b) (binding: Binding<'b, 'msg, 't>) = f |> mapModel |> mapData <| binding + + /// Maps the message of a binding with access to the model via a covariant mapping. + let mapMsgWithModel (f: 'a -> 'model -> 'b) (binding: Binding<'model, 'a, 't>) = + f |> mapMsgWithModel |> mapData <| binding + + /// Maps the message of a binding via a covariant mapping. + let mapMsg (f: 'a -> 'b) (binding: Binding<'model, 'a, 't>) = f |> mapMsg |> mapData <| binding + + /// Sets the message of a binding with access to the model. + let setMsgWithModel (f: 'model -> 'b) (binding: Binding<'model, 'a, 't>) = + f |> setMsgWithModel |> mapData <| binding + + /// Sets the message of a binding. + let setMsg (msg: 'b) (binding: Binding<'model, 'a, 't>) = msg |> setMsg |> mapData <| binding + + + /// Restricts the binding to models that satisfy the predicate after some model satisfies the predicate. + let addSticky (predicate: 'model -> bool) (binding: Binding<'model, 'msg, 't>) = + predicate |> addSticky |> mapData <| binding /// - /// Creates a Command binding that depends only on the model (not the - /// CommandParameter). + /// Adds caching to the given binding. The cache holds a single value and + /// is invalidated after the given binding raises the + /// PropertyChanged event. /// - /// Indicates whether the command can execute. - /// Returns the message to dispatch. - let model - canExec - (exec: 'model -> 'msg) - : string -> Binding<'model, 'msg, ICommand> = - id false (fun _ m -> m |> canExec) - >> mapMsgWithModel (fun _ y -> y |> exec) - >> addLazy (fun m1 m2 -> canExec m1 = canExec m2) + /// The binding to which caching is added. + let addCaching (binding: Binding<'model, 'msg, 't>) : Binding<'model, 'msg, 't> = binding |> mapData addCaching /// - /// Creates a Command binding that dispatches the specified message. + /// Adds validation to the given binding using INotifyDataErrorInfo. /// - /// Indicates whether the command can execute. - /// The message to dispatch. - let set - canExec - (msg: 'msg) - : string -> Binding<'model, 'msg, ICommand> = - id false (fun _ m -> m |> canExec) - >> setMsg msg + /// Returns the errors associated with the given model. + /// The binding to which validation is added. + let addValidation + (validate: 'model -> string list) + (binding: Binding<'model, 'msg, 't>) + : Binding<'model, 'msg, 't> = + binding |> mapData (addValidation validate) /// - /// Creates a Command binding that depends only on the model (not the - /// CommandParameter) and always executes. + /// Adds laziness to the updating of the given binding. If the models are considered equal, + /// then updating of the given binding is skipped. /// - /// Returns the message to dispatch. - let modelAlways - (exec: 'model -> 'msg) - : string -> Binding<'model, 'msg, ICommand> = - model (fun _ -> true) exec + /// Updating skipped when this function returns true. + /// The binding to which the laziness is added. + let addLazy (equals: 'model -> 'model -> bool) (binding: Binding<'model, 'msg, 't>) : Binding<'model, 'msg, 't> = + binding |> mapData (addLazy equals) /// - /// Creates a Command binding that dispatches the specified message - /// and always executes. - /// - /// The message to dispatch. - let setAlways - (msg: 'msg) - : string -> Binding<'model, 'msg, ICommand> = - set (fun _ -> true) msg - - module OneWay = - - /// Elemental instance of a one-way binding. - let id<'a, 'msg> : string -> Binding<'a, 'msg> = - OneWay.id - |> createBinding - - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let opt<'a, 'msg> : string -> Binding<'a option, 'msg> = - id - >> mapModel Option.box - - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let vopt<'a, 'msg> : string -> Binding<'a voption, 'msg> = - id - >> mapModel ValueOption.box - - - module OneWayToSource = - - /// Elemental instance of a one-way-to-source binding. - let id<'model, 'a> : string -> Binding<'model, 'a> = - OneWayToSource.id - |> createBinding - - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let vopt<'model, 'a> : string -> Binding<'model, 'a voption> = - id<'model, obj> - >> mapMsg ValueOption.unbox - - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let opt<'model, 'a> : string -> Binding<'model, 'a option> = - id<'model, obj> - >> mapMsg Option.unbox - - - module TwoWay = - - /// Elemental instance of a two-way binding. - let id<'a> : string -> Binding<'a, 'a> = - TwoWay.id - |> createBinding - - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let vopt<'a> : string -> Binding<'a voption, 'a voption> = - id - >> mapModel ValueOption.box - >> mapMsg ValueOption.unbox - - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - let opt<'a> : string -> Binding<'a option, 'a option> = - id - >> mapModel Option.box - >> mapMsg Option.unbox - - - module SubModelSelectedItem = - - /// Creates a two-way binding to a SelectedItem-like property where - /// the ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF view - /// models to/from their corresponding IDs, so the Elmish user code only has - /// to work with the IDs. + /// Alters the message stream via the given function. + /// Ideally suited for use with Reactive Extensions. + /// + /// open FSharp.Control.Reactive + /// let delay dispatch = + /// let subject = Subject.broadcast + /// let observable = subject :> System.IObservable<_> + /// observable + /// |> Observable.delay (System.TimeSpan.FromSeconds 1.0) + /// |> Observable.subscribe dispatch + /// |> ignore + /// subject.OnNext /// - /// Only use this if you are unable to use some kind of SelectedValue - /// or SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when initializing - /// the bindings if - /// does not correspond to a binding, and it will - /// throw at runtime if the inferred 'id type does not match the - /// actual ID type used in that binding. - let vopt subModelSeqBindingName : string -> Binding<'id voption, 'id voption> = - SubModelSelectedItem.create subModelSeqBindingName - |> createBinding - >> mapModel (ValueOption.map box) - >> mapMsg (ValueOption.map unbox) - - /// Creates a two-way binding to a SelectedItem-like property where - /// the ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF view - /// models to/from their corresponding IDs, so the Elmish user code only has - /// to work with the IDs. + /// // ... /// - /// Only use this if you are unable to use some kind of SelectedValue - /// or SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when initializing - /// the bindings if - /// does not correspond to a binding, and it will - /// throw at runtime if the inferred 'id type does not match the - /// actual ID type used in that binding. - let opt subModelSeqBindingName : string -> Binding<'id option, 'id option> = - vopt subModelSeqBindingName - >> mapModel ValueOption.ofOption - >> mapMsg ValueOption.toOption - - - module Cmd = - - let internal createWithParam exec canExec autoRequery = - Cmd.createWithParam exec canExec autoRequery - |> createBinding - - let internal create exec canExec = - createWithParam - (fun _ -> exec) - (fun _ -> canExec) - false - >> addLazy (fun m1 m2 -> canExec m1 = canExec m2) - - - module OneWaySeq = + /// binding |> Binding.alterMsgStream delay + /// + /// + /// The function that can alter the message stream. + /// The binding of the altered message stream. + let alterMsgStream + (alteration: ('b -> unit) -> 'a -> unit) + (binding: Binding<'model, 'a, 't>) + : Binding<'model, 'b, 't> = + binding |> mapData (alterMsgStream alteration) - let internal create get itemEquals getId = - OneWaySeq.create itemEquals getId - |> BindingData.mapModel get - |> createBinding + /// + /// Strongly-typed bindings that update the view from the model. + /// + module OneWayT = + + /// Elemental instance of a one-way binding. + let id<'a, 'msg> : string -> Binding<'a, 'msg, 'a> = OneWay.id |> createBindingT + + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let opt x : Binding<'a option, 'msg, System.Nullable<'a>> = x |> id |> mapModel Option.toNullable + + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let vopt x : Binding<'a voption, 'msg, System.Nullable<'a>> = + x |> id |> mapModel ValueOption.toNullable + + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let optobj<'a, 'msg when 'a: null> : string -> Binding<'a option, 'msg, 'a> = + id<'a, 'msg> >> mapModel Option.toObj + + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let voptobj<'a, 'msg when 'a: null> : string -> Binding<'a voption, 'msg, 'a> = + id<'a, 'msg> >> mapModel ValueOption.toObj - module SubModel = + /// + /// Strongly-typed bindings that update the model from the view. + /// + module OneWayToSourceT = + + /// Elemental instance of a one-way-to-source binding. + let id<'model, 'a> : string -> Binding<'model, 'a, 'a> = + OneWayToSource.id |> createBindingT + + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let optobj<'a, 'model when 'a: null> : string -> Binding<'model, 'a option, 'a> = + id<'model, 'a> >> mapMsg Option.ofObj + + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let voptobj<'a, 'model when 'a: null> : string -> Binding<'model, 'a voption, 'a> = + id<'model, 'a> >> mapMsg ValueOption.ofObj + + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let opt x : Binding<'model, 'a option, System.Nullable<'a>> = x |> id |> mapMsg Option.ofNullable + + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let vopt x : Binding<'model, 'a voption, System.Nullable<'a>> = + x |> id |> mapMsg ValueOption.ofNullable /// - /// Creates a binding to a sub-model/component. You typically bind this - /// to the DataContext of a UserControl or similar. + /// Strongly-typed bindings that update both ways /// - /// Returns the bindings for the sub-model. - let vopt (bindings: unit -> Binding<'model, 'msg> list) - : string -> Binding<'model voption, 'msg> = - SubModel.create - (fun args -> DynamicViewModel<'model, 'msg>(args, bindings ())) - IViewModel.updateModel - |> createBinding + module TwoWayT = + + /// Elemental instance of a two-way binding. + let id<'a> : string -> Binding<'a, 'a, 'a> = TwoWay.id |> createBindingT + + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let opt x : Binding<'a option, 'a option, System.Nullable<'a>> = + x + |> id + |> mapMsg Option.ofNullable + |> mapModel Option.toNullable + + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let vopt x : Binding<'a voption, 'a voption, System.Nullable<'a>> = + x + |> id + |> mapMsg ValueOption.ofNullable + |> mapModel ValueOption.toNullable + + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let optobj<'a when 'a: null> : string -> Binding<'a option, 'a option, 'a> = + id<'a> + >> mapModel Option.toObj + >> mapMsg Option.ofObj + + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let voptobj<'a when 'a: null> : string -> Binding<'a voption, 'a voption, 'a> = + id<'a> + >> mapMsg ValueOption.ofObj + >> mapModel ValueOption.toObj /// - /// Creates a binding to a sub-model/component. You typically bind this - /// to the DataContext of a UserControl or similar. + /// The strongly-typed counterpart of Binding.oneWaySeq with parameter getId. + /// Exposes an ObservableCollection of child items for binding. + /// Allows a more efficient update than would be possible without using ids. /// - /// Returns the bindings for the sub-model. - let opt (bindings: unit -> Binding<'model, 'msg> list) - : string -> Binding<'model option, 'msg> = - vopt bindings - >> mapModel ValueOption.ofOption + module OneWaySeqT = + + /// + /// Elemental instance of a OneWaySeqT binding + /// + /// Defines whether an item is "equal" and needs to be updated if the ids are the same + /// Unique identifier for each item in the list (for efficient updates). + let id itemEquals (getId: 'a -> 'id) : string -> Binding<_, 'msg, _> = + OneWaySeq.create itemEquals getId + |> createBindingT /// - /// Creates a binding to a sub-model/component. You typically bind this - /// to the DataContext of a UserControl or similar. + /// Strongly-typed bindings that dispatch messages from the view. /// - /// Returns the bindings for the sub-model. - let required (bindings: unit -> Binding<'model, 'msg> list) - : string -> Binding<'model, 'msg> = - vopt bindings - >> mapModel ValueSome - - /// - /// The strongly-typed counterpart of module SubModel. - /// For creating bindings to child view models that have their own bindings. - /// Typically bound from WPF using DataContext and Binding. - /// Can be used in binding lists if boxed using . - /// - module SubModelT = - - /// Exposes an optional view model member for binding. - let opt - (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) - : (string -> Binding<'bindingModel voption, 'msg, #IViewModel<'bindingModel, 'msg>>) - = - SubModel.create createVm IViewModel.updateModel - |> createBindingT - - /// Exposes a non-optional view model member for binding. - let req - (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) - : (string -> Binding<'bindingModel, 'msg, #IViewModel<'bindingModel, 'msg>>) - = - SubModel.create createVm IViewModel.updateModel - |> createBindingT - >> mapModel ValueSome + module CmdT = + + /// + /// Elemental instance of a Command binding. + /// Creates a Command binding that only passes the CommandParameter) + /// + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + /// Indicates whether the command can execute. + let id<'model> uiBoundCmdParam canExec : string -> Binding<'model, obj, ICommand> = + Cmd.createWithParam (fun p _ -> ValueSome p) canExec uiBoundCmdParam + |> createBindingT + + /// + /// Creates a Command binding that depends only on the model (not the + /// CommandParameter). + /// + /// Indicates whether the command can execute. + /// Returns the message to dispatch. + let model canExec (exec: 'model -> 'msg) : string -> Binding<'model, 'msg, ICommand> = + id false (fun _ m -> m |> canExec) + >> mapMsgWithModel (fun _ y -> y |> exec) + >> addLazy (fun m1 m2 -> canExec m1 = canExec m2) + + /// + /// Creates a Command binding that dispatches the specified message. + /// + /// Indicates whether the command can execute. + /// The message to dispatch. + let set canExec (msg: 'msg) : string -> Binding<'model, 'msg, ICommand> = + id false (fun _ m -> m |> canExec) >> setMsg msg + + /// + /// Creates a Command binding that depends only on the model (not the + /// CommandParameter) and always executes. + /// + /// Returns the message to dispatch. + let modelAlways (exec: 'model -> 'msg) : string -> Binding<'model, 'msg, ICommand> = model (fun _ -> true) exec + + /// + /// Creates a Command binding that dispatches the specified message + /// and always executes. + /// + /// The message to dispatch. + let setAlways (msg: 'msg) : string -> Binding<'model, 'msg, ICommand> = set (fun _ -> true) msg + + module OneWay = + + /// Elemental instance of a one-way binding. + let id<'a, 'msg> : string -> Binding<'a, 'msg> = OneWay.id |> createBinding + + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let opt<'a, 'msg> : string -> Binding<'a option, 'msg> = + id >> mapModel Option.box + + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let vopt<'a, 'msg> : string -> Binding<'a voption, 'msg> = + id >> mapModel ValueOption.box + + + module OneWayToSource = + + /// Elemental instance of a one-way-to-source binding. + let id<'model, 'a> : string -> Binding<'model, 'a> = + OneWayToSource.id |> createBinding + + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let vopt<'model, 'a> : string -> Binding<'model, 'a voption> = + id<'model, obj> >> mapMsg ValueOption.unbox + + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let opt<'model, 'a> : string -> Binding<'model, 'a option> = + id<'model, obj> >> mapMsg Option.unbox + + + module TwoWay = + + /// Elemental instance of a two-way binding. + let id<'a> : string -> Binding<'a, 'a> = TwoWay.id |> createBinding + + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let vopt<'a> : string -> Binding<'a voption, 'a voption> = + id + >> mapModel ValueOption.box + >> mapMsg ValueOption.unbox + + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + let opt<'a> : string -> Binding<'a option, 'a option> = + id + >> mapModel Option.box + >> mapMsg Option.unbox + + + module SubModelSelectedItem = + + /// Creates a two-way binding to a SelectedItem-like property where + /// the ItemsSource-like property is a + /// binding. Automatically converts the dynamically created Elmish.WPF view + /// models to/from their corresponding IDs, so the Elmish user code only has + /// to work with the IDs. + /// + /// Only use this if you are unable to use some kind of SelectedValue + /// or SelectedIndex property with a normal + /// binding. This binding is less type-safe. It will throw when initializing + /// the bindings if + /// does not correspond to a binding, and it will + /// throw at runtime if the inferred 'id type does not match the + /// actual ID type used in that binding. + let vopt subModelSeqBindingName : string -> Binding<'id voption, 'id voption> = + SubModelSelectedItem.create subModelSeqBindingName + |> createBinding + >> mapModel (ValueOption.map box) + >> mapMsg (ValueOption.map unbox) + + /// Creates a two-way binding to a SelectedItem-like property where + /// the ItemsSource-like property is a + /// binding. Automatically converts the dynamically created Elmish.WPF view + /// models to/from their corresponding IDs, so the Elmish user code only has + /// to work with the IDs. + /// + /// Only use this if you are unable to use some kind of SelectedValue + /// or SelectedIndex property with a normal + /// binding. This binding is less type-safe. It will throw when initializing + /// the bindings if + /// does not correspond to a binding, and it will + /// throw at runtime if the inferred 'id type does not match the + /// actual ID type used in that binding. + let opt subModelSeqBindingName : string -> Binding<'id option, 'id option> = + vopt subModelSeqBindingName + >> mapModel ValueOption.ofOption + >> mapMsg ValueOption.toOption + + + module Cmd = + + let internal createWithParam exec canExec autoRequery = + Cmd.createWithParam exec canExec autoRequery + |> createBinding + + let internal create exec canExec = + createWithParam (fun _ -> exec) (fun _ -> canExec) false + >> addLazy (fun m1 m2 -> canExec m1 = canExec m2) + + + module OneWaySeq = + + let internal create get itemEquals getId = + OneWaySeq.create itemEquals getId + |> BindingData.mapModel get + |> createBinding + + + module SubModel = + + /// + /// Creates a binding to a sub-model/component. You typically bind this + /// to the DataContext of a UserControl or similar. + /// + /// Returns the bindings for the sub-model. + let vopt (bindings: unit -> Binding<'model, 'msg> list) : string -> Binding<'model voption, 'msg> = + SubModel.create (fun args -> DynamicViewModel<'model, 'msg>(args, bindings ())) IViewModel.updateModel + |> createBinding + + /// + /// Creates a binding to a sub-model/component. You typically bind this + /// to the DataContext of a UserControl or similar. + /// + /// Returns the bindings for the sub-model. + let opt (bindings: unit -> Binding<'model, 'msg> list) : string -> Binding<'model option, 'msg> = + vopt bindings >> mapModel ValueOption.ofOption + + /// + /// Creates a binding to a sub-model/component. You typically bind this + /// to the DataContext of a UserControl or similar. + /// + /// Returns the bindings for the sub-model. + let required (bindings: unit -> Binding<'model, 'msg> list) : string -> Binding<'model, 'msg> = + vopt bindings >> mapModel ValueSome /// - /// Exposes a 'a seq (IEnumerable<'a>) view model member for binding. - /// Used rarely; usually, you want to expose an ObservableCollection<'a> - /// using SubModelSeqKeyedT or SubModelSeqUnkeyedT. + /// The strongly-typed counterpart of module SubModel. + /// For creating bindings to child view models that have their own bindings. + /// Typically bound from WPF using DataContext and Binding. + /// Can be used in binding lists if boxed using . /// - let seq - (createVm: ViewModelArgs<'bindingModel, 'msg> -> #seq<#IViewModel<'bindingModel, 'msg>>) - : (string -> Binding<'bindingModel, 'msg, #seq<#IViewModel<'bindingModel, 'msg>>>) - = - SubModel.create createVm (fun (vms, m) -> vms |> Seq.iter (fun vm -> IViewModel.updateModel (vm, m))) - |> createBindingT - >> mapModel ValueSome - - /// - /// The strongly-typed counterpart of Binding.subModelSeq without parameter getId. - /// Exposes an ObservableCollection of child view models for binding. - /// Identifies elements by index; - /// if possible, use SubModelSeqKeyedT (which uses parameter getId) instead. - /// Typically bound from WPF using DataContext and Binding. - /// Can be used in binding lists if boxed using . - /// - module SubModelSeqUnkeyedT = + module SubModelT = + + /// Exposes an optional view model member for binding. + let opt + (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) + : (string -> Binding<'bindingModel voption, 'msg, #IViewModel<'bindingModel, 'msg>>) = + SubModel.create createVm IViewModel.updateModel + |> createBindingT + + /// Exposes a non-optional view model member for binding. + let req + (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) + : (string -> Binding<'bindingModel, 'msg, #IViewModel<'bindingModel, 'msg>>) = + SubModel.create createVm IViewModel.updateModel + |> createBindingT + >> mapModel ValueSome + + /// + /// Exposes a 'a seq (IEnumerable<'a>) view model member for binding. + /// Used rarely; usually, you want to expose an ObservableCollection<'a> + /// using SubModelSeqKeyedT or SubModelSeqUnkeyedT. + /// + let seq + (createVm: ViewModelArgs<'bindingModel, 'msg> -> #seq<#IViewModel<'bindingModel, 'msg>>) + : (string -> Binding<'bindingModel, 'msg, #seq<#IViewModel<'bindingModel, 'msg>>>) = + SubModel.create createVm (fun (vms, m) -> + vms + |> Seq.iter (fun vm -> IViewModel.updateModel (vm, m))) + |> createBindingT + >> mapModel ValueSome /// - /// Creates an elemental SubModelSeqUnkeyedT binding. + /// The strongly-typed counterpart of Binding.subModelSeq without parameter getId. + /// Exposes an ObservableCollection of child view models for binding. + /// Identifies elements by index; + /// if possible, use SubModelSeqKeyedT (which uses parameter getId) instead. + /// Typically bound from WPF using DataContext and Binding. + /// Can be used in binding lists if boxed using . /// - /// - /// The function applied to every element of the bound ObservableCollection - /// to create a child view model. - /// - let id - (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) - : (string -> Binding<'bindingModelCollection, int * 'msg, ObservableCollection<#IViewModel<'bindingModel, 'msg>>>) - = - SubModelSeqUnkeyed.create createVm IViewModel.updateModel - |> createBindingT - - /// - /// The strongly-typed counterpart of Binding.subModelSeq with parameter getId. - /// Exposes an ObservableCollection of child view models for binding. - /// Typically bound from WPF using DataContext and Binding. - /// Can be used in binding lists if boxed using . - /// - module SubModelSeqKeyedT = + module SubModelSeqUnkeyedT = + + /// + /// Creates an elemental SubModelSeqUnkeyedT binding. + /// + /// + /// The function applied to every element of the bound ObservableCollection + /// to create a child view model. + /// + let id + (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) + : (string -> Binding<'bindingModelCollection, int * 'msg, ObservableCollection<#IViewModel<'bindingModel, 'msg>>>) = + SubModelSeqUnkeyed.create createVm IViewModel.updateModel + |> createBindingT /// - /// Creates an elemental SubModelSeqUnkeyedT binding. + /// The strongly-typed counterpart of Binding.subModelSeq with parameter getId. + /// Exposes an ObservableCollection of child view models for binding. + /// Typically bound from WPF using DataContext and Binding. + /// Can be used in binding lists if boxed using . /// - /// - /// The function applied to every element of the bound ObservableCollection - /// to create a child view model. - /// - /// - /// The function applied to every element of the bound ObservableCollection - /// to get a key used to identify that element. - /// Should not return duplicate keys for different elements. - /// - let id - (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) - (getId: 'bindingModel -> 'id) - : (string -> Binding<'bindingModelCollection, 'id * 'msg, ObservableCollection<#IViewModel<'bindingModel, 'msg>>>) - = - SubModelSeqKeyed.create createVm IViewModel.updateModel getId (IViewModel.currentModel >> getId) - |> createBindingT - - /// - /// The strongly-typed counterpart of Binding.subModelWin. - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (either in code - /// or in XAML). - /// - /// Can be used in binding lists if boxed using . - /// - module SubModelWinT = + module SubModelSeqKeyedT = + + /// + /// Creates an elemental SubModelSeqUnkeyedT binding. + /// + /// + /// The function applied to every element of the bound ObservableCollection + /// to create a child view model. + /// + /// + /// The function applied to every element of the bound ObservableCollection + /// to get a key used to identify that element. + /// Should not return duplicate keys for different elements. + /// + let id + (createVm: ViewModelArgs<'bindingModel, 'msg> -> #IViewModel<'bindingModel, 'msg>) + (getId: 'bindingModel -> 'id) + : (string -> Binding<'bindingModelCollection, 'id * 'msg, ObservableCollection<#IViewModel<'bindingModel, 'msg>>>) = + SubModelSeqKeyed.create createVm IViewModel.updateModel getId (IViewModel.currentModel >> getId) + |> createBindingT /// - /// Creates an elemental SubModelWinT binding. + /// The strongly-typed counterpart of Binding.subModelWin. /// Like , but uses the WindowState wrapper /// to show/hide/close a new window that will have the specified bindings as /// its DataContext. /// /// You do not need to set the DataContext yourself (either in code /// or in XAML). - /// The window can only be closed/hidden by changing the return value of - /// , and cannot be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it - /// or displaying relevant feedback to the user). + /// + /// Can be used in binding lists if boxed using . /// - /// Gets the window state and a sub-model. - /// Returns the view model for the window. - /// - /// The function used to get and configure the window. - /// - /// - /// Specifies whether the window will be shown modally (using - /// Window.ShowDialog, blocking the rest of the app) or non-modally (using - /// Window.Show). - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - let id - (getState: 'model -> WindowState<'bindingModel>) - (createVM: ViewModelArgs<'bindingModel, 'bindingMsg> -> #IViewModel<'bindingModel, 'bindingMsg>) - getWindow isModal onCloseRequested = - SubModelWin.create getState createVM IViewModel.updateModel Func2.id2 getWindow isModal onCloseRequested - |> createBindingT - + module SubModelWinT = + + /// + /// Creates an elemental SubModelWinT binding. + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (either in code + /// or in XAML). + /// The window can only be closed/hidden by changing the return value of + /// , and cannot be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it + /// or displaying relevant feedback to the user). + /// + /// Gets the window state and a sub-model. + /// Returns the view model for the window. + /// + /// The function used to get and configure the window. + /// + /// + /// Specifies whether the window will be shown modally (using + /// Window.ShowDialog, blocking the rest of the app) or non-modally (using + /// Window.Show). + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + let id + (getState: 'model -> WindowState<'bindingModel>) + (createVM: ViewModelArgs<'bindingModel, 'bindingMsg> -> #IViewModel<'bindingModel, 'bindingMsg>) + getWindow + isModal + onCloseRequested + = + SubModelWin.create getState createVM IViewModel.updateModel Func2.id2 getWindow isModal onCloseRequested + |> createBindingT + + + module SelectedIndex = + /// Prebuilt binding intended for use with Selector.SelectedIndex. + let vopt = + TwoWay.id + >> mapModel (ValueOption.defaultValue -1) + >> mapMsg (fun i -> if i < 0 then ValueNone else ValueSome i) + + /// Prebuilt binding intended for use with Selector.SelectedIndex. + let opt = + vopt + >> mapModel ValueOption.ofOption + >> mapMsg ValueOption.toOption + + + module SubModelWin = + + let internal create getState createViewModel updateViewModel toMsg getWindow isModal onCloseRequested = + SubModelWin.create getState createViewModel updateViewModel toMsg getWindow isModal onCloseRequested + |> createBinding + + + module SubModelSeqUnkeyed = + + let internal create createViewModel updateViewModel = + SubModelSeqUnkeyed.create createViewModel updateViewModel + |> createBinding + + + module SubModelSeqKeyed = + + let internal create createViewModel updateViewModel bmToId vmToId = + SubModelSeqKeyed.create createViewModel updateViewModel bmToId vmToId + |> createBinding - module SelectedIndex = - /// Prebuilt binding intended for use with Selector.SelectedIndex. - let vopt = - TwoWay.id - >> mapModel (ValueOption.defaultValue -1) - >> mapMsg (fun i -> if i < 0 then ValueNone else ValueSome i) - - /// Prebuilt binding intended for use with Selector.SelectedIndex. - let opt = - vopt - >> mapModel ValueOption.ofOption - >> mapMsg ValueOption.toOption +module Bindings = - module SubModelWin = + /// Maps the model of a list of bindings via a contravariant mapping. + let mapModel (f: 'a -> 'b) (bindings: Binding<'b, 'msg> list) = + f |> Binding.mapModel |> List.map <| bindings - let internal create getState createViewModel updateViewModel toMsg getWindow isModal onCloseRequested = - SubModelWin.create getState createViewModel updateViewModel toMsg getWindow isModal onCloseRequested - |> createBinding + /// Maps the message of a list of bindings with access to the model via a covariant mapping. + let mapMsgWithModel (f: 'a -> 'model -> 'b) (bindings: Binding<'model, 'a> list) = + f |> Binding.mapMsgWithModel |> List.map + <| bindings + /// Maps the message of a list of bindings via a covariant mapping. + let mapMsg (f: 'a -> 'b) (bindings: Binding<'model, 'a> list) = + f |> Binding.mapMsg |> List.map <| bindings - module SubModelSeqUnkeyed = - let internal create createViewModel updateViewModel = - SubModelSeqUnkeyed.create createViewModel updateViewModel - |> createBinding +[] +type Binding private () = + /// + /// Creates a binding intended for use with Selector.SelectedIndex. + /// + /// Gets the selected index from the model. + /// Returns the message to dispatch. + static member selectedIndex(get: 'model -> int voption, set: int voption -> 'msg) = + Binding.SelectedIndex.vopt + >> Binding.mapModel get + >> Binding.mapMsg set - module SubModelSeqKeyed = + /// + /// Creates a binding intended for use with Selector.SelectedIndex. + /// + /// Gets the selected index from the model. + /// Returns the message to dispatch. + static member selectedIndex(get: 'model -> int option, set: int option -> 'msg) = + Binding.SelectedIndex.opt + >> Binding.mapModel get + >> Binding.mapMsg set - let internal create createViewModel updateViewModel bmToId vmToId = - SubModelSeqKeyed.create createViewModel updateViewModel bmToId vmToId - |> createBinding + /// Creates a one-way binding. + /// Gets the value from the model. + static member oneWay(get: 'model -> 'a) : string -> Binding<'model, 'msg> = + Binding.OneWay.id<'a, 'msg> + >> Binding.addLazy (=) + >> Binding.mapModel get -module Bindings = - /// Maps the model of a list of bindings via a contravariant mapping. - let mapModel (f: 'a -> 'b) (bindings: Binding<'b, 'msg> list) = f |> Binding.mapModel |> List.map <| bindings + /// + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between the optional source value and an + /// unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + static member oneWayOpt(get: 'model -> 'a option) : string -> Binding<'model, 'msg> = + Binding.OneWay.opt<'a, 'msg> + >> Binding.addLazy (=) + >> Binding.mapModel get - /// Maps the message of a list of bindings with access to the model via a covariant mapping. - let mapMsgWithModel (f: 'a -> 'model -> 'b) (bindings: Binding<'model, 'a> list) = f |> Binding.mapMsgWithModel |> List.map <| bindings - /// Maps the message of a list of bindings via a covariant mapping. - let mapMsg (f: 'a -> 'b) (bindings: Binding<'model, 'a> list) = f |> Binding.mapMsg |> List.map <| bindings + /// + /// Creates a one-way binding to an optional value. The binding + /// automatically converts between the optional source value and an + /// unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + static member oneWayOpt(get: 'model -> 'a voption) : string -> Binding<'model, 'msg> = + Binding.OneWay.vopt<'a, 'msg> + >> Binding.addLazy (=) + >> Binding.mapModel get -[] -type Binding private () = + /// + /// Creates a lazily evaluated one-way binding. + /// will be called only when the output of changes, + /// as determined by . This may have better + /// performance than for expensive computations (but + /// may be less performant for non-expensive functions due to additional + /// overhead). + /// + /// Gets the value from the model. + /// + /// Indicates whether two intermediate values are equal. Good candidates are + /// elmEq and refEq. + /// + /// Transforms the value into the final type. + static member oneWayLazy + ( + get: 'model -> 'a, + equals: 'a -> 'a -> bool, + map: 'a -> 'b + ) : string -> Binding<'model, 'msg> = + Binding.OneWay.id<'b, 'msg> + >> Binding.mapModel map + >> Binding.addLazy equals + >> Binding.mapModel get + >> Binding.addCaching - /// - /// Creates a binding intended for use with Selector.SelectedIndex. - /// - /// Gets the selected index from the model. - /// Returns the message to dispatch. - static member selectedIndex - (get: 'model -> int voption, - set: int voption -> 'msg) = - Binding.SelectedIndex.vopt - >> Binding.mapModel get - >> Binding.mapMsg set - - /// - /// Creates a binding intended for use with Selector.SelectedIndex. - /// - /// Gets the selected index from the model. - /// Returns the message to dispatch. - static member selectedIndex - (get: 'model -> int option, - set: int option -> 'msg) = - Binding.SelectedIndex.opt - >> Binding.mapModel get - >> Binding.mapMsg set - - - /// Creates a one-way binding. - /// Gets the value from the model. - static member oneWay - (get: 'model -> 'a) - : string -> Binding<'model, 'msg> = - Binding.OneWay.id<'a, 'msg> - >> Binding.addLazy (=) - >> Binding.mapModel get - - - /// - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between the optional source value and an - /// unwrapped (possibly - /// null) value on the view side. - /// - /// Gets the value from the model. - static member oneWayOpt - (get: 'model -> 'a option) - : string -> Binding<'model, 'msg> = - Binding.OneWay.opt<'a, 'msg> - >> Binding.addLazy (=) - >> Binding.mapModel get - - - /// - /// Creates a one-way binding to an optional value. The binding - /// automatically converts between the optional source value and an - /// unwrapped (possibly - /// null) value on the view side. - /// - /// Gets the value from the model. - static member oneWayOpt - (get: 'model -> 'a voption) - : string -> Binding<'model, 'msg> = - Binding.OneWay.vopt<'a, 'msg> - >> Binding.addLazy (=) - >> Binding.mapModel get - - - /// - /// Creates a lazily evaluated one-way binding. - /// will be called only when the output of changes, - /// as determined by . This may have better - /// performance than for expensive computations (but - /// may be less performant for non-expensive functions due to additional - /// overhead). - /// - /// Gets the value from the model. - /// - /// Indicates whether two intermediate values are equal. Good candidates are - /// elmEq and refEq. - /// - /// Transforms the value into the final type. - static member oneWayLazy - (get: 'model -> 'a, - equals: 'a -> 'a -> bool, - map: 'a -> 'b) - : string -> Binding<'model, 'msg> = - Binding.OneWay.id<'b, 'msg> - >> Binding.mapModel map - >> Binding.addLazy equals - >> Binding.mapModel get - >> Binding.addCaching - - - /// - /// Creates a lazily evaluated one-way binding to an optional value. The - /// binding automatically converts between the optional source value and an - /// unwrapped (possibly null) value on the view side. will be called only when the output of changes, as determined by . - /// - /// This may have better performance than a non-lazy binding for expensive - /// computations (but may be less performant for non-expensive functions due - /// to additional overhead). - /// - /// Gets the intermediate value from the model. - /// - /// Indicates whether two intermediate values are equal. Good candidates are - /// elmEq and refEq. - /// - /// Transforms the intermediate value into the final - /// type. - static member oneWayOptLazy - (get: 'model -> 'a, - equals: 'a -> 'a -> bool, - map: 'a -> 'b option) - : string -> Binding<'model, 'msg> = - Binding.OneWay.opt<'b, 'msg> - >> Binding.mapModel map - >> Binding.addLazy equals - >> Binding.mapModel get - >> Binding.addCaching - - - /// - /// Creates a lazily evaluated one-way binding to an optional value. The - /// binding automatically converts between the optional source value and an - /// unwrapped (possibly null) value on the view side. will be called only when the output of changes, as determined by . - /// - /// This may have better performance than a non-lazy binding for expensive - /// computations (but may be less performant for non-expensive functions due - /// to additional overhead). - /// - /// Gets the value from the model. - /// - /// Indicates whether two intermediate values are equal. Good candidates are - /// elmEq and refEq. - /// - /// Transforms the intermediate value into the final - /// type. - static member oneWayOptLazy - (get: 'model -> 'a, - equals: 'a -> 'a -> bool, - map: 'a -> 'b voption) - : string -> Binding<'model, 'msg> = - Binding.OneWay.vopt<'b, 'msg> - >> Binding.mapModel map - >> Binding.addLazy equals - >> Binding.mapModel get - >> Binding.addCaching - - - /// Creates a one-way-to-source binding. - /// Returns the message to dispatch. - static member oneWayToSource - (set: 'a -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.OneWayToSource.id<'model, 'a> - >> Binding.mapMsgWithModel set - - /// - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - /// - /// Returns the message to dispatch. - static member oneWayToSourceOpt - (set: 'a option -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.OneWayToSource.opt - >> Binding.mapMsgWithModel set - - /// - /// Creates a one-way-to-source binding to an optional value. The binding - /// automatically converts between a missing value in the model and - /// a null value in the view. - /// - /// Returns the message to dispatch. - static member oneWayToSourceOpt - (set: 'a voption -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.OneWayToSource.vopt - >> Binding.mapMsgWithModel set - - - /// - /// Creates a one-way binding to a sequence of items, each uniquely - /// identified by the value returned by . The - /// binding will not be updated if the output of - /// does not change, as determined by . - /// The binding is backed by a persistent ObservableCollection, so - /// only changed items (as determined by ) - /// will be replaced. If the items are complex and you want them updated - /// instead of replaced, consider using . - /// - /// Gets the intermediate value from the model. - /// - /// Indicates whether two intermediate values are equal. Good candidates are - /// elmEq and refEq. - /// - /// Transforms the value into the final collection. - /// - /// Indicates whether two collection items are equal. Good candidates are - /// elmEq, refEq, or simply (=). - /// - /// Gets a unique identifier for a collection - /// item. - static member oneWaySeqLazy - (get: 'model -> 'a, - equals: 'a -> 'a -> bool, - map: 'a -> #seq<'b>, - itemEquals: 'b -> 'b -> bool, - getId: 'b -> 'id) - : string -> Binding<'model, 'msg> = - Binding.OneWaySeq.create map itemEquals getId - >> Binding.addLazy equals - >> Binding.mapModel get - - - /// - /// Creates a one-way binding to a sequence of items, each uniquely - /// identified by the value returned by . The - /// binding will not be updated if the output of - /// is referentially equal. This is the same as calling - /// with equals = refEq and - /// map = id. The binding is backed by a persistent - /// ObservableCollection, so only changed items (as determined by - /// ) will be replaced. If the items are - /// complex and you want them updated instead of replaced, consider using - /// . - /// - /// Gets the collection from the model. - /// - /// Indicates whether two collection items are equal. Good candidates are - /// elmEq, refEq, or simply (=). - /// - /// Gets a unique identifier for a collection - /// item. - static member oneWaySeq - (get: 'model -> #seq<'a>, - itemEquals: 'a -> 'a -> bool, - getId: 'a -> 'id) - : string -> Binding<'model, 'msg> = - Binding.OneWaySeq.create id itemEquals getId - >> Binding.addLazy refEq - >> Binding.mapModel get - - - /// Creates a two-way binding. - /// Gets the value from the model. - /// Returns the message to dispatch. - static member twoWay - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - - /// Creates a two-way binding. - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWay - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWay (get, set) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between the optional source value and an - /// unwrapped (possibly null) value on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - static member twoWayOpt - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - - /// - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between the optional source value and an - /// unwrapped (possibly null) value on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOpt - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOpt (get, set) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between the optional source value and an - /// unwrapped (possibly null) value on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - static member twoWayOpt - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - - /// - /// Creates a two-way binding to an optional value. The binding - /// automatically converts between the optional source value and an - /// unwrapped (possibly null) value on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOpt - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOpt (get, set) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation messages from the updated model. - /// - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> string list) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation validate - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation messages from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> string list, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> string voption) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> ValueOption.toList) - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> string voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> string option) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> Option.toList) - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> string option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> Result<'ignored, string>) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> ValueOption.ofError >> ValueOption.toList) - - /// - /// Creates a two-way binding with validation using - /// INotifyDataErrorInfo. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'model -> 'msg, - validate: 'model -> Result<'ignored, string>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation messages from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> string list) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation validate - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation messages from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> string list, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> string voption) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> ValueOption.toList) - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> string voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> string option) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> Option.toList) - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> string option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> Result<'ignored, string>) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> ValueOption.ofError >> ValueOption.toList) - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'model -> 'msg, - validate: 'model -> Result<'ignored, string>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation messages from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> string list) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation validate - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation messages from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> string list, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> string voption) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> ValueOption.toList) - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> string voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> string option) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> Option.toList) - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> string option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> Result<'ignored, string>) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addValidation (validate >> ValueOption.ofError >> ValueOption.toList) - - /// - /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts between - /// the optional source value and an unwrapped (possibly null) value - /// on the view side. - /// - /// Gets the value from the model. - /// Returns the message to dispatch. - /// - /// Returns the validation message from the updated model. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'model -> 'msg, - validate: 'model -> Result<'ignored, string>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a Command binding that depends only on the model (not the - /// CommandParameter) and can always execute. - /// - /// Returns the message to dispatch. - static member cmd - (exec: 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - (exec >> ValueSome) - (fun _ -> true) - - /// - /// Creates a Command binding that depends only on the model (not the - /// CommandParameter) and can always execute. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmd - (exec: 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmd exec - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns true. - /// - /// Returns the message to dispatch. - /// Indicates whether the command can execute. - static member cmdIf - (exec: 'model -> 'msg, - canExec: 'model -> bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - (exec >> ValueSome) - canExec - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns true. - /// - /// Returns the message to dispatch. - /// Indicates whether the command can execute. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdIf - (exec: 'model -> 'msg, - canExec: 'model -> bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdIf (exec, canExec) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns ValueSome. - /// - /// Returns the message to dispatch. - static member cmdIf - (exec: 'model -> 'msg voption) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - exec - (exec >> ValueOption.isSome) - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns ValueSome. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdIf - (exec: 'model -> 'msg voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdIf exec - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns Some. - /// - /// Returns the message to dispatch. - static member cmdIf - (exec: 'model -> 'msg option) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - (exec >> ValueOption.ofOption) - (exec >> Option.isSome) - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns Some. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdIf - (exec: 'model -> 'msg option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdIf exec - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns Ok. - /// - /// This overload allows more easily re-using the same validation functions - /// for inputs and commands. - /// - /// Returns the message to dispatch. - static member cmdIf - (exec: 'model -> Result<'msg, 'ignored>) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - (exec >> ValueOption.ofOk) - (exec >> Result.isOk) - - /// - /// Creates a conditional Command binding that depends only on the - /// model (not the CommandParameter) and can execute if - /// returns Ok. - /// - /// This overload allows more easily re-using the same validation functions - /// for inputs and commands. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdIf - (exec: 'model -> Result<'msg, 'ignored>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdIf exec - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a Command binding that depends on the - /// CommandParameter - /// and can always execute. - /// - /// Returns the message to dispatch. - static member cmdParam - (exec: obj -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p model -> exec p model |> ValueSome) - (fun _ _ -> true) - false - - /// - /// Creates a Command binding that depends on the - /// CommandParameter - /// and can always execute. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParam - (exec: obj -> 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParam exec - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a Command binding that depends on the - /// CommandParameter - /// and can execute if returns true. - /// - /// Returns the message to dispatch. - /// Indicates whether the command can execute. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - static member cmdParamIf - (exec: obj -> 'model -> 'msg, - canExec: obj -> 'model -> bool, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p m -> exec p m |> ValueSome) - canExec - (defaultArg uiBoundCmdParam false) - - /// - /// Creates a Command binding that depends on the - /// CommandParameter - /// and can execute if returns true. - /// - /// Returns the message to dispatch. - /// Indicates whether the command can execute. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> 'msg, - canExec: obj -> 'model -> bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, canExec) - >> Binding.alterMsgStream wrapDispatch - - /// - /// Creates a Command binding that depends on the - /// CommandParameter - /// and can execute if returns true. - /// - /// Returns the message to dispatch. - /// Indicates whether the command can execute. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> 'msg, - canExec: obj -> 'model -> bool, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, canExec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns ValueSome. - /// - /// Returns the message to dispatch. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - static member cmdParamIf - (exec: obj -> 'model -> 'msg voption, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - exec - (fun p m -> exec p m |> ValueOption.isSome) - (defaultArg uiBoundCmdParam false) - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns ValueSome. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> 'msg voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf exec - >> Binding.alterMsgStream wrapDispatch - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns ValueSome. - /// - /// Returns the message to dispatch. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> 'msg voption, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns Some. - /// - /// Returns the message to dispatch. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - static member cmdParamIf - (exec: obj -> 'model -> 'msg option, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p m -> exec p m |> ValueOption.ofOption) - (fun p m -> exec p m |> Option.isSome) - (defaultArg uiBoundCmdParam false) - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns Some. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> 'msg option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf exec - >> Binding.alterMsgStream wrapDispatch - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns Some. - /// - /// Returns the message to dispatch. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> 'msg option, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns Ok. - /// - /// This overload allows more easily re-using the same validation functions - /// for inputs and commands. - /// - /// Returns the message to dispatch. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - static member cmdParamIf - (exec: obj -> 'model -> Result<'msg, 'ignored>, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p m -> exec p m |> ValueOption.ofOk) - (fun p m -> exec p m |> Result.isOk) - (defaultArg uiBoundCmdParam false) - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns Ok. - /// - /// This overload allows more easily re-using the same validation functions - /// for inputs and commands. - /// - /// Returns the message to dispatch. - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> Result<'msg, 'ignored>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf exec - >> Binding.alterMsgStream wrapDispatch - - /// - /// Creates a conditional Command binding that depends on the - /// CommandParameter - /// and can execute if returns Ok. - /// - /// This overload allows more easily re-using the same validation functions - /// for inputs and commands. - /// - /// Returns the message to dispatch. - /// - /// If true, CanExecuteChanged will trigger every time WPF's - /// CommandManager - /// detects UI changes that could potentially influence the command's - /// ability to execute. This will likely lead to many more triggers than - /// necessary, but is needed if you have bound the CommandParameter - /// to another UI property. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member cmdParamIf - (exec: obj -> 'model -> Result<'msg, 'ignored>, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a binding to a sub-model/component that has its own bindings and - /// message type. You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// Gets the sub-model from the model. - /// - /// Converts the models to the model used by the bindings. - /// - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - [] - static member subModel - (getSubModel: 'model -> 'subModel, - toBindingModel: 'model * 'subModel -> 'bindingModel, - toMsg: 'bindingMsg -> 'msg, - bindings: unit -> Binding<'bindingModel, 'bindingMsg> list) - : string -> Binding<'model, 'msg> = - Binding.SubModel.required bindings - >> Binding.mapModel (fun m -> toBindingModel (m, getSubModel m)) - >> Binding.mapMsg toMsg - - /// - /// Creates a binding to a sub-model/component that has its own bindings and - /// message type. You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// Gets the sub-model from the model. - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - [] - static member subModel - (getSubModel: 'model -> 'subModel, - toMsg: 'subMsg -> 'msg, - bindings: unit -> Binding<'model * 'subModel, 'subMsg> list) - : string -> Binding<'model, 'msg> = - Binding.SubModel.required bindings - >> Binding.mapModel (fun m -> (m, getSubModel m)) - >> Binding.mapMsg toMsg - - - /// - /// Creates a binding to a sub-model/component that has its own bindings. - /// You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// Gets the sub-model from the model. - /// Returns the bindings for the sub-model. - [ Binding<'model, 'msg> list\". To avoid a compile error when upgrading, replace this method call with its implementation.")>] - static member subModel - (getSubModel: 'model -> 'subModel, - bindings: unit -> Binding<'model * 'subModel, 'msg> list) - : string -> Binding<'model, 'msg> = - Binding.SubModel.required bindings - >> Binding.mapModel (fun m -> (m, getSubModel m)) - - - /// - /// Creates a binding to a sub-model/component that has its own bindings and - /// message type, and may not exist. If it does not exist, bindings to this - /// model will return null unless is - /// true, in which case the last non-null model will be - /// returned. You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// The 'sticky' part is useful if you want to e.g. animate away a - /// UserControl when the model is missing, but don't want the data - /// used by that control to be cleared once the animation starts. (The - /// animation must be triggered using another binding since this will never - /// return null.) - /// - /// Gets the sub-model from the model. - /// - /// Converts the models to the model used by the bindings. - /// - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// If true, when the model is missing, the last non-null - /// model will be returned instead of null. - /// - [] - static member subModelOpt - (getSubModel: 'model -> 'subModel voption, - toBindingModel: 'model * 'subModel -> 'bindingModel, - toMsg: 'bindingMsg -> 'msg, - bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, - ?sticky: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModel.vopt bindings - >> if (defaultArg sticky false) then Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) else id - >> Binding.mapModel (fun m -> getSubModel m |> ValueOption.map (fun sub -> toBindingModel (m, sub))) - >> Binding.mapMsg toMsg - - - /// - /// Creates a binding to a sub-model/component that has its own bindings and - /// message type, and may not exist. If it does not exist, bindings to this - /// model will return null unless is - /// true, in which case the last non-null model will be - /// returned. You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// The 'sticky' part is useful if you want to e.g. animate away a - /// UserControl when the model is missing, but don't want the data - /// used by that control to be cleared once the animation starts. (The - /// animation must be triggered using another binding since this will never - /// return null.) - /// - /// Gets the sub-model from the model. - /// - /// Converts the models to the model used by the bindings. - /// - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// If true, when the model is missing, the last non-null - /// model will be returned instead of null. - /// - [] - static member subModelOpt - (getSubModel: 'model -> 'subModel option, - toBindingModel: 'model * 'subModel -> 'bindingModel, - toMsg: 'bindingMsg -> 'msg, - bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, - ?sticky: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModel.opt bindings - >> if (defaultArg sticky false) then Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) else id - >> Binding.mapModel (fun m -> getSubModel m |> Option.map (fun sub -> toBindingModel (m, sub))) - >> Binding.mapMsg toMsg - - /// - /// Creates a binding to a sub-model/component that has its own bindings and - /// message type, and may not exist. If it does not exist, bindings to this - /// model will return null unless is - /// true, in which case the last non-null model will be - /// returned. You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// The 'sticky' part is useful if you want to e.g. animate away a - /// UserControl when the model is missing, but don't want the data - /// used by that control to be cleared once the animation starts. (The - /// animation must be triggered using another binding since this will never - /// return null.) - /// - /// Gets the sub-model from the model. - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// If true, when the model is missing, the last non-null - /// model will be returned instead of null. - /// - [] - static member subModelOpt - (getSubModel: 'model -> 'subModel voption, - toMsg: 'subMsg -> 'msg, - bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, - ?sticky: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModel.vopt bindings - >> if (defaultArg sticky false) then Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) else id - >> Binding.mapModel (fun m -> getSubModel m |> ValueOption.map (fun sub -> (m, sub))) - >> Binding.mapMsg toMsg - - - /// - /// Creates a binding to a sub-model/component that has its own bindings and - /// message type, and may not exist. If it does not exist, bindings to this - /// model will return null unless is - /// true, in which case the last non-null model will be - /// returned. You typically bind this to the DataContext of a - /// UserControl or similar. - /// - /// The 'sticky' part is useful if you want to e.g. animate away a - /// UserControl when the model is missing, but don't want the data - /// used by that control to be cleared once the animation starts. (The - /// animation must be triggered using another binding since this will never - /// return null.) - /// - /// Gets the sub-model from the model. - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// If true, when the model is missing, the last non-null - /// model will be returned instead of null. - /// - [] - static member subModelOpt - (getSubModel: 'model -> 'subModel option, - toMsg: 'subMsg -> 'msg, - bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, - ?sticky: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModel.opt bindings - >> if (defaultArg sticky false) then Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) else id - >> Binding.mapModel (fun m -> getSubModel m |> Option.map (fun sub -> (m, sub))) - >> Binding.mapMsg toMsg - - - /// - /// Creates a binding to a sub-model/component that has its own bindings, - /// and may not exist. If it does not exist, bindings to this model will - /// return null unless is true, in - /// which case the last non-null model will be returned. You - /// typically bind this to the DataContext of a UserControl or - /// similar. - /// - /// The 'sticky' part is useful if you want to e.g. animate away a - /// UserControl when the model is missing, but don't want the data - /// used by that control to be cleared once the animation starts. (The - /// animation must be triggered using another binding since this will never - /// return null.) - /// - /// Gets the sub-model from the model. - /// Returns the bindings for the sub-model. - /// - /// If true, when the model is missing, the last non-null - /// model will be returned instead of null. - /// - [ Binding<'model, 'msg> list\". To avoid a compile error when upgrading, replace this method call with (a specialization of) its implementation.")>] - static member subModelOpt - (getSubModel: 'model -> 'subModel voption, - bindings: unit -> Binding<'model * 'subModel, 'msg> list, - ?sticky: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModel.vopt bindings - >> if (defaultArg sticky false) then Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) else id - >> Binding.mapModel (fun m -> getSubModel m |> ValueOption.map (fun sub -> (m, sub))) - - - /// - /// Creates a binding to a sub-model/component that has its own bindings, - /// and may not exist. If it does not exist, bindings to this model will - /// return null unless is true, in - /// which case the last non-null model will be returned. You - /// typically bind this to the DataContext of a UserControl or - /// similar. - /// - /// The 'sticky' part is useful if you want to e.g. animate away a - /// UserControl when the model is missing, but don't want the data - /// used by that control to be cleared once the animation starts. (The - /// animation must be triggered using another binding since this will never - /// return null.) - /// - /// Gets the sub-model from the model. - /// Returns the bindings for the sub-model. - /// - /// If true, when the model is missing, the last non-null - /// model will be returned instead of null. - /// - [ Binding<'model, 'msg> list\". To avoid a compile error when upgrading, replace this method call with (a specialization of) its implementation.")>] - static member subModelOpt - (getSubModel: 'model -> 'subModel option, - bindings: unit -> Binding<'model * 'subModel, 'msg> list, - ?sticky: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModel.opt bindings - >> if (defaultArg sticky false) then Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) else id - >> Binding.mapModel (fun m -> getSubModel m |> Option.map (fun sub -> (m, sub))) - - - /// - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (neither in code - /// nor XAML). - /// - /// The window can only be closed/hidden by changing the return value of - /// , and can not be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it, - /// or displaying relevant feedback to the user.) - /// - /// If you don't need a sub-model, you can use - /// WindowState<unit> to just control the Window visibility, - /// and pass fst to . - /// - /// Gets the window state and a sub-model. - /// - /// Converts the models to the model used by the bindings. - /// - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// The function used to get and configure the window. - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - /// - /// Specifies whether the window will be shown modally (using - /// window.ShowDialog, blocking the rest of the app) or non-modally (using - /// window.Show). - /// - static member subModelWin - (getState: 'model -> WindowState<'subModel>, - toBindingModel: 'model * 'subModel -> 'bindingModel, - toMsg: 'bindingMsg -> 'msg, - bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, - getWindow: 'model -> Dispatch<'msg> -> #Window, - ?onCloseRequested: 'msg, - ?isModal: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModelWin.create - (fun m -> getState m |> WindowState.map (fun sub -> toBindingModel (m, sub))) - (fun args -> DynamicViewModel<'bindingModel, 'bindingMsg>(args, bindings ())) - IViewModel.updateModel - (fun _ -> toMsg) - (fun m d -> upcast getWindow m d) - (defaultArg isModal false) - (fun _ -> defaultArg (onCloseRequested |> Option.map ValueSome) ValueNone) - - - /// - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (neither in code - /// nor XAML). - /// - /// The window can only be closed/hidden by changing the return value of - /// , and can not be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it, - /// or displaying relevant feedback to the user.) - /// - /// If you don't need a sub-model, you can use - /// WindowState<unit> to just control the Window visibility, - /// and pass fst to . - /// - /// Gets the window state and a sub-model. - /// - /// Converts the models to the model used by the bindings. - /// - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// The function used to get and configure the window. - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - /// - /// Specifies whether the window will be shown modally (using - /// window.ShowDialog, blocking the rest of the app) or non-modally (using - /// window.Show). - /// - static member subModelWin - (getState: 'model -> WindowState<'subModel>, - toBindingModel: 'model * 'subModel -> 'bindingModel, - toMsg: 'bindingMsg -> 'msg, - bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, - getWindow: unit -> #Window, - ?onCloseRequested: 'msg, - ?isModal: bool) - : string -> Binding<'model, 'msg> = - Binding.subModelWin( - getState, - toBindingModel, - toMsg, - bindings, - (fun _ _ -> getWindow ()), - ?onCloseRequested = onCloseRequested, - ?isModal = isModal - ) - - - /// - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (neither in code - /// nor XAML). - /// - /// The window can only be closed/hidden by changing the return value of - /// , and can not be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it, - /// or displaying relevant feedback to the user.) - /// - /// Gets the window state and a sub-model. - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// The function used to get and configure the window. - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - /// - /// Specifies whether the window will be shown modally (using - /// window.ShowDialog, blocking the rest of the app) or non-modally (using - /// window.Show). - /// - static member subModelWin - (getState: 'model -> WindowState<'subModel>, - toMsg: 'subMsg -> 'msg, - bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, - getWindow: 'model -> Dispatch<'msg> -> #Window, - ?onCloseRequested: 'msg, - ?isModal: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModelWin.create - (fun m -> getState m |> WindowState.map (fun sub -> (m, sub))) - (fun args -> DynamicViewModel<'model * 'subModel, 'subMsg>(args, bindings ())) - IViewModel.updateModel - (fun _ -> toMsg) - (fun m d -> upcast getWindow m d) - (defaultArg isModal false) - (fun _ -> defaultArg (onCloseRequested |> Option.map ValueSome) ValueNone) - - - /// - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (neither in code - /// nor XAML). - /// - /// The window can only be closed/hidden by changing the return value of - /// , and can not be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it, - /// or displaying relevant feedback to the user.) - /// - /// Gets the window state and a sub-model. - /// - /// Converts the messages used in the bindings to parent model messages - /// (e.g. a parent message union case that wraps the child message type). - /// - /// Returns the bindings for the sub-model. - /// - /// The function used to get and configure the window. - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - /// - /// Specifies whether the window will be shown modally (using - /// window.ShowDialog, blocking the rest of the app) or non-modally (using - /// window.Show). - /// - static member subModelWin - (getState: 'model -> WindowState<'subModel>, - toMsg: 'subMsg -> 'msg, - bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, - getWindow: unit -> #Window, - ?onCloseRequested: 'msg, - ?isModal: bool) - : string -> Binding<'model, 'msg> = - Binding.subModelWin( - getState, - toMsg, - bindings, - (fun _ _ -> getWindow ()), - ?onCloseRequested = onCloseRequested, - ?isModal = isModal - ) - - - /// - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (neither in code - /// nor XAML). - /// - /// The window can only be closed/hidden by changing the return value of - /// , and can not be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it, - /// or displaying relevant feedback to the user.) - /// - /// Gets the window state and a sub-model. - /// Returns the bindings for the sub-model. - /// - /// The function used to get and configure the window. - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - /// - /// Specifies whether the window will be shown modally (using - /// window.ShowDialog, blocking the rest of the app) or non-modally (using - /// window.Show). - /// - static member subModelWin - (getState: 'model -> WindowState<'subModel>, - bindings: unit -> Binding<'model * 'subModel, 'msg> list, - getWindow: 'model -> Dispatch<'msg> -> #Window, - ?onCloseRequested: 'msg, - ?isModal: bool) - : string -> Binding<'model, 'msg> = - Binding.SubModelWin.create - (fun m -> getState m |> WindowState.map (fun sub -> (m, sub))) - (fun args -> DynamicViewModel<'model * 'subModel, 'msg>(args, bindings ())) - IViewModel.updateModel - (fun _ -> id) - (fun m d -> upcast getWindow m d) - (defaultArg isModal false) - (fun _ -> defaultArg (onCloseRequested |> Option.map ValueSome) ValueNone) - - - /// - /// Like , but uses the WindowState wrapper - /// to show/hide/close a new window that will have the specified bindings as - /// its DataContext. - /// - /// You do not need to set the DataContext yourself (neither in code - /// nor XAML). - /// - /// The window can only be closed/hidden by changing the return value of - /// , and can not be directly closed by the - /// user. External close attempts (the Close/X button, Alt+F4, or System - /// Menu -> Close) will cause the message specified by - /// to be dispatched. You should supply - /// and react to this in a manner that - /// will not confuse a user trying to close the window (e.g. by closing it, - /// or displaying relevant feedback to the user.) - /// - /// Gets the window state and a sub-model. - /// Returns the bindings for the sub-model. - /// - /// The function used to get and configure the window. - /// - /// - /// The message to be dispatched on external close attempts (the Close/X - /// button, Alt+F4, or System Menu -> Close). - /// - /// - /// Specifies whether the window will be shown modally (using - /// window.ShowDialog, blocking the rest of the app) or non-modally (using - /// window.Show). - /// - static member subModelWin - (getState: 'model -> WindowState<'subModel>, - bindings: unit -> Binding<'model * 'subModel, 'msg> list, - getWindow: unit -> #Window, - ?onCloseRequested: 'msg, - ?isModal: bool) - : string -> Binding<'model, 'msg> = - Binding.subModelWin( - getState, - bindings, - (fun _ _ -> getWindow ()), - ?onCloseRequested = onCloseRequested, - ?isModal = isModal - ) - - static member subModelSeq // TODO: make into function - (getBindings: unit -> Binding<'model, 'msg> list) - : string -> Binding<'model seq, int * 'msg> = - Binding.SubModelSeqUnkeyed.create - (fun args -> DynamicViewModel<'model, 'msg>(args, getBindings ())) - IViewModel.updateModel - - static member subModelSeq // TODO: make into function - (getBindings: unit -> Binding<'model, 'msg> list, - getId: 'model -> 'id) - : string -> Binding<'model seq, 'id * 'msg> = - Binding.SubModelSeqKeyed.create - (fun args -> DynamicViewModel<'model, 'msg>(args, getBindings ())) - IViewModel.updateModel - getId - (IViewModel.currentModel >> getId) - - - /// - /// Creates a binding to a sequence of sub-models, each uniquely identified - /// by the value returned by . The sub-models have - /// their own bindings and message type. You typically bind this to the - /// ItemsSource of an ItemsControl, ListView, - /// TreeView, etc. - /// - /// Gets the sub-models from the model. - /// - /// Converts the models to the model used by the bindings. - /// - /// Gets a unique identifier for a sub-model. - /// - /// Converts the sub-model ID and messages used in the bindings to parent - /// model messages (e.g. a parent message union case that wraps the - /// sub-model ID and message type). - /// - /// Returns the bindings for the sub-model. - static member subModelSeq - (getSubModels: 'model -> #seq<'subModel>, - toBindingModel: 'model * 'subModel -> 'bindingModel, - getId: 'bindingModel -> 'id, - toMsg: 'id * 'bindingMsg -> 'msg, - bindings: unit -> Binding<'bindingModel, 'bindingMsg> list) - : string -> Binding<'model, 'msg> = - Binding.SubModelSeqKeyed.create - (fun args -> DynamicViewModel<'bindingModel, 'bindingMsg>(args, bindings ())) - IViewModel.updateModel - getId - (IViewModel.currentModel >> getId) - >> Binding.mapModel (fun m -> getSubModels m |> Seq.map (fun sub -> toBindingModel (m, sub))) - >> Binding.mapMsg toMsg - - - /// - /// Creates a binding to a sequence of sub-models, each uniquely identified - /// by the value returned by . The sub-models have - /// their own bindings and message type. You typically bind this to the - /// ItemsSource of an ItemsControl, ListView, - /// TreeView, etc. - /// - /// Gets the sub-models from the model. - /// Gets a unique identifier for a sub-model. - /// - /// Converts the sub-model ID and messages used in the bindings to parent - /// model messages (e.g. a parent message union case that wraps the - /// sub-model ID and message type). - /// - /// Returns the bindings for the sub-model. - static member subModelSeq - (getSubModels: 'model -> #seq<'subModel>, - getId: 'subModel -> 'id, - toMsg: 'id * 'subMsg -> 'msg, - bindings: unit -> Binding<'model * 'subModel, 'subMsg> list) - : string -> Binding<'model, 'msg> = - Binding.SubModelSeqKeyed.create - (fun args -> DynamicViewModel<'model * 'subModel, 'subMsg>(args, bindings ())) - IViewModel.updateModel - (snd >> getId) - (IViewModel.currentModel >> snd >> getId) - >> Binding.mapModel (fun m -> getSubModels m |> Seq.map (fun sub -> (m, sub))) - >> Binding.mapMsg toMsg - - - /// - /// Creates a binding to a sequence of sub-models, each uniquely identified - /// by the value returned by . The sub-models have - /// their own bindings. You typically bind this to the ItemsSource of - /// an - /// ItemsControl, ListView, TreeView, etc. - /// - /// Gets the sub-models from the model. - /// Gets a unique identifier for a sub-model. - /// Returns the bindings for the sub-model. - static member subModelSeq - (getSubModels: 'model -> #seq<'subModel>, - getId: 'subModel -> 'id, - bindings: unit -> Binding<'model * 'subModel, 'msg> list) - : string -> Binding<'model, 'msg> = - Binding.SubModelSeqKeyed.create - (fun args -> DynamicViewModel<'model * 'subModel, 'msg>(args, bindings ())) - IViewModel.updateModel - (snd >> getId) - (IViewModel.currentModel >> snd >> getId) - >> Binding.mapModel (fun m -> getSubModels m |> Seq.map (fun sub -> (m, sub))) - >> Binding.mapMsg snd - - - /// - /// Creates a two-way binding to a SelectedItem-like property where - /// the - /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF view - /// models to/from their corresponding IDs, so the Elmish user code only has - /// to work with the IDs. - /// - /// Only use this if you are unable to use some kind of SelectedValue - /// or - /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when initializing - /// the bindings if - /// does not correspond to a binding, and it will - /// throw at runtime if the inferred 'id type does not match the - /// actual ID type used in that binding. - /// - /// - /// The name of the binding used as the items - /// source. - /// - /// Gets the selected sub-model/sub-binding ID from the - /// model. - /// - /// Returns the message to dispatch on selections/de-selections. - /// - static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id voption, - set: 'id voption -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.SubModelSelectedItem.vopt subModelSeqBindingName - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addCaching - - /// - /// Creates a two-way binding to a SelectedItem-like property where - /// the - /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF view - /// models to/from their corresponding IDs, so the Elmish user code only has - /// to work with the IDs. - /// - /// Only use this if you are unable to use some kind of SelectedValue - /// or - /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when initializing - /// the bindings if - /// does not correspond to a binding, and it will - /// throw at runtime if the inferred 'id type does not match the - /// actual ID type used in that binding. - /// - /// - /// The name of the binding used as the items - /// source. - /// - /// Gets the selected sub-model/sub-binding ID from the - /// model. - /// - /// Returns the message to dispatch on selections/de-selections. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id voption, - set: 'id voption -> 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.subModelSelectedItem (subModelSeqBindingName, get, set) - >> Binding.alterMsgStream wrapDispatch - - - /// - /// Creates a two-way binding to a SelectedItem-like property where - /// the - /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF view - /// models to/from their corresponding IDs, so the Elmish user code only has - /// to work with the IDs. - /// - /// Only use this if you are unable to use some kind of SelectedValue - /// or - /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when initializing - /// the bindings if - /// does not correspond to a binding, and it will - /// throw at runtime if the inferred 'id type does not match the - /// actual ID type used in that binding. - /// - /// - /// The name of the binding used as the items - /// source. - /// - /// Gets the selected sub-model/sub-binding ID from the - /// model. - /// - /// Returns the message to dispatch on selections/de-selections. - /// - static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id option, - set: 'id option -> 'model -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.SubModelSelectedItem.opt subModelSeqBindingName - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsgWithModel set - >> Binding.addCaching - - /// - /// Creates a two-way binding to a SelectedItem-like property where - /// the - /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF view - /// models to/from their corresponding IDs, so the Elmish user code only has - /// to work with the IDs. - /// - /// Only use this if you are unable to use some kind of SelectedValue - /// or - /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when initializing - /// the bindings if - /// does not correspond to a binding, and it will - /// throw at runtime if the inferred 'id type does not match the - /// actual ID type used in that binding. - /// - /// - /// The name of the binding used as the items - /// source. - /// - /// Gets the selected sub-model/sub-binding ID from the - /// model. - /// - /// Returns the message to dispatch on selections/de-selections. - /// - /// - /// Wraps the dispatch function with additional behavior, such as - /// throttling, debouncing, or limiting. - /// - [] - static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id option, - set: 'id option -> 'model -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.subModelSelectedItem (subModelSeqBindingName, get, set) - >> Binding.alterMsgStream wrapDispatch + /// + /// Creates a lazily evaluated one-way binding to an optional value. The + /// binding automatically converts between the optional source value and an + /// unwrapped (possibly null) value on the view side. will be called only when the output of changes, as determined by . + /// + /// This may have better performance than a non-lazy binding for expensive + /// computations (but may be less performant for non-expensive functions due + /// to additional overhead). + /// + /// Gets the intermediate value from the model. + /// + /// Indicates whether two intermediate values are equal. Good candidates are + /// elmEq and refEq. + /// + /// Transforms the intermediate value into the final + /// type. + static member oneWayOptLazy + ( + get: 'model -> 'a, + equals: 'a -> 'a -> bool, + map: 'a -> 'b option + ) : string -> Binding<'model, 'msg> = + Binding.OneWay.opt<'b, 'msg> + >> Binding.mapModel map + >> Binding.addLazy equals + >> Binding.mapModel get + >> Binding.addCaching -// Some members are implemented as extensions to help overload resolution -[] -module Extensions = + /// + /// Creates a lazily evaluated one-way binding to an optional value. The + /// binding automatically converts between the optional source value and an + /// unwrapped (possibly null) value on the view side. will be called only when the output of changes, as determined by . + /// + /// This may have better performance than a non-lazy binding for expensive + /// computations (but may be less performant for non-expensive functions due + /// to additional overhead). + /// + /// Gets the value from the model. + /// + /// Indicates whether two intermediate values are equal. Good candidates are + /// elmEq and refEq. + /// + /// Transforms the intermediate value into the final + /// type. + static member oneWayOptLazy + ( + get: 'model -> 'a, + equals: 'a -> 'a -> bool, + map: 'a -> 'b voption + ) : string -> Binding<'model, 'msg> = + Binding.OneWay.vopt<'b, 'msg> + >> Binding.mapModel map + >> Binding.addLazy equals + >> Binding.mapModel get + >> Binding.addCaching - type Binding with /// Creates a one-way-to-source binding. /// Returns the message to dispatch. - static member oneWayToSource - (set: 'a -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.OneWayToSource.id<'model, 'a> - >> Binding.mapMsg set + static member oneWayToSource(set: 'a -> 'model -> 'msg) : string -> Binding<'model, 'msg> = + Binding.OneWayToSource.id<'model, 'a> + >> Binding.mapMsgWithModel set /// /// Creates a one-way-to-source binding to an optional value. The binding @@ -2960,11 +774,9 @@ module Extensions = /// a null value in the view. /// /// Returns the message to dispatch. - static member oneWayToSourceOpt - (set: 'a option -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.OneWayToSource.opt - >> Binding.mapMsg set + static member oneWayToSourceOpt(set: 'a option -> 'model -> 'msg) : string -> Binding<'model, 'msg> = + Binding.OneWayToSource.opt + >> Binding.mapMsgWithModel set /// /// Creates a one-way-to-source binding to an optional value. The binding @@ -2972,24 +784,84 @@ module Extensions = /// a null value in the view. /// /// Returns the message to dispatch. - static member oneWayToSourceOpt - (set: 'a voption -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.OneWayToSource.vopt - >> Binding.mapMsg set + static member oneWayToSourceOpt(set: 'a voption -> 'model -> 'msg) : string -> Binding<'model, 'msg> = + Binding.OneWayToSource.vopt + >> Binding.mapMsgWithModel set + + + /// + /// Creates a one-way binding to a sequence of items, each uniquely + /// identified by the value returned by . The + /// binding will not be updated if the output of + /// does not change, as determined by . + /// The binding is backed by a persistent ObservableCollection, so + /// only changed items (as determined by ) + /// will be replaced. If the items are complex and you want them updated + /// instead of replaced, consider using . + /// + /// Gets the intermediate value from the model. + /// + /// Indicates whether two intermediate values are equal. Good candidates are + /// elmEq and refEq. + /// + /// Transforms the value into the final collection. + /// + /// Indicates whether two collection items are equal. Good candidates are + /// elmEq, refEq, or simply (=). + /// + /// Gets a unique identifier for a collection + /// item. + static member oneWaySeqLazy + ( + get: 'model -> 'a, + equals: 'a -> 'a -> bool, + map: 'a -> #seq<'b>, + itemEquals: 'b -> 'b -> bool, + getId: 'b -> 'id + ) : string -> Binding<'model, 'msg> = + Binding.OneWaySeq.create map itemEquals getId + >> Binding.addLazy equals + >> Binding.mapModel get + + + /// + /// Creates a one-way binding to a sequence of items, each uniquely + /// identified by the value returned by . The + /// binding will not be updated if the output of + /// is referentially equal. This is the same as calling + /// with equals = refEq and + /// map = id. The binding is backed by a persistent + /// ObservableCollection, so only changed items (as determined by + /// ) will be replaced. If the items are + /// complex and you want them updated instead of replaced, consider using + /// . + /// + /// Gets the collection from the model. + /// + /// Indicates whether two collection items are equal. Good candidates are + /// elmEq, refEq, or simply (=). + /// + /// Gets a unique identifier for a collection + /// item. + static member oneWaySeq + ( + get: 'model -> #seq<'a>, + itemEquals: 'a -> 'a -> bool, + getId: 'a -> 'id + ) : string -> Binding<'model, 'msg> = + Binding.OneWaySeq.create id itemEquals getId + >> Binding.addLazy refEq + >> Binding.mapModel get /// Creates a two-way binding. /// Gets the value from the model. /// Returns the message to dispatch. - static member twoWay - (get: 'model -> 'a, - set: 'a -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set + static member twoWay(get: 'model -> 'a, set: 'a -> 'model -> 'msg) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set /// Creates a two-way binding. /// Gets the value from the model. @@ -3000,12 +872,13 @@ module Extensions = /// [] static member twoWay - (get: 'model -> 'a, - set: 'a -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWay (get, set) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWay (get, set) + >> Binding.alterMsgStream wrapDispatch /// @@ -3016,13 +889,14 @@ module Extensions = /// Gets the value from the model. /// Returns the message to dispatch. static member twoWayOpt - (get: 'model -> 'a option, - set: 'a option -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set /// /// Creates a two-way binding to an optional value. The binding @@ -3037,12 +911,13 @@ module Extensions = /// [] static member twoWayOpt - (get: 'model -> 'a option, - set: 'a option -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOpt (get, set) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOpt (get, set) + >> Binding.alterMsgStream wrapDispatch /// @@ -3053,13 +928,14 @@ module Extensions = /// Gets the value from the model. /// Returns the message to dispatch. static member twoWayOpt - (get: 'model -> 'a voption, - set: 'a voption -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set /// /// Creates a two-way binding to an optional value. The binding @@ -3074,12 +950,13 @@ module Extensions = /// [] static member twoWayOpt - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOpt (get, set) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOpt (get, set) + >> Binding.alterMsgStream wrapDispatch /// @@ -3092,15 +969,16 @@ module Extensions = /// Returns the validation messages from the updated model. /// static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> string list) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation validate + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> string list + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation validate /// /// Creates a two-way binding with validation using @@ -3117,13 +995,14 @@ module Extensions = /// [] static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> string list, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> string list, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// @@ -3136,15 +1015,16 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> string voption) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> ValueOption.toList) + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> string voption + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation (validate >> ValueOption.toList) /// /// Creates a two-way binding with validation using @@ -3161,13 +1041,14 @@ module Extensions = /// [] static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> string voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> string voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// @@ -3180,15 +1061,16 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> string option) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> Option.toList) + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> string option + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation (validate >> Option.toList) /// /// Creates a two-way binding with validation using @@ -3205,13 +1087,14 @@ module Extensions = /// [] static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> string option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> string option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// @@ -3224,15 +1107,20 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> Result<'ignored, string>) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.id<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> ValueOption.ofError >> ValueOption.toList) + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> Result<'ignored, string> + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation ( + validate + >> ValueOption.ofError + >> ValueOption.toList + ) /// /// Creates a two-way binding with validation using @@ -3249,20 +1137,21 @@ module Extensions = /// [] static member twoWayValidate - (get: 'model -> 'a, - set: 'a -> 'msg, - validate: 'model -> Result<'ignored, string>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a, + set: 'a -> 'model -> 'msg, + validate: 'model -> Result<'ignored, string>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3270,21 +1159,22 @@ module Extensions = /// Returns the validation messages from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> string list) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation validate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> string list + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation validate /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3297,20 +1187,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> string list, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> string list, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3318,21 +1209,22 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> string voption) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> ValueOption.toList) + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> string voption + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation (validate >> ValueOption.toList) /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3345,20 +1237,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> string voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> string voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3366,21 +1259,22 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> string option) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> Option.toList) + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> string option + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation (validate >> Option.toList) /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3393,20 +1287,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> string option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> string option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3414,21 +1309,26 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> Result<'ignored, string>) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.vopt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> ValueOption.ofError >> ValueOption.toList) + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> Result<'ignored, string> + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation ( + validate + >> ValueOption.ofError + >> ValueOption.toList + ) /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3441,20 +1341,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a voption, - set: 'a voption -> 'msg, - validate: 'model -> Result<'ignored, string>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a voption, + set: 'a voption -> 'model -> 'msg, + validate: 'model -> Result<'ignored, string>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3462,21 +1363,22 @@ module Extensions = /// Returns the validation messages from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> string list) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation validate + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> string list + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation validate /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3489,20 +1391,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> string list, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> string list, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3510,21 +1413,22 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> string voption) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> ValueOption.toList) + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> string voption + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation (validate >> ValueOption.toList) /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3537,20 +1441,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> string voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> string voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3558,21 +1463,22 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> string option) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> Option.toList) + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> string option + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation (validate >> Option.toList) /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3585,20 +1491,21 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> string option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> string option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3606,21 +1513,26 @@ module Extensions = /// Returns the validation message from the updated model. /// static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> Result<'ignored, string>) - : string -> Binding<'model, 'msg> = - Binding.TwoWay.opt<'a> - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addValidation (validate >> ValueOption.ofError >> ValueOption.toList) + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> Result<'ignored, string> + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addValidation ( + validate + >> ValueOption.ofError + >> ValueOption.toList + ) /// /// Creates a two-way binding to an optional value with validation using - /// INotifyDataErrorInfo. The binding automatically converts - /// between the optional source value and an unwrapped (possibly - /// null) value on the view side. + /// INotifyDataErrorInfo. The binding automatically converts between + /// the optional source value and an unwrapped (possibly null) value + /// on the view side. /// /// Gets the value from the model. /// Returns the message to dispatch. @@ -3633,30 +1545,27 @@ module Extensions = /// [] static member twoWayOptValidate - (get: 'model -> 'a option, - set: 'a option -> 'msg, - validate: 'model -> Result<'ignored, string>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.twoWayOptValidate (get, set, validate) - >> Binding.alterMsgStream wrapDispatch + ( + get: 'model -> 'a option, + set: 'a option -> 'model -> 'msg, + validate: 'model -> Result<'ignored, string>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch /// - /// Creates a Command binding that dispatches the specified message - /// and can always execute. + /// Creates a Command binding that depends only on the model (not the + /// CommandParameter) and can always execute. /// /// Returns the message to dispatch. - static member cmd - (exec: 'msg) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - (fun _ -> exec |> ValueSome) - (fun _ -> true) + static member cmd(exec: 'model -> 'msg) : string -> Binding<'model, 'msg> = + Binding.Cmd.create (exec >> ValueSome) (fun _ -> true) /// - /// Creates a Command binding that dispatches the specified message - /// and can always execute. + /// Creates a Command binding that depends only on the model (not the + /// CommandParameter) and can always execute. /// /// Returns the message to dispatch. /// @@ -3665,30 +1574,30 @@ module Extensions = /// [] static member cmd - (exec: 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmd exec - >> Binding.alterMsgStream wrapDispatch + ( + exec: 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmd exec + >> Binding.alterMsgStream wrapDispatch /// - /// Creates a Command binding that dispatches the specified message - /// and can execute if returns true. + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns true. /// /// Returns the message to dispatch. /// Indicates whether the command can execute. - static member cmdIf - (exec: 'msg, - canExec: 'model -> bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.create - (fun _ -> exec |> ValueSome) - canExec + static member cmdIf(exec: 'model -> 'msg, canExec: 'model -> bool) : string -> Binding<'model, 'msg> = + Binding.Cmd.create (exec >> ValueSome) canExec /// - /// Creates a Command binding that dispatches the specified message - /// and can execute if returns true. + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns true. /// /// Returns the message to dispatch. /// Indicates whether the command can execute. @@ -3698,12 +1607,112 @@ module Extensions = /// [] static member cmdIf - (exec: 'msg, - canExec: 'model -> bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdIf (exec, canExec) - >> Binding.alterMsgStream wrapDispatch + ( + exec: 'model -> 'msg, + canExec: 'model -> bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdIf (exec, canExec) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns ValueSome. + /// + /// Returns the message to dispatch. + static member cmdIf(exec: 'model -> 'msg voption) : string -> Binding<'model, 'msg> = + Binding.Cmd.create exec (exec >> ValueOption.isSome) + + /// + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns ValueSome. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdIf + ( + exec: 'model -> 'msg voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdIf exec + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns Some. + /// + /// Returns the message to dispatch. + static member cmdIf(exec: 'model -> 'msg option) : string -> Binding<'model, 'msg> = + Binding.Cmd.create (exec >> ValueOption.ofOption) (exec >> Option.isSome) + + /// + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns Some. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdIf + ( + exec: 'model -> 'msg option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdIf exec + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns Ok. + /// + /// This overload allows more easily re-using the same validation functions + /// for inputs and commands. + /// + /// Returns the message to dispatch. + static member cmdIf(exec: 'model -> Result<'msg, 'ignored>) : string -> Binding<'model, 'msg> = + Binding.Cmd.create (exec >> ValueOption.ofOk) (exec >> Result.isOk) + + /// + /// Creates a conditional Command binding that depends only on the + /// model (not the CommandParameter) and can execute if + /// returns Ok. + /// + /// This overload allows more easily re-using the same validation functions + /// for inputs and commands. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdIf + ( + exec: 'model -> Result<'msg, 'ignored>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdIf exec + >> Binding.alterMsgStream wrapDispatch /// @@ -3712,13 +1721,8 @@ module Extensions = /// and can always execute. /// /// Returns the message to dispatch. - static member cmdParam - (exec: obj -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p _ -> exec p |> ValueSome) - (fun _ _ -> true) - false + static member cmdParam(exec: obj -> 'model -> 'msg) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam (fun p model -> exec p model |> ValueSome) (fun _ _ -> true) false /// /// Creates a Command binding that depends on the @@ -3732,19 +1736,21 @@ module Extensions = /// [] static member cmdParam - (exec: obj -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParam exec - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParam exec + >> Binding.alterMsgStream wrapDispatch /// - /// Creates a conditional Command binding that depends on the + /// Creates a Command binding that depends on the /// CommandParameter - /// and can execute if returns ValueSome. + /// and can execute if returns true. /// /// Returns the message to dispatch. + /// Indicates whether the command can execute. /// /// If true, CanExecuteChanged will trigger every time WPF's /// CommandManager @@ -3754,38 +1760,41 @@ module Extensions = /// to another UI property. /// static member cmdParamIf - (exec: obj -> 'msg voption, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p _ -> exec p) - (fun p _ -> exec p |> ValueOption.isSome) - (defaultArg uiBoundCmdParam false) + ( + exec: obj -> 'model -> 'msg, + canExec: obj -> 'model -> bool, + ?uiBoundCmdParam: bool + ) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam (fun p m -> exec p m |> ValueSome) canExec (defaultArg uiBoundCmdParam false) /// - /// Creates a conditional Command binding that depends on the + /// Creates a Command binding that depends on the /// CommandParameter - /// and can execute if returns ValueSome. + /// and can execute if returns true. /// /// Returns the message to dispatch. + /// Indicates whether the command can execute. /// /// Wraps the dispatch function with additional behavior, such as /// throttling, debouncing, or limiting. /// [] static member cmdParamIf - (exec: obj -> 'msg voption, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf exec - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg, + canExec: obj -> 'model -> bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, canExec) + >> Binding.alterMsgStream wrapDispatch /// - /// Creates a conditional Command binding that depends on the + /// Creates a Command binding that depends on the /// CommandParameter - /// and can execute if returns ValueSome. + /// and can execute if returns true. /// /// Returns the message to dispatch. + /// Indicates whether the command can execute. /// /// If true, CanExecuteChanged will trigger every time WPF's /// CommandManager @@ -3800,18 +1809,20 @@ module Extensions = /// [] static member cmdParamIf - (exec: obj -> 'msg voption, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg, + canExec: obj -> 'model -> bool, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, canExec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns Some. + /// and can execute if returns ValueSome. /// /// Returns the message to dispatch. /// @@ -3823,18 +1834,16 @@ module Extensions = /// to another UI property. /// static member cmdParamIf - (exec: obj -> 'msg option, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p _ -> exec p |> ValueOption.ofOption) - (fun p _ -> exec p |> Option.isSome) - (defaultArg uiBoundCmdParam false) + ( + exec: obj -> 'model -> 'msg voption, + ?uiBoundCmdParam: bool + ) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam exec (fun p m -> exec p m |> ValueOption.isSome) (defaultArg uiBoundCmdParam false) /// /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns Some. + /// and can execute if returns ValueSome. /// /// Returns the message to dispatch. /// @@ -3843,16 +1852,17 @@ module Extensions = /// [] static member cmdParamIf - (exec: obj -> 'msg option, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf exec - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf exec + >> Binding.alterMsgStream wrapDispatch /// /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns Some. + /// and can execute if returns ValueSome. /// /// Returns the message to dispatch. /// @@ -3869,21 +1879,19 @@ module Extensions = /// [] static member cmdParamIf - (exec: obj -> 'msg option, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg voption, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns Ok. - /// - /// This overload allows more easily re-using the same validation - /// functions for inputs and commands. + /// and can execute if returns Some. /// /// Returns the message to dispatch. /// @@ -3895,21 +1903,19 @@ module Extensions = /// to another UI property. /// static member cmdParamIf - (exec: obj -> Result<'msg, 'ignored>, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p _ -> exec p |> ValueOption.ofOk) - (fun p _ -> exec p |> Result.isOk) - (defaultArg uiBoundCmdParam false) + ( + exec: obj -> 'model -> 'msg option, + ?uiBoundCmdParam: bool + ) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam + (fun p m -> exec p m |> ValueOption.ofOption) + (fun p m -> exec p m |> Option.isSome) + (defaultArg uiBoundCmdParam false) /// /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns Ok. - /// - /// This overload allows more easily re-using the same validation - /// functions for inputs and commands. + /// and can execute if returns Some. /// /// Returns the message to dispatch. /// @@ -3918,19 +1924,17 @@ module Extensions = /// [] static member cmdParamIf - (exec: obj -> Result<'msg, 'ignored>, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf exec - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf exec + >> Binding.alterMsgStream wrapDispatch /// /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns Ok. - /// - /// This overload allows more easily re-using the same validation - /// functions for inputs and commands. + /// and can execute if returns Some. /// /// Returns the message to dispatch. /// @@ -3947,21 +1951,24 @@ module Extensions = /// [] static member cmdParamIf - (exec: obj -> Result<'msg, 'ignored>, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> 'msg option, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch /// - /// Creates a Command binding that depends on the + /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns true. + /// and can execute if returns Ok. + /// + /// This overload allows more easily re-using the same validation functions + /// for inputs and commands. /// /// Returns the message to dispatch. - /// Indicates whether the command can execute. /// /// If true, CanExecuteChanged will trigger every time WPF's /// CommandManager @@ -3971,42 +1978,46 @@ module Extensions = /// to another UI property. /// static member cmdParamIf - (exec: obj -> 'msg, - canExec: obj -> bool, - ?uiBoundCmdParam: bool) - : string -> Binding<'model, 'msg> = - Binding.Cmd.createWithParam - (fun p _ -> exec p |> ValueSome) - (fun p _ -> canExec p) - (defaultArg uiBoundCmdParam false) + ( + exec: obj -> 'model -> Result<'msg, 'ignored>, + ?uiBoundCmdParam: bool + ) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam + (fun p m -> exec p m |> ValueOption.ofOk) + (fun p m -> exec p m |> Result.isOk) + (defaultArg uiBoundCmdParam false) /// - /// Creates a Command binding that depends on the + /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns true. + /// and can execute if returns Ok. + /// + /// This overload allows more easily re-using the same validation functions + /// for inputs and commands. /// /// Returns the message to dispatch. - /// Indicates whether the command can execute. /// /// Wraps the dispatch function with additional behavior, such as /// throttling, debouncing, or limiting. /// [] static member cmdParamIf - (exec: obj -> 'msg, - canExec: obj -> bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, canExec) - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> Result<'msg, 'ignored>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf exec + >> Binding.alterMsgStream wrapDispatch /// - /// Creates a Command binding that depends on the + /// Creates a conditional Command binding that depends on the /// CommandParameter - /// and can execute if returns true. + /// and can execute if returns Ok. + /// + /// This overload allows more easily re-using the same validation functions + /// for inputs and commands. /// /// Returns the message to dispatch. - /// Indicates whether the command can execute. /// /// If true, CanExecuteChanged will trigger every time WPF's /// CommandManager @@ -4021,32 +2032,793 @@ module Extensions = /// [] static member cmdParamIf - (exec: obj -> 'msg, - canExec: obj -> bool, - uiBoundCmdParam: bool, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.cmdParamIf (exec, canExec, uiBoundCmdParam) - >> Binding.alterMsgStream wrapDispatch + ( + exec: obj -> 'model -> Result<'msg, 'ignored>, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a binding to a sub-model/component that has its own bindings and + /// message type. You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// Gets the sub-model from the model. + /// + /// Converts the models to the model used by the bindings. + /// + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + [] + static member subModel + ( + getSubModel: 'model -> 'subModel, + toBindingModel: 'model * 'subModel -> 'bindingModel, + toMsg: 'bindingMsg -> 'msg, + bindings: unit -> Binding<'bindingModel, 'bindingMsg> list + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.required bindings + >> Binding.mapModel (fun m -> toBindingModel (m, getSubModel m)) + >> Binding.mapMsg toMsg + + /// + /// Creates a binding to a sub-model/component that has its own bindings and + /// message type. You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// Gets the sub-model from the model. + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + [] + static member subModel + ( + getSubModel: 'model -> 'subModel, + toMsg: 'subMsg -> 'msg, + bindings: unit -> Binding<'model * 'subModel, 'subMsg> list + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.required bindings + >> Binding.mapModel (fun m -> (m, getSubModel m)) + >> Binding.mapMsg toMsg + + + /// + /// Creates a binding to a sub-model/component that has its own bindings. + /// You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// Gets the sub-model from the model. + /// Returns the bindings for the sub-model. + [ Binding<'model, 'msg> list\". To avoid a compile error when upgrading, replace this method call with its implementation.")>] + static member subModel + ( + getSubModel: 'model -> 'subModel, + bindings: unit -> Binding<'model * 'subModel, 'msg> list + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.required bindings + >> Binding.mapModel (fun m -> (m, getSubModel m)) + + + /// + /// Creates a binding to a sub-model/component that has its own bindings and + /// message type, and may not exist. If it does not exist, bindings to this + /// model will return null unless is + /// true, in which case the last non-null model will be + /// returned. You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// The 'sticky' part is useful if you want to e.g. animate away a + /// UserControl when the model is missing, but don't want the data + /// used by that control to be cleared once the animation starts. (The + /// animation must be triggered using another binding since this will never + /// return null.) + /// + /// Gets the sub-model from the model. + /// + /// Converts the models to the model used by the bindings. + /// + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// If true, when the model is missing, the last non-null + /// model will be returned instead of null. + /// + [] + static member subModelOpt + ( + getSubModel: 'model -> 'subModel voption, + toBindingModel: 'model * 'subModel -> 'bindingModel, + toMsg: 'bindingMsg -> 'msg, + bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, + ?sticky: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.vopt bindings + >> if (defaultArg sticky false) then + Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) + else + id + >> Binding.mapModel (fun m -> + getSubModel m + |> ValueOption.map (fun sub -> toBindingModel (m, sub))) + >> Binding.mapMsg toMsg + + + /// + /// Creates a binding to a sub-model/component that has its own bindings and + /// message type, and may not exist. If it does not exist, bindings to this + /// model will return null unless is + /// true, in which case the last non-null model will be + /// returned. You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// The 'sticky' part is useful if you want to e.g. animate away a + /// UserControl when the model is missing, but don't want the data + /// used by that control to be cleared once the animation starts. (The + /// animation must be triggered using another binding since this will never + /// return null.) + /// + /// Gets the sub-model from the model. + /// + /// Converts the models to the model used by the bindings. + /// + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// If true, when the model is missing, the last non-null + /// model will be returned instead of null. + /// + [] + static member subModelOpt + ( + getSubModel: 'model -> 'subModel option, + toBindingModel: 'model * 'subModel -> 'bindingModel, + toMsg: 'bindingMsg -> 'msg, + bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, + ?sticky: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.opt bindings + >> if (defaultArg sticky false) then + Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) + else + id + >> Binding.mapModel (fun m -> + getSubModel m + |> Option.map (fun sub -> toBindingModel (m, sub))) + >> Binding.mapMsg toMsg + + /// + /// Creates a binding to a sub-model/component that has its own bindings and + /// message type, and may not exist. If it does not exist, bindings to this + /// model will return null unless is + /// true, in which case the last non-null model will be + /// returned. You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// The 'sticky' part is useful if you want to e.g. animate away a + /// UserControl when the model is missing, but don't want the data + /// used by that control to be cleared once the animation starts. (The + /// animation must be triggered using another binding since this will never + /// return null.) + /// + /// Gets the sub-model from the model. + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// If true, when the model is missing, the last non-null + /// model will be returned instead of null. + /// + [] + static member subModelOpt + ( + getSubModel: 'model -> 'subModel voption, + toMsg: 'subMsg -> 'msg, + bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, + ?sticky: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.vopt bindings + >> if (defaultArg sticky false) then + Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) + else + id + >> Binding.mapModel (fun m -> + getSubModel m + |> ValueOption.map (fun sub -> (m, sub))) + >> Binding.mapMsg toMsg + + + /// + /// Creates a binding to a sub-model/component that has its own bindings and + /// message type, and may not exist. If it does not exist, bindings to this + /// model will return null unless is + /// true, in which case the last non-null model will be + /// returned. You typically bind this to the DataContext of a + /// UserControl or similar. + /// + /// The 'sticky' part is useful if you want to e.g. animate away a + /// UserControl when the model is missing, but don't want the data + /// used by that control to be cleared once the animation starts. (The + /// animation must be triggered using another binding since this will never + /// return null.) + /// + /// Gets the sub-model from the model. + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// If true, when the model is missing, the last non-null + /// model will be returned instead of null. + /// + [] + static member subModelOpt + ( + getSubModel: 'model -> 'subModel option, + toMsg: 'subMsg -> 'msg, + bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, + ?sticky: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.opt bindings + >> if (defaultArg sticky false) then + Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) + else + id + >> Binding.mapModel (fun m -> getSubModel m |> Option.map (fun sub -> (m, sub))) + >> Binding.mapMsg toMsg + + + /// + /// Creates a binding to a sub-model/component that has its own bindings, + /// and may not exist. If it does not exist, bindings to this model will + /// return null unless is true, in + /// which case the last non-null model will be returned. You + /// typically bind this to the DataContext of a UserControl or + /// similar. + /// + /// The 'sticky' part is useful if you want to e.g. animate away a + /// UserControl when the model is missing, but don't want the data + /// used by that control to be cleared once the animation starts. (The + /// animation must be triggered using another binding since this will never + /// return null.) + /// + /// Gets the sub-model from the model. + /// Returns the bindings for the sub-model. + /// + /// If true, when the model is missing, the last non-null + /// model will be returned instead of null. + /// + [ Binding<'model, 'msg> list\". To avoid a compile error when upgrading, replace this method call with (a specialization of) its implementation.")>] + static member subModelOpt + ( + getSubModel: 'model -> 'subModel voption, + bindings: unit -> Binding<'model * 'subModel, 'msg> list, + ?sticky: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.vopt bindings + >> if (defaultArg sticky false) then + Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) + else + id + >> Binding.mapModel (fun m -> + getSubModel m + |> ValueOption.map (fun sub -> (m, sub))) + + + /// + /// Creates a binding to a sub-model/component that has its own bindings, + /// and may not exist. If it does not exist, bindings to this model will + /// return null unless is true, in + /// which case the last non-null model will be returned. You + /// typically bind this to the DataContext of a UserControl or + /// similar. + /// + /// The 'sticky' part is useful if you want to e.g. animate away a + /// UserControl when the model is missing, but don't want the data + /// used by that control to be cleared once the animation starts. (The + /// animation must be triggered using another binding since this will never + /// return null.) + /// + /// Gets the sub-model from the model. + /// Returns the bindings for the sub-model. + /// + /// If true, when the model is missing, the last non-null + /// model will be returned instead of null. + /// + [ Binding<'model, 'msg> list\". To avoid a compile error when upgrading, replace this method call with (a specialization of) its implementation.")>] + static member subModelOpt + ( + getSubModel: 'model -> 'subModel option, + bindings: unit -> Binding<'model * 'subModel, 'msg> list, + ?sticky: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModel.opt bindings + >> if (defaultArg sticky false) then + Binding.addLazy (fun previous next -> previous.IsSome && next.IsNone) + else + id + >> Binding.mapModel (fun m -> getSubModel m |> Option.map (fun sub -> (m, sub))) + + + /// + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (neither in code + /// nor XAML). + /// + /// The window can only be closed/hidden by changing the return value of + /// , and can not be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it, + /// or displaying relevant feedback to the user.) + /// + /// If you don't need a sub-model, you can use + /// WindowState<unit> to just control the Window visibility, + /// and pass fst to . + /// + /// Gets the window state and a sub-model. + /// + /// Converts the models to the model used by the bindings. + /// + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// The function used to get and configure the window. + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + /// + /// Specifies whether the window will be shown modally (using + /// window.ShowDialog, blocking the rest of the app) or non-modally (using + /// window.Show). + /// + static member subModelWin + ( + getState: 'model -> WindowState<'subModel>, + toBindingModel: 'model * 'subModel -> 'bindingModel, + toMsg: 'bindingMsg -> 'msg, + bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, + getWindow: 'model -> Dispatch<'msg> -> #Window, + ?onCloseRequested: 'msg, + ?isModal: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModelWin.create + (fun m -> + getState m + |> WindowState.map (fun sub -> toBindingModel (m, sub))) + (fun args -> DynamicViewModel<'bindingModel, 'bindingMsg>(args, bindings ())) + IViewModel.updateModel + (fun _ -> toMsg) + (fun m d -> upcast getWindow m d) + (defaultArg isModal false) + (fun _ -> defaultArg (onCloseRequested |> Option.map ValueSome) ValueNone) + + + /// + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (neither in code + /// nor XAML). + /// + /// The window can only be closed/hidden by changing the return value of + /// , and can not be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it, + /// or displaying relevant feedback to the user.) + /// + /// If you don't need a sub-model, you can use + /// WindowState<unit> to just control the Window visibility, + /// and pass fst to . + /// + /// Gets the window state and a sub-model. + /// + /// Converts the models to the model used by the bindings. + /// + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// The function used to get and configure the window. + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + /// + /// Specifies whether the window will be shown modally (using + /// window.ShowDialog, blocking the rest of the app) or non-modally (using + /// window.Show). + /// + static member subModelWin + ( + getState: 'model -> WindowState<'subModel>, + toBindingModel: 'model * 'subModel -> 'bindingModel, + toMsg: 'bindingMsg -> 'msg, + bindings: unit -> Binding<'bindingModel, 'bindingMsg> list, + getWindow: unit -> #Window, + ?onCloseRequested: 'msg, + ?isModal: bool + ) : string -> Binding<'model, 'msg> = + Binding.subModelWin ( + getState, + toBindingModel, + toMsg, + bindings, + (fun _ _ -> getWindow ()), + ?onCloseRequested = onCloseRequested, + ?isModal = isModal + ) + + + /// + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (neither in code + /// nor XAML). + /// + /// The window can only be closed/hidden by changing the return value of + /// , and can not be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it, + /// or displaying relevant feedback to the user.) + /// + /// Gets the window state and a sub-model. + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// The function used to get and configure the window. + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + /// + /// Specifies whether the window will be shown modally (using + /// window.ShowDialog, blocking the rest of the app) or non-modally (using + /// window.Show). + /// + static member subModelWin + ( + getState: 'model -> WindowState<'subModel>, + toMsg: 'subMsg -> 'msg, + bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, + getWindow: 'model -> Dispatch<'msg> -> #Window, + ?onCloseRequested: 'msg, + ?isModal: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModelWin.create + (fun m -> + getState m + |> WindowState.map (fun sub -> (m, sub))) + (fun args -> DynamicViewModel<'model * 'subModel, 'subMsg>(args, bindings ())) + IViewModel.updateModel + (fun _ -> toMsg) + (fun m d -> upcast getWindow m d) + (defaultArg isModal false) + (fun _ -> defaultArg (onCloseRequested |> Option.map ValueSome) ValueNone) + + + /// + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (neither in code + /// nor XAML). + /// + /// The window can only be closed/hidden by changing the return value of + /// , and can not be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it, + /// or displaying relevant feedback to the user.) + /// + /// Gets the window state and a sub-model. + /// + /// Converts the messages used in the bindings to parent model messages + /// (e.g. a parent message union case that wraps the child message type). + /// + /// Returns the bindings for the sub-model. + /// + /// The function used to get and configure the window. + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + /// + /// Specifies whether the window will be shown modally (using + /// window.ShowDialog, blocking the rest of the app) or non-modally (using + /// window.Show). + /// + static member subModelWin + ( + getState: 'model -> WindowState<'subModel>, + toMsg: 'subMsg -> 'msg, + bindings: unit -> Binding<'model * 'subModel, 'subMsg> list, + getWindow: unit -> #Window, + ?onCloseRequested: 'msg, + ?isModal: bool + ) : string -> Binding<'model, 'msg> = + Binding.subModelWin ( + getState, + toMsg, + bindings, + (fun _ _ -> getWindow ()), + ?onCloseRequested = onCloseRequested, + ?isModal = isModal + ) + + + /// + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (neither in code + /// nor XAML). + /// + /// The window can only be closed/hidden by changing the return value of + /// , and can not be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it, + /// or displaying relevant feedback to the user.) + /// + /// Gets the window state and a sub-model. + /// Returns the bindings for the sub-model. + /// + /// The function used to get and configure the window. + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + /// + /// Specifies whether the window will be shown modally (using + /// window.ShowDialog, blocking the rest of the app) or non-modally (using + /// window.Show). + /// + static member subModelWin + ( + getState: 'model -> WindowState<'subModel>, + bindings: unit -> Binding<'model * 'subModel, 'msg> list, + getWindow: 'model -> Dispatch<'msg> -> #Window, + ?onCloseRequested: 'msg, + ?isModal: bool + ) : string -> Binding<'model, 'msg> = + Binding.SubModelWin.create + (fun m -> + getState m + |> WindowState.map (fun sub -> (m, sub))) + (fun args -> DynamicViewModel<'model * 'subModel, 'msg>(args, bindings ())) + IViewModel.updateModel + (fun _ -> id) + (fun m d -> upcast getWindow m d) + (defaultArg isModal false) + (fun _ -> defaultArg (onCloseRequested |> Option.map ValueSome) ValueNone) + + + /// + /// Like , but uses the WindowState wrapper + /// to show/hide/close a new window that will have the specified bindings as + /// its DataContext. + /// + /// You do not need to set the DataContext yourself (neither in code + /// nor XAML). + /// + /// The window can only be closed/hidden by changing the return value of + /// , and can not be directly closed by the + /// user. External close attempts (the Close/X button, Alt+F4, or System + /// Menu -> Close) will cause the message specified by + /// to be dispatched. You should supply + /// and react to this in a manner that + /// will not confuse a user trying to close the window (e.g. by closing it, + /// or displaying relevant feedback to the user.) + /// + /// Gets the window state and a sub-model. + /// Returns the bindings for the sub-model. + /// + /// The function used to get and configure the window. + /// + /// + /// The message to be dispatched on external close attempts (the Close/X + /// button, Alt+F4, or System Menu -> Close). + /// + /// + /// Specifies whether the window will be shown modally (using + /// window.ShowDialog, blocking the rest of the app) or non-modally (using + /// window.Show). + /// + static member subModelWin + ( + getState: 'model -> WindowState<'subModel>, + bindings: unit -> Binding<'model * 'subModel, 'msg> list, + getWindow: unit -> #Window, + ?onCloseRequested: 'msg, + ?isModal: bool + ) : string -> Binding<'model, 'msg> = + Binding.subModelWin ( + getState, + bindings, + (fun _ _ -> getWindow ()), + ?onCloseRequested = onCloseRequested, + ?isModal = isModal + ) + + static member subModelSeq + (getBindings: unit -> Binding<'model, 'msg> list) + : string -> Binding<'model seq, int * 'msg> = + Binding.SubModelSeqUnkeyed.create + (fun args -> DynamicViewModel<'model, 'msg>(args, getBindings ())) + IViewModel.updateModel + + static member subModelSeq + ( + getBindings: unit -> Binding<'model, 'msg> list, + getId: 'model -> 'id + ) : string -> Binding<'model seq, 'id * 'msg> = + Binding.SubModelSeqKeyed.create + (fun args -> DynamicViewModel<'model, 'msg>(args, getBindings ())) + IViewModel.updateModel + getId + (IViewModel.currentModel >> getId) + + + /// + /// Creates a binding to a sequence of sub-models, each uniquely identified + /// by the value returned by . The sub-models have + /// their own bindings and message type. You typically bind this to the + /// ItemsSource of an ItemsControl, ListView, + /// TreeView, etc. + /// + /// Gets the sub-models from the model. + /// + /// Converts the models to the model used by the bindings. + /// + /// Gets a unique identifier for a sub-model. + /// + /// Converts the sub-model ID and messages used in the bindings to parent + /// model messages (e.g. a parent message union case that wraps the + /// sub-model ID and message type). + /// + /// Returns the bindings for the sub-model. + static member subModelSeq + ( + getSubModels: 'model -> #seq<'subModel>, + toBindingModel: 'model * 'subModel -> 'bindingModel, + getId: 'bindingModel -> 'id, + toMsg: 'id * 'bindingMsg -> 'msg, + bindings: unit -> Binding<'bindingModel, 'bindingMsg> list + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSeqKeyed.create + (fun args -> DynamicViewModel<'bindingModel, 'bindingMsg>(args, bindings ())) + IViewModel.updateModel + getId + (IViewModel.currentModel >> getId) + >> Binding.mapModel (fun m -> + getSubModels m + |> Seq.map (fun sub -> toBindingModel (m, sub))) + >> Binding.mapMsg toMsg + + + /// + /// Creates a binding to a sequence of sub-models, each uniquely identified + /// by the value returned by . The sub-models have + /// their own bindings and message type. You typically bind this to the + /// ItemsSource of an ItemsControl, ListView, + /// TreeView, etc. + /// + /// Gets the sub-models from the model. + /// Gets a unique identifier for a sub-model. + /// + /// Converts the sub-model ID and messages used in the bindings to parent + /// model messages (e.g. a parent message union case that wraps the + /// sub-model ID and message type). + /// + /// Returns the bindings for the sub-model. + static member subModelSeq + ( + getSubModels: 'model -> #seq<'subModel>, + getId: 'subModel -> 'id, + toMsg: 'id * 'subMsg -> 'msg, + bindings: unit -> Binding<'model * 'subModel, 'subMsg> list + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSeqKeyed.create + (fun args -> DynamicViewModel<'model * 'subModel, 'subMsg>(args, bindings ())) + IViewModel.updateModel + (snd >> getId) + (IViewModel.currentModel >> snd >> getId) + >> Binding.mapModel (fun m -> getSubModels m |> Seq.map (fun sub -> (m, sub))) + >> Binding.mapMsg toMsg + + + /// + /// Creates a binding to a sequence of sub-models, each uniquely identified + /// by the value returned by . The sub-models have + /// their own bindings. You typically bind this to the ItemsSource of + /// an + /// ItemsControl, ListView, TreeView, etc. + /// + /// Gets the sub-models from the model. + /// Gets a unique identifier for a sub-model. + /// Returns the bindings for the sub-model. + static member subModelSeq + ( + getSubModels: 'model -> #seq<'subModel>, + getId: 'subModel -> 'id, + bindings: unit -> Binding<'model * 'subModel, 'msg> list + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSeqKeyed.create + (fun args -> DynamicViewModel<'model * 'subModel, 'msg>(args, bindings ())) + IViewModel.updateModel + (snd >> getId) + (IViewModel.currentModel >> snd >> getId) + >> Binding.mapModel (fun m -> getSubModels m |> Seq.map (fun sub -> (m, sub))) + >> Binding.mapMsg snd /// /// Creates a two-way binding to a SelectedItem-like property where /// the /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF - /// view models to/from their corresponding IDs, so the Elmish user code - /// only has to work with the IDs. + /// binding. Automatically converts the dynamically created Elmish.WPF view + /// models to/from their corresponding IDs, so the Elmish user code only has + /// to work with the IDs. /// - /// Only use this if you are unable to use some kind of - /// SelectedValue or + /// Only use this if you are unable to use some kind of SelectedValue + /// or /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when - /// initializing the bindings if - /// does not correspond to a binding, and it - /// will throw at runtime if the inferred 'id type does not - /// match the actual ID type used in that binding. + /// binding. This binding is less type-safe. It will throw when initializing + /// the bindings if + /// does not correspond to a binding, and it will + /// throw at runtime if the inferred 'id type does not match the + /// actual ID type used in that binding. /// /// /// The name of the binding used as the items @@ -4058,33 +2830,33 @@ module Extensions = /// Returns the message to dispatch on selections/de-selections. /// static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id voption, - set: 'id voption -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.SubModelSelectedItem.vopt subModelSeqBindingName - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addCaching + ( + subModelSeqBindingName: string, + get: 'model -> 'id voption, + set: 'id voption -> 'model -> 'msg + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSelectedItem.vopt subModelSeqBindingName + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addCaching /// /// Creates a two-way binding to a SelectedItem-like property where /// the /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF - /// view models to/from their corresponding IDs, so the Elmish user code - /// only has to work with the IDs. + /// binding. Automatically converts the dynamically created Elmish.WPF view + /// models to/from their corresponding IDs, so the Elmish user code only has + /// to work with the IDs. /// - /// Only use this if you are unable to use some kind of - /// SelectedValue or + /// Only use this if you are unable to use some kind of SelectedValue + /// or /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when - /// initializing the bindings if - /// does not correspond to a binding, and it - /// will throw at runtime if the inferred 'id type does not - /// match the actual ID type used in that binding. + /// binding. This binding is less type-safe. It will throw when initializing + /// the bindings if + /// does not correspond to a binding, and it will + /// throw at runtime if the inferred 'id type does not match the + /// actual ID type used in that binding. /// /// /// The name of the binding used as the items @@ -4101,32 +2873,32 @@ module Extensions = /// [] static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id voption, - set: 'id voption -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.subModelSelectedItem (subModelSeqBindingName, get, set) - >> Binding.alterMsgStream wrapDispatch + ( + subModelSeqBindingName: string, + get: 'model -> 'id voption, + set: 'id voption -> 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.subModelSelectedItem (subModelSeqBindingName, get, set) + >> Binding.alterMsgStream wrapDispatch /// /// Creates a two-way binding to a SelectedItem-like property where /// the /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF - /// view models to/from their corresponding IDs, so the Elmish user code - /// only has to work with the IDs. + /// binding. Automatically converts the dynamically created Elmish.WPF view + /// models to/from their corresponding IDs, so the Elmish user code only has + /// to work with the IDs. /// - /// Only use this if you are unable to use some kind of - /// SelectedValue or + /// Only use this if you are unable to use some kind of SelectedValue + /// or /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when - /// initializing the bindings if - /// does not correspond to a binding, and it - /// will throw at runtime if the inferred 'id type does not - /// match the actual ID type used in that binding. + /// binding. This binding is less type-safe. It will throw when initializing + /// the bindings if + /// does not correspond to a binding, and it will + /// throw at runtime if the inferred 'id type does not match the + /// actual ID type used in that binding. /// /// /// The name of the binding used as the items @@ -4138,33 +2910,33 @@ module Extensions = /// Returns the message to dispatch on selections/de-selections. /// static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id option, - set: 'id option -> 'msg) - : string -> Binding<'model, 'msg> = - Binding.SubModelSelectedItem.opt subModelSeqBindingName - >> Binding.addLazy (=) - >> Binding.mapModel get - >> Binding.mapMsg set - >> Binding.addCaching + ( + subModelSeqBindingName: string, + get: 'model -> 'id option, + set: 'id option -> 'model -> 'msg + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSelectedItem.opt subModelSeqBindingName + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsgWithModel set + >> Binding.addCaching /// /// Creates a two-way binding to a SelectedItem-like property where /// the /// ItemsSource-like property is a - /// binding. Automatically converts the dynamically created Elmish.WPF - /// view models to/from their corresponding IDs, so the Elmish user code - /// only has to work with the IDs. + /// binding. Automatically converts the dynamically created Elmish.WPF view + /// models to/from their corresponding IDs, so the Elmish user code only has + /// to work with the IDs. /// - /// Only use this if you are unable to use some kind of - /// SelectedValue or + /// Only use this if you are unable to use some kind of SelectedValue + /// or /// SelectedIndex property with a normal - /// binding. This binding is less type-safe. It will throw when - /// initializing the bindings if - /// does not correspond to a binding, and it - /// will throw at runtime if the inferred 'id type does not - /// match the actual ID type used in that binding. + /// binding. This binding is less type-safe. It will throw when initializing + /// the bindings if + /// does not correspond to a binding, and it will + /// throw at runtime if the inferred 'id type does not match the + /// actual ID type used in that binding. /// /// /// The name of the binding used as the items @@ -4181,10 +2953,1281 @@ module Extensions = /// [] static member subModelSelectedItem - (subModelSeqBindingName: string, - get: 'model -> 'id option, - set: 'id option -> 'msg, - wrapDispatch: Dispatch<'msg> -> Dispatch<'msg>) - : string -> Binding<'model, 'msg> = - Binding.subModelSelectedItem (subModelSeqBindingName, get, set) - >> Binding.alterMsgStream wrapDispatch + ( + subModelSeqBindingName: string, + get: 'model -> 'id option, + set: 'id option -> 'model -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.subModelSelectedItem (subModelSeqBindingName, get, set) + >> Binding.alterMsgStream wrapDispatch + + + +// Some members are implemented as extensions to help overload resolution +[] +module Extensions = + + type Binding with + + /// Creates a one-way-to-source binding. + /// Returns the message to dispatch. + static member oneWayToSource(set: 'a -> 'msg) : string -> Binding<'model, 'msg> = + Binding.OneWayToSource.id<'model, 'a> + >> Binding.mapMsg set + + /// + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + /// + /// Returns the message to dispatch. + static member oneWayToSourceOpt(set: 'a option -> 'msg) : string -> Binding<'model, 'msg> = + Binding.OneWayToSource.opt >> Binding.mapMsg set + + /// + /// Creates a one-way-to-source binding to an optional value. The binding + /// automatically converts between a missing value in the model and + /// a null value in the view. + /// + /// Returns the message to dispatch. + static member oneWayToSourceOpt(set: 'a voption -> 'msg) : string -> Binding<'model, 'msg> = + Binding.OneWayToSource.vopt >> Binding.mapMsg set + + + /// Creates a two-way binding. + /// Gets the value from the model. + /// Returns the message to dispatch. + static member twoWay(get: 'model -> 'a, set: 'a -> 'msg) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + + /// Creates a two-way binding. + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWay + ( + get: 'model -> 'a, + set: 'a -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWay (get, set) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between the optional source value and an + /// unwrapped (possibly null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + static member twoWayOpt(get: 'model -> 'a option, set: 'a option -> 'msg) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + + /// + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between the optional source value and an + /// unwrapped (possibly null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOpt + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOpt (get, set) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between the optional source value and an + /// unwrapped (possibly null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + static member twoWayOpt(get: 'model -> 'a voption, set: 'a voption -> 'msg) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + + /// + /// Creates a two-way binding to an optional value. The binding + /// automatically converts between the optional source value and an + /// unwrapped (possibly null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOpt + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOpt (get, set) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation messages from the updated model. + /// + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> string list + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation validate + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation messages from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> string list, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> string voption + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation (validate >> ValueOption.toList) + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> string voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> string option + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation (validate >> Option.toList) + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> string option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> Result<'ignored, string> + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.id<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation ( + validate + >> ValueOption.ofError + >> ValueOption.toList + ) + + /// + /// Creates a two-way binding with validation using + /// INotifyDataErrorInfo. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayValidate + ( + get: 'model -> 'a, + set: 'a -> 'msg, + validate: 'model -> Result<'ignored, string>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation messages from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> string list + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation validate + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation messages from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> string list, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> string voption + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation (validate >> ValueOption.toList) + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> string voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> string option + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation (validate >> Option.toList) + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> string option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> Result<'ignored, string> + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.vopt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation ( + validate + >> ValueOption.ofError + >> ValueOption.toList + ) + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a voption, + set: 'a voption -> 'msg, + validate: 'model -> Result<'ignored, string>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation messages from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> string list + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation validate + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation messages from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> string list, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> string voption + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation (validate >> ValueOption.toList) + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> string voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> string option + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation (validate >> Option.toList) + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> string option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> Result<'ignored, string> + ) : string -> Binding<'model, 'msg> = + Binding.TwoWay.opt<'a> + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addValidation ( + validate + >> ValueOption.ofError + >> ValueOption.toList + ) + + /// + /// Creates a two-way binding to an optional value with validation using + /// INotifyDataErrorInfo. The binding automatically converts + /// between the optional source value and an unwrapped (possibly + /// null) value on the view side. + /// + /// Gets the value from the model. + /// Returns the message to dispatch. + /// + /// Returns the validation message from the updated model. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member twoWayOptValidate + ( + get: 'model -> 'a option, + set: 'a option -> 'msg, + validate: 'model -> Result<'ignored, string>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.twoWayOptValidate (get, set, validate) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a Command binding that dispatches the specified message + /// and can always execute. + /// + /// Returns the message to dispatch. + static member cmd(exec: 'msg) : string -> Binding<'model, 'msg> = + Binding.Cmd.create (fun _ -> exec |> ValueSome) (fun _ -> true) + + /// + /// Creates a Command binding that dispatches the specified message + /// and can always execute. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmd + ( + exec: 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmd exec + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a Command binding that dispatches the specified message + /// and can execute if returns true. + /// + /// Returns the message to dispatch. + /// Indicates whether the command can execute. + static member cmdIf(exec: 'msg, canExec: 'model -> bool) : string -> Binding<'model, 'msg> = + Binding.Cmd.create (fun _ -> exec |> ValueSome) canExec + + /// + /// Creates a Command binding that dispatches the specified message + /// and can execute if returns true. + /// + /// Returns the message to dispatch. + /// Indicates whether the command can execute. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdIf + ( + exec: 'msg, + canExec: 'model -> bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdIf (exec, canExec) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a Command binding that depends on the + /// CommandParameter + /// and can always execute. + /// + /// Returns the message to dispatch. + static member cmdParam(exec: obj -> 'msg) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam (fun p _ -> exec p |> ValueSome) (fun _ _ -> true) false + + /// + /// Creates a Command binding that depends on the + /// CommandParameter + /// and can always execute. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParam + ( + exec: obj -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParam exec + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns ValueSome. + /// + /// Returns the message to dispatch. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + static member cmdParamIf(exec: obj -> 'msg voption, ?uiBoundCmdParam: bool) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam + (fun p _ -> exec p) + (fun p _ -> exec p |> ValueOption.isSome) + (defaultArg uiBoundCmdParam false) + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns ValueSome. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> 'msg voption, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf exec + >> Binding.alterMsgStream wrapDispatch + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns ValueSome. + /// + /// Returns the message to dispatch. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> 'msg voption, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns Some. + /// + /// Returns the message to dispatch. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + static member cmdParamIf(exec: obj -> 'msg option, ?uiBoundCmdParam: bool) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam + (fun p _ -> exec p |> ValueOption.ofOption) + (fun p _ -> exec p |> Option.isSome) + (defaultArg uiBoundCmdParam false) + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns Some. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> 'msg option, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf exec + >> Binding.alterMsgStream wrapDispatch + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns Some. + /// + /// Returns the message to dispatch. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> 'msg option, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns Ok. + /// + /// This overload allows more easily re-using the same validation + /// functions for inputs and commands. + /// + /// Returns the message to dispatch. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + static member cmdParamIf + ( + exec: obj -> Result<'msg, 'ignored>, + ?uiBoundCmdParam: bool + ) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam + (fun p _ -> exec p |> ValueOption.ofOk) + (fun p _ -> exec p |> Result.isOk) + (defaultArg uiBoundCmdParam false) + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns Ok. + /// + /// This overload allows more easily re-using the same validation + /// functions for inputs and commands. + /// + /// Returns the message to dispatch. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> Result<'msg, 'ignored>, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf exec + >> Binding.alterMsgStream wrapDispatch + + /// + /// Creates a conditional Command binding that depends on the + /// CommandParameter + /// and can execute if returns Ok. + /// + /// This overload allows more easily re-using the same validation + /// functions for inputs and commands. + /// + /// Returns the message to dispatch. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> Result<'msg, 'ignored>, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a Command binding that depends on the + /// CommandParameter + /// and can execute if returns true. + /// + /// Returns the message to dispatch. + /// Indicates whether the command can execute. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + static member cmdParamIf + ( + exec: obj -> 'msg, + canExec: obj -> bool, + ?uiBoundCmdParam: bool + ) : string -> Binding<'model, 'msg> = + Binding.Cmd.createWithParam + (fun p _ -> exec p |> ValueSome) + (fun p _ -> canExec p) + (defaultArg uiBoundCmdParam false) + + /// + /// Creates a Command binding that depends on the + /// CommandParameter + /// and can execute if returns true. + /// + /// Returns the message to dispatch. + /// Indicates whether the command can execute. + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> 'msg, + canExec: obj -> bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, canExec) + >> Binding.alterMsgStream wrapDispatch + + /// + /// Creates a Command binding that depends on the + /// CommandParameter + /// and can execute if returns true. + /// + /// Returns the message to dispatch. + /// Indicates whether the command can execute. + /// + /// If true, CanExecuteChanged will trigger every time WPF's + /// CommandManager + /// detects UI changes that could potentially influence the command's + /// ability to execute. This will likely lead to many more triggers than + /// necessary, but is needed if you have bound the CommandParameter + /// to another UI property. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member cmdParamIf + ( + exec: obj -> 'msg, + canExec: obj -> bool, + uiBoundCmdParam: bool, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.cmdParamIf (exec, canExec, uiBoundCmdParam) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to a SelectedItem-like property where + /// the + /// ItemsSource-like property is a + /// binding. Automatically converts the dynamically created Elmish.WPF + /// view models to/from their corresponding IDs, so the Elmish user code + /// only has to work with the IDs. + /// + /// Only use this if you are unable to use some kind of + /// SelectedValue or + /// SelectedIndex property with a normal + /// binding. This binding is less type-safe. It will throw when + /// initializing the bindings if + /// does not correspond to a binding, and it + /// will throw at runtime if the inferred 'id type does not + /// match the actual ID type used in that binding. + /// + /// + /// The name of the binding used as the items + /// source. + /// + /// Gets the selected sub-model/sub-binding ID from the + /// model. + /// + /// Returns the message to dispatch on selections/de-selections. + /// + static member subModelSelectedItem + ( + subModelSeqBindingName: string, + get: 'model -> 'id voption, + set: 'id voption -> 'msg + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSelectedItem.vopt subModelSeqBindingName + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addCaching + + /// + /// Creates a two-way binding to a SelectedItem-like property where + /// the + /// ItemsSource-like property is a + /// binding. Automatically converts the dynamically created Elmish.WPF + /// view models to/from their corresponding IDs, so the Elmish user code + /// only has to work with the IDs. + /// + /// Only use this if you are unable to use some kind of + /// SelectedValue or + /// SelectedIndex property with a normal + /// binding. This binding is less type-safe. It will throw when + /// initializing the bindings if + /// does not correspond to a binding, and it + /// will throw at runtime if the inferred 'id type does not + /// match the actual ID type used in that binding. + /// + /// + /// The name of the binding used as the items + /// source. + /// + /// Gets the selected sub-model/sub-binding ID from the + /// model. + /// + /// Returns the message to dispatch on selections/de-selections. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member subModelSelectedItem + ( + subModelSeqBindingName: string, + get: 'model -> 'id voption, + set: 'id voption -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.subModelSelectedItem (subModelSeqBindingName, get, set) + >> Binding.alterMsgStream wrapDispatch + + + /// + /// Creates a two-way binding to a SelectedItem-like property where + /// the + /// ItemsSource-like property is a + /// binding. Automatically converts the dynamically created Elmish.WPF + /// view models to/from their corresponding IDs, so the Elmish user code + /// only has to work with the IDs. + /// + /// Only use this if you are unable to use some kind of + /// SelectedValue or + /// SelectedIndex property with a normal + /// binding. This binding is less type-safe. It will throw when + /// initializing the bindings if + /// does not correspond to a binding, and it + /// will throw at runtime if the inferred 'id type does not + /// match the actual ID type used in that binding. + /// + /// + /// The name of the binding used as the items + /// source. + /// + /// Gets the selected sub-model/sub-binding ID from the + /// model. + /// + /// Returns the message to dispatch on selections/de-selections. + /// + static member subModelSelectedItem + ( + subModelSeqBindingName: string, + get: 'model -> 'id option, + set: 'id option -> 'msg + ) : string -> Binding<'model, 'msg> = + Binding.SubModelSelectedItem.opt subModelSeqBindingName + >> Binding.addLazy (=) + >> Binding.mapModel get + >> Binding.mapMsg set + >> Binding.addCaching + + /// + /// Creates a two-way binding to a SelectedItem-like property where + /// the + /// ItemsSource-like property is a + /// binding. Automatically converts the dynamically created Elmish.WPF + /// view models to/from their corresponding IDs, so the Elmish user code + /// only has to work with the IDs. + /// + /// Only use this if you are unable to use some kind of + /// SelectedValue or + /// SelectedIndex property with a normal + /// binding. This binding is less type-safe. It will throw when + /// initializing the bindings if + /// does not correspond to a binding, and it + /// will throw at runtime if the inferred 'id type does not + /// match the actual ID type used in that binding. + /// + /// + /// The name of the binding used as the items + /// source. + /// + /// Gets the selected sub-model/sub-binding ID from the + /// model. + /// + /// Returns the message to dispatch on selections/de-selections. + /// + /// + /// Wraps the dispatch function with additional behavior, such as + /// throttling, debouncing, or limiting. + /// + [] + static member subModelSelectedItem + ( + subModelSeqBindingName: string, + get: 'model -> 'id option, + set: 'id option -> 'msg, + wrapDispatch: Dispatch<'msg> -> Dispatch<'msg> + ) : string -> Binding<'model, 'msg> = + Binding.subModelSelectedItem (subModelSeqBindingName, get, set) + >> Binding.alterMsgStream wrapDispatch diff --git a/src/Samples/Samples.generated.sln b/src/Samples/Samples.generated.sln index 67ad07ba..a8dded5b 100644 --- a/src/Samples/Samples.generated.sln +++ b/src/Samples/Samples.generated.sln @@ -3,39 +3,39 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capabilities", "Capabilities\Capabilities.csproj", "{50BE7B69-76EF-4A2F-956D-040F62645E48}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capabilities", "Capabilities\Capabilities.csproj", "{11D2A55F-9315-4538-B3E4-44868816E81C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBindingsAndBehaviors", "EventBindingsAndBehaviors\EventBindingsAndBehaviors.csproj", "{5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBindingsAndBehaviors", "EventBindingsAndBehaviors\EventBindingsAndBehaviors.csproj", "{26B784E2-4A97-49CB-9DD2-AD23F85A999D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDialogs", "FileDialogs\FileDialogs.csproj", "{57352DDA-A661-46B8-A0FE-232A8C72BC1A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDialogs", "FileDialogs\FileDialogs.csproj", "{49005859-27DB-4A96-B80F-2C15AFF1A828}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDialogsCmdMsg", "FileDialogsCmdMsg\FileDialogsCmdMsg.csproj", "{A1A57765-4218-42C2-9A0B-9DD20945EEE6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileDialogsCmdMsg", "FileDialogsCmdMsg\FileDialogsCmdMsg.csproj", "{1E366BD6-1750-4150-A6AC-2615551E0E51}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiselect", "Multiselect\Multiselect.csproj", "{7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiselect", "Multiselect\Multiselect.csproj", "{E81053F1-ACE1-4439-BA6A-162E3EA4E407}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewWindow", "NewWindow\NewWindow.csproj", "{8CD66A83-01AD-433E-B41A-E7772C1A20C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewWindow", "NewWindow\NewWindow.csproj", "{F6734731-8849-4D1E-A1CF-95CF091E3EC3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneWaySeq", "OneWaySeq\OneWaySeq.csproj", "{E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneWaySeq", "OneWaySeq\OneWaySeq.csproj", "{C2932EB2-16DF-48D8-8044-4A517815AF50}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SingleCounter", "SingleCounter\SingleCounter.csproj", "{38238E19-8FC9-4E61-B6E0-4AD42B077574}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SingleCounter", "SingleCounter\SingleCounter.csproj", "{96A6B550-E41C-4029-9D24-D1B1AD60954B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sticky", "Sticky\Sticky.csproj", "{097E0802-A94D-4599-84B8-6935EA9FCB01}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sticky", "Sticky\Sticky.csproj", "{0E7F8A17-5F75-434C-B0A7-EC83783E9BB8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModel", "SubModel\SubModel.csproj", "{AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModel", "SubModel\SubModel.csproj", "{6D0A8947-5A9D-4A50-AC0C-D797EE87640B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelOpt", "SubModelOpt\SubModelOpt.csproj", "{CC68E5BA-B417-4142-ACF4-4AB267904082}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelOpt", "SubModelOpt\SubModelOpt.csproj", "{F0069157-09E3-40DD-97D6-B5A3D3C42C8B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelSelectedItem", "SubModelSelectedItem\SubModelSelectedItem.csproj", "{C7CF229D-D0A0-402B-818F-999A7F08725E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelSelectedItem", "SubModelSelectedItem\SubModelSelectedItem.csproj", "{FC4518FB-73DD-480B-B652-61F8CD20255B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelSeq", "SubModelSeq\SubModelSeq.csproj", "{13684CFF-E918-47E5-8983-C68A2CB466B7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelSeq", "SubModelSeq\SubModelSeq.csproj", "{5B197AC6-9D9A-46A9-B3E8-DC33AB855159}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelStatic", "SubModelStatic\SubModelStatic.csproj", "{61E5C65D-796D-44CF-BFFC-2E7F66681DA2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SubModelStatic", "SubModelStatic\SubModelStatic.csproj", "{D345CFDA-A4C8-4FFD-A1BD-97BBB4291189}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Threading", "Threading\Threading.csproj", "{9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Threading", "Threading\Threading.csproj", "{1E9C54A2-B9DD-4F97-85B9-0DF52FD3202A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiBoundCmdParam", "UiBoundCmdParam\UiBoundCmdParam.csproj", "{A1D12300-4F10-42E6-907D-C98D84599FF7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UiBoundCmdParam", "UiBoundCmdParam\UiBoundCmdParam.csproj", "{C75025E1-20AA-4F16-820E-E7B184D7DFBC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Validation", "Validation\Validation.csproj", "{33A5664F-B468-444B-BB9C-91D6ACF2FCE7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Validation", "Validation\Validation.csproj", "{1B62316F-F093-443F-ACF4-68CC21E89E23}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -43,74 +43,74 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50BE7B69-76EF-4A2F-956D-040F62645E48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50BE7B69-76EF-4A2F-956D-040F62645E48}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50BE7B69-76EF-4A2F-956D-040F62645E48}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50BE7B69-76EF-4A2F-956D-040F62645E48}.Release|Any CPU.Build.0 = Release|Any CPU - {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5389CE5E-4D9E-4A86-BFF3-B6721BBA4D0E}.Release|Any CPU.Build.0 = Release|Any CPU - {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {57352DDA-A661-46B8-A0FE-232A8C72BC1A}.Release|Any CPU.Build.0 = Release|Any CPU - {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1A57765-4218-42C2-9A0B-9DD20945EEE6}.Release|Any CPU.Build.0 = Release|Any CPU - {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7C14D91F-D43A-4A14-B076-F3D16BFD3B8A}.Release|Any CPU.Build.0 = Release|Any CPU - {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CD66A83-01AD-433E-B41A-E7772C1A20C5}.Release|Any CPU.Build.0 = Release|Any CPU - {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E69B6B61-6056-4B4D-BC9A-03F41C7A9EDC}.Release|Any CPU.Build.0 = Release|Any CPU - {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Debug|Any CPU.Build.0 = Debug|Any CPU - {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Release|Any CPU.ActiveCfg = Release|Any CPU - {38238E19-8FC9-4E61-B6E0-4AD42B077574}.Release|Any CPU.Build.0 = Release|Any CPU - {097E0802-A94D-4599-84B8-6935EA9FCB01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {097E0802-A94D-4599-84B8-6935EA9FCB01}.Debug|Any CPU.Build.0 = Debug|Any CPU - {097E0802-A94D-4599-84B8-6935EA9FCB01}.Release|Any CPU.ActiveCfg = Release|Any CPU - {097E0802-A94D-4599-84B8-6935EA9FCB01}.Release|Any CPU.Build.0 = Release|Any CPU - {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AFD3D3BC-2229-4D5C-9F37-E4C5594CFD36}.Release|Any CPU.Build.0 = Release|Any CPU - {CC68E5BA-B417-4142-ACF4-4AB267904082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CC68E5BA-B417-4142-ACF4-4AB267904082}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CC68E5BA-B417-4142-ACF4-4AB267904082}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC68E5BA-B417-4142-ACF4-4AB267904082}.Release|Any CPU.Build.0 = Release|Any CPU - {C7CF229D-D0A0-402B-818F-999A7F08725E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7CF229D-D0A0-402B-818F-999A7F08725E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C7CF229D-D0A0-402B-818F-999A7F08725E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7CF229D-D0A0-402B-818F-999A7F08725E}.Release|Any CPU.Build.0 = Release|Any CPU - {13684CFF-E918-47E5-8983-C68A2CB466B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13684CFF-E918-47E5-8983-C68A2CB466B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13684CFF-E918-47E5-8983-C68A2CB466B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13684CFF-E918-47E5-8983-C68A2CB466B7}.Release|Any CPU.Build.0 = Release|Any CPU - {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {61E5C65D-796D-44CF-BFFC-2E7F66681DA2}.Release|Any CPU.Build.0 = Release|Any CPU - {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F874D8C-C33C-42D9-AAD6-CC94CC1DC177}.Release|Any CPU.Build.0 = Release|Any CPU - {A1D12300-4F10-42E6-907D-C98D84599FF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1D12300-4F10-42E6-907D-C98D84599FF7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1D12300-4F10-42E6-907D-C98D84599FF7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1D12300-4F10-42E6-907D-C98D84599FF7}.Release|Any CPU.Build.0 = Release|Any CPU - {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {33A5664F-B468-444B-BB9C-91D6ACF2FCE7}.Release|Any CPU.Build.0 = Release|Any CPU + {11D2A55F-9315-4538-B3E4-44868816E81C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11D2A55F-9315-4538-B3E4-44868816E81C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11D2A55F-9315-4538-B3E4-44868816E81C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11D2A55F-9315-4538-B3E4-44868816E81C}.Release|Any CPU.Build.0 = Release|Any CPU + {26B784E2-4A97-49CB-9DD2-AD23F85A999D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26B784E2-4A97-49CB-9DD2-AD23F85A999D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26B784E2-4A97-49CB-9DD2-AD23F85A999D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26B784E2-4A97-49CB-9DD2-AD23F85A999D}.Release|Any CPU.Build.0 = Release|Any CPU + {49005859-27DB-4A96-B80F-2C15AFF1A828}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49005859-27DB-4A96-B80F-2C15AFF1A828}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49005859-27DB-4A96-B80F-2C15AFF1A828}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49005859-27DB-4A96-B80F-2C15AFF1A828}.Release|Any CPU.Build.0 = Release|Any CPU + {1E366BD6-1750-4150-A6AC-2615551E0E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E366BD6-1750-4150-A6AC-2615551E0E51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E366BD6-1750-4150-A6AC-2615551E0E51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E366BD6-1750-4150-A6AC-2615551E0E51}.Release|Any CPU.Build.0 = Release|Any CPU + {E81053F1-ACE1-4439-BA6A-162E3EA4E407}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E81053F1-ACE1-4439-BA6A-162E3EA4E407}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E81053F1-ACE1-4439-BA6A-162E3EA4E407}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E81053F1-ACE1-4439-BA6A-162E3EA4E407}.Release|Any CPU.Build.0 = Release|Any CPU + {F6734731-8849-4D1E-A1CF-95CF091E3EC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6734731-8849-4D1E-A1CF-95CF091E3EC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6734731-8849-4D1E-A1CF-95CF091E3EC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6734731-8849-4D1E-A1CF-95CF091E3EC3}.Release|Any CPU.Build.0 = Release|Any CPU + {C2932EB2-16DF-48D8-8044-4A517815AF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2932EB2-16DF-48D8-8044-4A517815AF50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2932EB2-16DF-48D8-8044-4A517815AF50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2932EB2-16DF-48D8-8044-4A517815AF50}.Release|Any CPU.Build.0 = Release|Any CPU + {96A6B550-E41C-4029-9D24-D1B1AD60954B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96A6B550-E41C-4029-9D24-D1B1AD60954B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96A6B550-E41C-4029-9D24-D1B1AD60954B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96A6B550-E41C-4029-9D24-D1B1AD60954B}.Release|Any CPU.Build.0 = Release|Any CPU + {0E7F8A17-5F75-434C-B0A7-EC83783E9BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E7F8A17-5F75-434C-B0A7-EC83783E9BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E7F8A17-5F75-434C-B0A7-EC83783E9BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E7F8A17-5F75-434C-B0A7-EC83783E9BB8}.Release|Any CPU.Build.0 = Release|Any CPU + {6D0A8947-5A9D-4A50-AC0C-D797EE87640B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D0A8947-5A9D-4A50-AC0C-D797EE87640B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D0A8947-5A9D-4A50-AC0C-D797EE87640B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D0A8947-5A9D-4A50-AC0C-D797EE87640B}.Release|Any CPU.Build.0 = Release|Any CPU + {F0069157-09E3-40DD-97D6-B5A3D3C42C8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0069157-09E3-40DD-97D6-B5A3D3C42C8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0069157-09E3-40DD-97D6-B5A3D3C42C8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0069157-09E3-40DD-97D6-B5A3D3C42C8B}.Release|Any CPU.Build.0 = Release|Any CPU + {FC4518FB-73DD-480B-B652-61F8CD20255B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC4518FB-73DD-480B-B652-61F8CD20255B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC4518FB-73DD-480B-B652-61F8CD20255B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC4518FB-73DD-480B-B652-61F8CD20255B}.Release|Any CPU.Build.0 = Release|Any CPU + {5B197AC6-9D9A-46A9-B3E8-DC33AB855159}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B197AC6-9D9A-46A9-B3E8-DC33AB855159}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B197AC6-9D9A-46A9-B3E8-DC33AB855159}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B197AC6-9D9A-46A9-B3E8-DC33AB855159}.Release|Any CPU.Build.0 = Release|Any CPU + {D345CFDA-A4C8-4FFD-A1BD-97BBB4291189}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D345CFDA-A4C8-4FFD-A1BD-97BBB4291189}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D345CFDA-A4C8-4FFD-A1BD-97BBB4291189}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D345CFDA-A4C8-4FFD-A1BD-97BBB4291189}.Release|Any CPU.Build.0 = Release|Any CPU + {1E9C54A2-B9DD-4F97-85B9-0DF52FD3202A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E9C54A2-B9DD-4F97-85B9-0DF52FD3202A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E9C54A2-B9DD-4F97-85B9-0DF52FD3202A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E9C54A2-B9DD-4F97-85B9-0DF52FD3202A}.Release|Any CPU.Build.0 = Release|Any CPU + {C75025E1-20AA-4F16-820E-E7B184D7DFBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C75025E1-20AA-4F16-820E-E7B184D7DFBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C75025E1-20AA-4F16-820E-E7B184D7DFBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C75025E1-20AA-4F16-820E-E7B184D7DFBC}.Release|Any CPU.Build.0 = Release|Any CPU + {1B62316F-F093-443F-ACF4-68CC21E89E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B62316F-F093-443F-ACF4-68CC21E89E23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B62316F-F093-443F-ACF4-68CC21E89E23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B62316F-F093-443F-ACF4-68CC21E89E23}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs b/src/Samples/SubModelSelectedItem.Core/FsWPF.fs deleted file mode 100644 index 67e8e06f..00000000 --- a/src/Samples/SubModelSelectedItem.Core/FsWPF.fs +++ /dev/null @@ -1,55 +0,0 @@ -namespace FsWPF - -open System -open System.Windows.Data -open System.Globalization -open System.Windows -open System.Windows.Controls -open Elmish.WPF.Samples.SubModelSelectedItem.Program -open Form - -type ComponentsTemplateSelector() = - inherit DataTemplateSelector() - - member val TextBox_DT: DataTemplate = null with get, set - member val CheckBox_DT: DataTemplate = null with get, set - member val ComboBox_DT: DataTemplate = null with get, set - - override this.SelectTemplate(item: obj, container: DependencyObject) : DataTemplate = - match item with - | :? Form.Components as component_ -> - match component_ with - | Form.Components.TextBox _ -> this.TextBox_DT - | Form.Components.CheckBox _ -> this.CheckBox_DT - | Form.Components.ComboBox _ -> this.ComboBox_DT - | _ -> null - - -// not working; idea taken from "https://github.com/elmish/Elmish.WPF/issues/270#issuecomment-687329493" -type GetComponentsVMToResource() = - let toVM = - function - | TextBox a -> a |> box - | CheckBox a -> a |> box - | ComboBox a -> a |> box - - interface IValueConverter with - member this.Convert - ( - value: obj, - targetType: System.Type, - parameter: obj, - culture: System.Globalization.CultureInfo - ) : obj = - match value with - | :? list as components -> components |> List.map toVM |> box - | _ -> failwith "shouldn't happen" - - member this.ConvertBack - ( - value: obj, - targetType: System.Type, - parameter: obj, - culture: System.Globalization.CultureInfo - ) : obj = - raise (System.NotImplementedException()) \ No newline at end of file diff --git a/src/Samples/SubModelSelectedItem.Core/Program.fs b/src/Samples/SubModelSelectedItem.Core/Program.fs index a5f95fd0..e60b1310 100644 --- a/src/Samples/SubModelSelectedItem.Core/Program.fs +++ b/src/Samples/SubModelSelectedItem.Core/Program.fs @@ -1,347 +1,512 @@ -namespace Elmish.WPF.Samples.SubModelSelectedItem.Program +namespace Program open System +open Elmish +open Elmish.WPF open Serilog open Serilog.Extensions.Logging -open Elmish.WPF +open System.Windows.Media -(* -[toDos] - - ("#" = issue, "•" = task) - # change all [dynamic bindings] to [static bindings] (using an upcoming Elmish.WPF revised static bindings approach?) - • static bindings: make ViewModels for each Component (also working Intellisense for binding submodel in Xalm) - • [?] would something other than "SubModelSelectItem" be a better option? - - # not working: DataTemplateSelector - • add: get focus after adding + selecting FormComponent (Behavior) +[] +module Helpers = - • refactor: revise all helpers in Form (some were made quick&dirty) - • refactor: make update and VM cleaner (helpers) - • revise naming(?): keep "_Model", "_Msg", "_VM"? IMO it helps seperate better childs visually + better Intellisense experience in Xaml -*) + //• logging + let createDashedLine () = String.replicate 69 "-" + let logDashedLine = createDashedLine () -module FormComponentHelpers = let generateName (prefix: string) = let randomNumber () = Random().Next(1000, 10000).ToString() prefix + randomNumber () -module TextBoxComponent = + +module TextBox = type Model = { Id: Guid; Text: string } let create () = { Id = Guid.NewGuid() - Text = FormComponentHelpers.generateName "TextBox_" } + Text = generateName "TextBox_" } - let init () = create () + type Msg = | Dummy - type Msg = DummyMsg + let init () = create () let update msg m = match msg with - | DummyMsg -> m + | Dummy -> m -module CheckBoxComponent = +module CheckBox = - type Model = - { Id: Guid - Label: string - IsChecked: bool } + type Model = { Id: Guid; Label: string } let create () = { Id = Guid.NewGuid() - Label = FormComponentHelpers.generateName "CheckBox_" - IsChecked = false } + Label = generateName "CheckBox_" } - let init () = create () + type Msg = | Dummy - type Msg = DummyMsg + let init () = create () let update msg m = match msg with - | DummyMsg -> m + | Dummy -> m -module ComboBoxComponent = +module ComboBox = type Model = { Id: Guid - Header: string // Header (of GroupBox containing it) - Items: string list } + Header: string + Items: string list + SelectedItem: string option } let create () = { Id = Guid.NewGuid() - Header = FormComponentHelpers.generateName "ComboBox_" - Items = [ "Item 1"; "Item 2"; "Item 3" ] } + Header = generateName "ComboBox_" + Items = [ "Option1"; "Option2"; "Option3" ] + SelectedItem = None } - let init () = create () + type Msg = SelectItem of string option - type Msg = DummyMsg + let init () = create () let update msg m = match msg with - | DummyMsg -> m + | SelectItem item -> { m with SelectedItem = item } -module Form = +//# Form Designer +module App = - type Components = - | TextBox of TextBoxComponent.Model - | CheckBox of CheckBoxComponent.Model - | ComboBox of ComboBoxComponent.Model + type FormComponent = + | TextBox of TextBox.Model + | CheckBox of CheckBox.Model + | ComboBox of ComboBox.Model type Model = - { Components: Components list - SelectedComponent: Guid option - //• SubModels - TextBox_Model: TextBoxComponent.Model - CheckBox_Model: CheckBoxComponent.Model - ComboBox_Model: ComboBoxComponent.Model } + { Components: FormComponent list + PreviousSelectedFormComponent: Guid option + SelectedFormComponent: Guid option + Log: string list } module ModelM = - module TextBox = - let get (m: Model) = m.TextBox_Model - - let components_Mock = - [ for _ in 1..3 do - yield TextBox(TextBoxComponent.create ()) - yield CheckBox(CheckBoxComponent.create ()) - yield ComboBox(ComboBoxComponent.create ()) ] - - let init () = - { Components = components_Mock - SelectedComponent = None - //• SubModels - TextBox_Model = TextBoxComponent.init () - CheckBox_Model = CheckBoxComponent.init () - ComboBox_Model = ComboBoxComponent.init () } + module Components = + let get m = m.Components type Msg = - | Select of Guid option + | SelectFormComponent of Guid option | AddTextBox | AddCheckBox | AddComboBox + | RemoveComponent of Guid + | ClearLog //• SubModels - | TextBox_Msg of TextBoxComponent.Msg - | CheckBox_Msg of CheckBoxComponent.Msg - | ComboBoxC_Msg of ComboBoxComponent.Msg + | TextBox_Msg of Guid * TextBox.Msg + | CheckBox_Msg of Guid * CheckBox.Msg + | ComboBox_Msg of Guid * ComboBox.Msg + + let componentsMock = + [ TextBox(TextBox.create ()) + CheckBox(CheckBox.create ()) + TextBox(TextBox.create ()) + CheckBox(CheckBox.create ()) + ComboBox(ComboBox.create ()) ] + + + let getId (fc: FormComponent) = + match fc with + | TextBox tb -> tb.Id + | CheckBox cb -> cb.Id + | ComboBox cb -> cb.Id + + let init () = + { Components = componentsMock + SelectedFormComponent = Some(getId (List.item 3 componentsMock)) + PreviousSelectedFormComponent = None + Log = [] }, + Cmd.none + [] - module Form = - - let getSelectedEntityIdFromSelectComponent (m: Model) = - match m.SelectedComponent with - | Some selectedId -> selectedId - | None -> Guid.Empty - - let getComponentId component_ = - match component_ with - | TextBox a -> a.Id - | CheckBox b -> b.Id - | ComboBox c -> c.Id - - let getComponentName component_ = - match component_ with - | TextBox a -> a.Text - | CheckBox b -> b.Label - | ComboBox c -> c.Header - - let isSelected selectedId component_ = - match selectedId, component_ with - | Some id, TextBox a -> a.Id = id - | Some id, CheckBox b -> b.Id = id - | Some id, ComboBox c -> c.Id = id - | _ -> false - - let insertComponentAfterSelected selectedComponent newComponent components = - - // sample purpose: make explicit that a new component has been added - let prependNewName component_ = - match component_ with - | TextBox a -> TextBox { a with Text = "#New# " + a.Text } - | CheckBox b -> CheckBox { b with Label = "#New# " + b.Label } - | ComboBox c -> ComboBox { c with Header = "#New# " + c.Header } - - let newComponentWithPrependedName = prependNewName newComponent - - match selectedComponent with - | None -> - // If no component is selected, append the new one to the end - components @ [ newComponentWithPrependedName ] - | Some selectedId -> - let rec insertAfterSelected = - function - | [] -> [ newComponentWithPrependedName ] - | comp :: rest when getComponentId comp = selectedId -> - comp :: newComponentWithPrependedName :: rest - | comp :: rest -> comp :: insertAfterSelected rest - - insertAfterSelected components + module private UpdateHelpers = + //🞍 common + let insertAt index item list = + let before, after = List.splitAt index list + before @ [ item ] @ after + + + let getComponentName fc = + match fc with + | TextBox tb -> tb.Text + | CheckBox cb -> cb.Label + | ComboBox cb -> cb.Header + + + let selectFormComponent id m = + let logEntry = + match id with + | Some selectedId -> + match m.Components + |> List.tryFind (fun c -> getId c = selectedId) + with + | Some c -> + let name = getComponentName c + sprintf "Selected: %s" name + | None -> "Selected: Unknown component" + | None -> "Deselected" + + { m with + SelectedFormComponent = id + Log = logEntry :: logDashedLine :: m.Log } - let update msg m = - match msg with - | Select entityId -> { m with SelectedComponent = entityId } + //🞍 add/remove + let addTextBox m = + let newTextBox = TextBox.create () - | AddTextBox -> - let newTextBox = TextBox(TextBoxComponent.create ()) + let updatedTextBox = { newTextBox with Text = "#New# " + newTextBox.Text } - let newTextBoxId = - match newTextBox with - | TextBox a -> a.Id - | _ -> Guid.Empty + let newComponent = TextBox updatedTextBox + + let components = + match m.SelectedFormComponent with + | Some selectedId -> + match m.Components + |> List.tryFindIndex (fun c -> getId c = selectedId) + with + | Some index -> insertAt (index + 1) newComponent m.Components + | None -> m.Components @ [ newComponent ] + | None -> m.Components @ [ newComponent ] + + let log = + ("Added: " + updatedTextBox.Text) + :: ("Selected: " + updatedTextBox.Text) + :: logDashedLine :: m.Log { m with - Components = insertComponentAfterSelected m.SelectedComponent newTextBox m.Components - SelectedComponent = Some newTextBoxId } + Components = components + SelectedFormComponent = Some(getId newComponent) + Log = log } - | AddCheckBox -> - let newCheckBox = CheckBox(CheckBoxComponent.create ()) - let newCheckBoxId = - match newCheckBox with - | CheckBox b -> b.Id - | _ -> Guid.Empty + let addCheckBox m = + let newCheckBox = CheckBox.create () + + let updatedCheckBox = { newCheckBox with Label = "#New# " + newCheckBox.Label } + + let newComponent = CheckBox updatedCheckBox + + let components = + match m.SelectedFormComponent with + | Some selectedId -> + match m.Components + |> List.tryFindIndex (fun c -> getId c = selectedId) + with + | Some index -> insertAt (index + 1) newComponent m.Components + | None -> m.Components @ [ newComponent ] + | None -> m.Components @ [ newComponent ] + + let log = + ("Added: " + updatedCheckBox.Label) + :: ("Selected: " + updatedCheckBox.Label) + :: logDashedLine :: m.Log { m with - Components = insertComponentAfterSelected m.SelectedComponent newCheckBox m.Components - SelectedComponent = Some newCheckBoxId } + Components = components + SelectedFormComponent = Some(getId newComponent) + Log = log } + + + let addComboBox m = + let newComboBox = ComboBox.create () + let newComponent = ComboBox newComboBox + + let components = + match m.SelectedFormComponent with + | Some selectedId -> + match m.Components + |> List.tryFindIndex (fun c -> getId c = selectedId) + with + | Some index -> insertAt (index + 1) newComponent m.Components + | None -> m.Components @ [ newComponent ] + | None -> m.Components @ [ newComponent ] + + let log = + ("Added: " + newComboBox.Header) + :: ("Selected: " + newComboBox.Header) + :: logDashedLine :: m.Log + + { m with + Components = components + SelectedFormComponent = Some(getId newComponent) + Log = log } + + + let removeComponent id m : Model * Guid option * string list = + let componentOpt = + m.Components + |> List.tryFind (fun c -> getId c = id) + + let removedComponentName = + match componentOpt with + | Some c -> getComponentName c + | None -> "Unknown Component" + + let idxOpt = + m.Components + |> List.tryFindIndex (fun c -> getId c = id) + + let components = List.filter (fun c -> getId c <> id) m.Components + + let newSelected = + match idxOpt with + | Some idx when idx > 0 -> Some(getId (List.item (idx - 1) m.Components)) + | Some idx when components.Length > 0 -> Some(getId (List.item 0 components)) + | _ -> None + + let logs = [ "Removed: " + removedComponentName ] + + let m' = { m with Components = components } + m', newSelected, logs + + + //🞍 SubModels + // maybe refactor later + let textBox_Msg (id, msg) m = + let updateComponent c = + match c with + | TextBox tb when tb.Id = id -> TextBox(TextBox.update msg tb) + | other -> other + + { m with Components = List.map updateComponent m.Components } + + + let checkBox_Msg (id, msg) m = + let updateComponent c = + match c with + | CheckBox cb when cb.Id = id -> CheckBox(CheckBox.update msg cb) + | other -> other + + { m with Components = List.map updateComponent m.Components } + + + let comboBox_Msg (id, msg) m = + let updateComponent c = + match c with + | ComboBox cb when cb.Id = id -> ComboBox(ComboBox.update msg cb) + | other -> other + + { m with Components = List.map updateComponent m.Components } + + + let update msg m : Model * Cmd = + match msg with + | SelectFormComponent id -> + let m' = selectFormComponent id m + m', Cmd.none + | AddTextBox -> + let m' = addTextBox m + m', Cmd.none + | AddCheckBox -> + let m' = addCheckBox m + m', Cmd.none | AddComboBox -> - let newComboBox = ComboBox(ComboBoxComponent.create ()) + let m' = addComboBox m + m', Cmd.none + | RemoveComponent id -> + let m', newSelectedId, logs = removeComponent id m + let cmd = Cmd.ofMsg (SelectFormComponent newSelectedId) + let m'' = { m' with Log = logs @ m'.Log } + m'', cmd + | ClearLog -> { m with Log = [] }, Cmd.none + // SubModels + | TextBox_Msg (id, msg) -> + let m' = textBox_Msg (id, msg) m + m', Cmd.none + | CheckBox_Msg (id, msg) -> + let m' = checkBox_Msg (id, msg) m + m', Cmd.none + | ComboBox_Msg (id, msg) -> + let m' = comboBox_Msg (id, msg) m + m', Cmd.none - let newComboBoxId = - match newComboBox with - | ComboBox c -> c.Id - | _ -> Guid.Empty - { m with - Components = insertComponentAfterSelected m.SelectedComponent newComboBox m.Components - SelectedComponent = Some newComboBoxId } +[] +type TextBox_VM(args) = + inherit ViewModelBase(args) - //• SubModels - | TextBox_Msg msg -> { m with TextBox_Model = TextBoxComponent.update msg m.TextBox_Model } - | CheckBox_Msg msg -> { m with CheckBox_Model = CheckBoxComponent.update msg m.CheckBox_Model } - | ComboBoxC_Msg msg -> { m with ComboBox_Model = ComboBoxComponent.update msg m.ComboBox_Model } + //• helpers + + //🅦 TextBox add ctor? TRY MAYBE IT DOEST WORK BECAUSE OF THIS + new() = TextBox_VM(TextBox.init () |> ViewModelArgs.simple) + + //• members + member _.Text = base.Get () (Binding.oneWay ((fun m -> m.Text))) + + +[] +type CheckBox_VM(args) = + inherit ViewModelBase(args) + + //• helpers + new() = CheckBox_VM(CheckBox.init () |> ViewModelArgs.simple) + + //• members + member _.Label = base.Get () (Binding.oneWay ((fun m -> m.Label))) + + +[] +type ComboBox_VM(args) = + inherit ViewModelBase(args) + new() = ComboBox_VM(ComboBox.init () |> ViewModelArgs.simple) + + member _.Items = base.Get () (Binding.oneWay (fun m -> m.Items)) + member _.Header = base.Get () (Binding.oneWay (fun m -> m.Header)) + + member _.SelectedItem + with get () = + base.Get + () + (Binding.twoWay ((fun (m: ComboBox.Model) -> m.SelectedItem), (fun v _ -> ComboBox.Msg.SelectItem v))) + and set (value) = + base.Set + (value) + (Binding.twoWay ((fun (m: ComboBox.Model) -> m.SelectedItem), (fun v _ -> ComboBox.Msg.SelectItem v))) -// ViewModel/Bindings -open Form.Form // ugly [] -type Form_VM(args) = - inherit ViewModelBase(args) +type FormComponent_VM(args: ViewModelArgs) = + inherit ViewModelBase(args) - new() = Form_VM(Form.init () |> ViewModelArgs.simple) + member this.CurrentModel = + (this :> IViewModel) + .CurrentModel + + member this.Model = fst this.CurrentModel + member this.FormComponent = snd this.CurrentModel + + member this.Id = + match this.FormComponent with + | App.FormComponent.TextBox tb -> tb.Id + | App.FormComponent.CheckBox cb -> cb.Id + | App.FormComponent.ComboBox cb -> cb.Id + + member this.ComponentVM: obj = + let id = this.Id + + match this.FormComponent with + | App.FormComponent.TextBox tb -> + upcast TextBox_VM(ViewModelArgs.map (fun _ -> tb) (fun msg -> App.Msg.TextBox_Msg(id, msg)) args) + | App.FormComponent.CheckBox cb -> + upcast CheckBox_VM(ViewModelArgs.map (fun _ -> cb) (fun msg -> App.Msg.CheckBox_Msg(id, msg)) args) + | App.FormComponent.ComboBox cb -> + upcast ComboBox_VM(ViewModelArgs.map (fun _ -> cb) (fun msg -> App.Msg.ComboBox_Msg(id, msg)) args) + + member this.SelectedLabel = + let componentId = this.Id - // I *really* don't like the stringly-typed nature of this binding + no Intellisense in Xaml for submodel properties - //# DataTemplateSelector not working, but SelectedEntity works - //# as ListItem ItemSource = "Elmish.WPF.DynamicViewModel'2" - member _.Components = base.Get () - (Binding.subModelSeq ( - (fun m -> m.Components), - (fun (e) -> getComponentId e), - (fun () -> - [ - //• TextBox - "Text" - |> Binding.oneWay (fun (_, e) -> getComponentName e) - "SelectedLabel" - |> Binding.oneWay (fun (m, e) -> - if isSelected m.SelectedComponent e then - " - Selected" - else - "") ]) - - //• CheckBox - // (add later) - - //• ComboBox - )) - - - //# DataTemplateSelector seems to work (though I can't bind to subProperties), but SelectedEntity breaks.. - //# as ListItem ItemSource = "ComboBox {Id = 3232 (..) Header = "ComboBox_3232" Items = ["Item 1"; "Item 2"; "Item 3"]}" - member _.Components2 = base.Get() (Binding.oneWaySeq (( _.Components ), (=), id)) - - - // I don't like the stringly-typed nature of this binding either - member _.SelectedEntity + (Binding.oneWay (fun (m, _) -> + if Some componentId = m.SelectedFormComponent then + " • Selected" + else + "")) + + + // can be done in XAML using lots of boilerplate + // I guess that since it's in a ViewModel, there is no "separation of concerns" issues + //# what do you think? + member this.BackgroundColor: Brush = + match this.FormComponent with + | App.FormComponent.TextBox _ -> Brushes.DarkGreen + | App.FormComponent.CheckBox _ -> Brushes.DarkRed + | App.FormComponent.ComboBox _ -> Brushes.DarkOrange + + +// Adjusted App_VM with SelectedComponent having both get and set +[] +type App_VM(args) = + inherit ViewModelBase(args) + + let getId ((_, fc): App.Model * App.FormComponent) = + match fc with + | App.FormComponent.TextBox tb -> tb.Id + | App.FormComponent.CheckBox cb -> cb.Id + | App.FormComponent.ComboBox cb -> cb.Id + + + new() = App_VM((App.init () |> fst) |> ViewModelArgs.simple) + + + member _.Components_VM = + base.Get + () + (Binding.SubModelSeqKeyedT.id (fun args -> FormComponent_VM(args)) getId + >> Binding.mapModel (fun (m: App.Model) -> m.Components |> List.map (fun fc -> (m, fc))) + >> Binding.addLazy (fun m1 m2 -> + m1.SelectedFormComponent = m2.SelectedFormComponent + && m1.Components = m2.Components) + >> Binding.mapMsg (fun (_id, msg) -> msg)) + + + + member _.SelectedComponent with get () = base.Get () (Binding.subModelSelectedItem ( - "Components", - (fun (m: Form.Model) -> m.SelectedComponent), - Form.Msg.Select + "Components_VM", + (fun (m: App.Model) -> m.SelectedFormComponent), + App.Msg.SelectFormComponent )) - and set (value) = + and set value = base.Set - value + (value) (Binding.subModelSelectedItem ( - "Components", - (fun (m: Form.Model) -> m.SelectedComponent), - Form.Msg.Select + "Components_VM", + (fun (m: App.Model) -> m.SelectedFormComponent), + App.Msg.SelectFormComponent )) - member _.SelectedEntityLog - with get () = - base.Get - () - (Binding.oneWay (fun (m: Form.Model) -> - match m.SelectedComponent with - | Some id -> - let index = - m.Components - |> List.findIndex (fun e -> getComponentId e = id) - - let name = - m.Components - |> List.find (fun e -> getComponentId e = id) - |> getComponentName - - let componentType = - match m.Components - |> List.find (fun e -> getComponentId e = id) - with - | Form.Components.TextBox _ -> "Type: A" - | Form.Components.CheckBox _ -> "Type: B" - | Form.Components.ComboBox _ -> "Type: C" - - sprintf "Selected: Name = %s, Index = %d, %s" name index componentType - | None -> "No selection")) - and set (value) = base.Set value (Binding.oneWay (fun _ -> "")) - - - //• Commands - member _.AddTextBox = base.Get () (Binding.CmdT.setAlways Form.AddTextBox) - member _.AddCheckBox = base.Get () (Binding.CmdT.setAlways Form.AddCheckBox) - member _.AddComboBox = base.Get () (Binding.CmdT.setAlways Form.AddComboBox) - - member _.SelectRandom = + + + member _.AddTextBox = base.Get () (Binding.CmdT.setAlways App.Msg.AddTextBox) + member _.AddCheckBox = base.Get () (Binding.CmdT.setAlways App.Msg.AddCheckBox) + member _.AddComboBox = base.Get () (Binding.CmdT.setAlways App.Msg.AddComboBox) + member _.ClearLog = base.Get () (Binding.CmdT.setAlways App.Msg.ClearLog) + + + member _.RemoveSelectedComponent = base.Get () - (Binding.cmd (fun (m: Form.Model) -> - let randomEntity = m.Components.Item(Random().Next(m.Components.Length)) + (Binding.cmdIf (fun (m: App.Model) -> + match m.SelectedFormComponent with + | Some id -> App.Msg.RemoveComponent id |> ValueSome + | None -> ValueNone)) - match randomEntity with - | Form.Components.TextBox aModel -> Some aModel.Id - | Form.Components.CheckBox bModel -> Some bModel.Id - | Form.Components.ComboBox cModel -> Some cModel.Id - |> Form.Msg.Select)) - member _.Deselect = base.Get() (Binding.CmdT.setAlways (Form.Msg.Select None)) - + + //🅦 review it (put in update?) + member _.Log = + base.Get + () + (Binding.oneWay (fun m -> + let logLength = List.length m.Log + let takeCount = min 40 logLength + String.concat "\n" (List.take takeCount m.Log))) + module Program = - let main window = + + let main (window) = + let logger = LoggerConfiguration() .MinimumLevel.Override("Elmish.WPF.Update", Events.LogEventLevel.Verbose) @@ -350,6 +515,6 @@ module Program = .WriteTo.Console() .CreateLogger() - WpfProgram.mkSimpleT Form.init Form.update Form_VM + WpfProgram.mkProgramT App.init App.update App_VM |> WpfProgram.withLogger (new SerilogLoggerFactory(logger)) - |> WpfProgram.startElmishLoop window \ No newline at end of file + |> WpfProgram.startElmishLoop window diff --git a/src/Samples/SubModelSelectedItem/MainWindow.xaml b/src/Samples/SubModelSelectedItem/MainWindow.xaml index 9be097e0..51b4ae45 100644 --- a/src/Samples/SubModelSelectedItem/MainWindow.xaml +++ b/src/Samples/SubModelSelectedItem/MainWindow.xaml @@ -1,146 +1,143 @@  - - - - - + DataType="{x:Type vm:FormComponent_VM}"> + + + + + + - - - - + DataType="{x:Type vm:TextBox_VM}"> + - - - - + DataType="{x:Type vm:CheckBox_VM}"> + - - + + + + + + + + + + - - - - + + - - - - - + Orientation="Vertical"> - - - - + + +