GithubHelp home page GithubHelp logo

elmish.wpf's Introduction

WPF done the Elmish Way

NuGet version NuGet downloads Build status

The good parts of MVVM (the data bindings) with the simplicity and robustness of an MVU architecture for the rest of your app. Never write an overly-complex ViewModel class again!

Elevator pitch

Elmish.WPF is a production-ready library that allows you to write WPF apps with the robust, simple, well-known, and battle-tested MVU architecture, while still allowing you to use all your XAML knowledge and tooling to create UIs.

Some benefits of MVU you’ll get with Elmish.WPF include:

  • Simple-to-understand, unidirectional data flow
  • Single source of truth for all the state in your app
  • Simple async/IO
  • Immutable data
  • Pure functions
  • Great testability
  • Simple optimization
  • 78% more rockets 🚀

Even with static views, your central model/update code can follow an idiomatic Elmish/MVU architecture. You could, if you wanted, use the same model/update code to implement an app using a dynamic UI library such as Fabulous or Fable.React, by just rewriting the “U” part of MVU.

Static XAML views is a feature, not a limitation. See the FAQ for several unique benefits to this approach!

Elmish.WPF uses Elmish, an F# implementation of the MVU message loop.

Big thanks to @MrMattSim for the wonderful logo!

Sponsor

JetBrains logo

Thanks to JetBrains for sponsoring Elmish.WPF with OSS licenses!

Recommended resources

Getting started with Elmish.WPF

See the SingleCounter sample for a very simple app. The central points are (assuming up-to-date VS2019):

  1. Create an F# Class Library. If targeting .NET 5 or .NET Core, the project file should look like this:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net5.0-windows</TargetFramework>  <!-- Or another target framework -->
        <UseWpf>true</UseWpf>
      </PropertyGroup>
    
      <!-- other stuff -->

    If targeting .NET Framework (4.6.1 or later), replace the first line with

    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  2. Add NuGet reference to package Elmish.WPF.

  3. Define the model that describes your app’s state and a function that initializes it:

    type Model =
      { Count: int
        StepSize: int }
    
    let init () =
      { Count = 0
        StepSize = 1 }
  4. Define the various messages that can change your model:

    type Msg =
      | Increment
      | Decrement
      | SetStepSize of int
  5. Define an update function that takes a message and a model and returns an updated model:

    let update msg m =
      match msg with
      | Increment -> { m with Count = m.Count + m.StepSize }
      | Decrement -> { m with Count = m.Count - m.StepSize }
      | SetStepSize x -> { m with StepSize = x }
  6. Define the “view” function using the Bindings module. This is the central public API of Elmish.WPF.

    Normally in Elm/Elmish this function is called view and would take a model and a dispatch function (to dispatch new messages to the update loop) and return the UI (e.g. a HTML DOM to be rendered), but in Elmish.WPF this function is in general only run once and simply sets up bindings that XAML-defined views can use. Therefore, let’s call it bindings instead of view.

    open Elmish.WPF
    
    let bindings () =
      [
        "CounterValue" |> Binding.oneWay (fun m -> m.Count)
        "Increment" |> Binding.cmd (fun m -> Increment)
        "Decrement" |> Binding.cmd (fun m -> Decrement)
        "StepSize" |> Binding.twoWay(
          (fun m -> float m.StepSize),
          (fun newVal m -> int newVal |> SetStepSize))
      ]

    The strings identify the binding names to be used in the XAML views. The Binding module has many functions to create various types of bindings.

    Alternatively, use statically-typed view models in order to get better IDE support in the XAML.

    open Elmish.WPF
    
    type CounterViewModel(args) =
      inherit ViewModelBase<Model, Msg>(args)
    
      member _.CounterValue = base.Get() (Binding.OneWayT.id >> Binding.mapModel (fun m -> m.Count))
      member _.Increment = base.Get() (Binding.CmdT.setAlways Counter.Increment)
      member _.Decrement = base.Get() (Binding.CmdT.setAlways Counter.Decrement)
      member _.StepSize
        with get() = base.Get() (Binding.OneWayT.id >> Binding.mapModel (fun m -> m.StepSize))
        and set(v) = base.Set(v) (Binding.OneWayToSourceT.id >> Binding.mapMsg Counter.Msg.SetStepSize)
  7. Create a function that accepts the app’s main window (to be created) and configures and starts the Elmish loop for the window with your init, update and bindings:

    open Elmish.WPF
    
    let main window =
      Program.mkSimple init update bindings
      |> Program.runElmishLoop window

    Alternatively, use a statically-typed view model at the top level.

    open Elmish.WPF
    
    let main window =
      Program.mkSimpleT init update CounterViewModel
      |> Program.runElmishLoop window

    In the code above, Program.runElmishLoop will set the window’s DataContext to the specified bindings and start the Elmish dispatch loop for the window.

  8. Create a WPF app project (using the Visual Studio template called WPF App (.NET)). This will be your entry point and contain the XAML views. Add a reference to the F# project, and make the following changes in the csproj file:

    • Currently, the core Elmish logs are only output to the console. If you want a console window for displaying Elmish logs, change <OutputType>WinExe</OutputType> to <OutputType>Exe</OutputType> and add <DisableWinExeOutputInference>true</DisableWinExeOutputInference>.
    • If the project file starts with the now legacy <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">, change it to <Project Sdk="Microsoft.NET.Sdk">
    • Change the target framework to match the one used in the F# project (e.g. net5.0-windows).

    Make the following changes to App.xaml.cs to initialize Elmish when the app starts:

    public partial class App : Application
    {
      public App()
      {
        this.Activated += StartElmish;
      }
    
      private void StartElmish(object sender, EventArgs e)
      {
        this.Activated -= StartElmish;
        Program.main(MainWindow);
      }
    
    }
  9. Define your views and bindings in XAML:

    <Window
        x:Class="MyNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding CounterValue}" />
        <Button Command="{Binding Decrement}" Content="-" />
        <Button Command="{Binding Increment}" Content="+" />
        <TextBlock Text="{Binding StepSize}" />
        <Slider Value="{Binding StepSize}" TickFrequency="1" Minimum="1" Maximum="10" />
      </StackPanel>
    </Window>
  10. Profit! :)

Further resources:

  • The Elmish.WPF tutorial provides information on general MVU/Elmish concepts and how they apply to Elmish.WPF, as well as the various Elmish.WPF bindings.
  • The samples are complete, working mini-apps demonstrating selected aspects of Elmish.WPF.
  • If you'd like to contribute, please read and follow the Contributor guidelines.

FAQ

Static views in MVU? Isn’t that just a half-baked solution that only exists due to a lack of better alternatives?

Not at all! 🙂

It’s true that static views aren’t as composable as dynamic views. It’s also true that at the time of writing, there are no solid, production-ready dynamic UI libraries for WPF (though there are no lack of half-finished attempts or proof-of-concepts: Elmish.WPF.Dynamic, Fabulous.WPF, Skylight, Uil). Heck, it’s even true that Elmish.WPF was originally created with static views due to the difficulty of creating a dynamic UI library, as described in issue #1.

However, Elmish.WPF’s static-view-based solution has several unique benefits:

  • You can use your existing XAML and MVVM knowledge (that is, the best part of MVVM – the UI bindings – without having to deal with NavigationServices, ViewModelLocators, state synchronization, INotifyPropertyChanged, etc.)
  • Huge mindshare – there are tons of relevant XAML and MVVM resources on the net which can help with the UI and data binding part if you get stuck
  • Automatic support for all 3rd party WPF UI libraries like MaterialDesignInXamlToolkit, since it just uses XAML and bindings (support for 3rd party libraries is commonly a major pain point for dynamic UI solutions)
  • You can use the XAML designer (including design-time data binding)
  • Automatically puts all the power of WPF at your fingertips, whereas dynamic UI solutions have inherent limitations that are not easy to work around

In short, for WPF apps, a solution based on static XAML views is currently the way to go.

Do I have to use the project structure outlined above?

Not at all. The above example, as well as the samples, keep all non-UI code in a single project for simplicity, and all the XAML in a C# project for better tooling.

An alternative with a clearer separation of UI and core logic can be implemented by splitting the F# project into two projects:

  • A core library containing the model definitions and update functions.
    • This library can include a reference to Elmish (e.g. for the Cmd module helpers), but not to Elmish.WPF, which depends on WPF and has a UI-centered API (specifying bindings). This will ensure your core logic (such as the update function) is free from any UI concerns, and allow you to re-use the core library should you want to port your app to another Elmish-based solution (e.g. Fable.React).
  • An Elmish.WPF project that contains the bindings (or view) function and the call to Program.runElmishLoop.
    • This project would reference the core library and Elmish.WPF.

Another alternative is to turn the sample code on its head and have the F# project be a console app containing your entry point (with a call to Program.runWindow) and referencing the C#/XAML project (instead of the other way around, as demonstrated above).

In general, you have a large amount of freedom in how you structure your solution and what kind of entry point you use.

How can I test commands? What is the CmdMsg pattern?

Since the commands (Cmd<Msg>) returned by init and update are lists of functions, they are not particularly testable. A general pattern to get around this is to replace the commands with pure data that are transformed to the actual commands elsewhere:

  • Create a CmdMsg union type with cases for each command you want to execute in the app.
  • Make init and update return model * CmdMsg list instead of model * Cmd<Msg>. Since init and update now return data, they are much easier to test.
  • Create a trivial/too-boring-to-test cmdMsgToCmd function that transforms a CmdMsg to the corresponding Cmd.
  • Finally, create “normal” versions of init and update that you can use when creating Program. Elmish.WPF provides Program.mkProgramWpfWithCmdMsg that does this for you (but there’s no magic going on – it’s really easy to do yourself).

The FileDialogsCmdMsg sample demonstrates this approach. For more information, see the Fabulous documentation. For reference, here is the discussion that led to this pattern.

Can I use design-time view models?

Yes. Assuming you have a C# XAML and entry point project referencing the F# project, simply use ViewModel.designInstance (e.g. in the F# project) to create a view model instance that your XAML can use at design-time:

module MyAssembly.DesignViewModels
let myVm = ViewModel.designInstance myModel myBindings

Then use the following attributes wherever you need a design-time VM:

<Window
    ...
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:MyAssembly;assembly=MyAssembly"
    mc:Ignorable="d"
    d:DataContext="{x:Static vm:DesignViewModels.myVm}">

When targeting legacy .NET Framework, “Project code” must be enabled in the XAML designer for this to work.

If you are using static view models, make sure that the View Model type is in a namespace and add a default constructor that passes a model into ViewModelArgs.simple:

namespace ViewModels

type [<AllowNullLiteral>] AppViewModel (args) =
  inherit ViewModelBase<AppModel, AppMsg>(args)
  
  new() = AppViewModel(App.init () |> ViewModelArgs.simple)

Then use the following attributes just like you would in a normal C# MVVM project:

<Window
    ...
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:ViewModels.SubModelStatic;assembly=MyAssembly"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance Type=vm:AppViewModel, IsDesignTimeCreatable=True}">
.NET Core 3 workaround

When targeting .NET Core 3, a bug in the XAML designer causes design-time data to not be displayed through DataContext bindings. See this issue for details. One workaround is to add a d:DataContext binding alongside your normal DataContext binding. Another workaround is to change

<local:MyControl DataContext="{Binding Child}" />

to

<local:MyControl
  DataContext="{Binding Child}"
  d:DataContext="{Binding DataContext.Child,
                          RelativeSource={RelativeSource AncestorType=T}}" />

where T is the type of the parent object that contains local:MyControl (or a more distant ancestor, though there are issues with using Window as the type).

Can I open new windows/dialogs?

Sure! Just use Binding.subModelWin. It works like Binding.subModel, but has a WindowState wrapper around the returned model to control whether the window is closed, hidden, or visible. You can use both modal and non-modal windows/dialogs, and everything is a part of the Elmish core loop. Check out the NewWindow sample.

Note that if you use App.xaml startup, you may want to set ShutdownMode="OnMainWindowClose" in App.xaml if that’s the desired behavior.

Can I bind to events and use behaviors?

Sure! Check out the EventBindingsAndBehaviors sample. Note that you have to install the NuGet package Microsoft.Xaml.Behaviors.Wpf.

How can I control logging?

Elmish.WPF uses Microsoft.Extensions.Logging. To see Elmish.WPF output in your favorite logging framework, use WpfProgram.withLogger to pass an ILoggerFactory:

WpfProgram.mkSimple init update bindings
|> WpfProgram.withLogger yourLoggerFactory
|> WpfProgram.runWindow window

For example, in Serilog, you need to install Serilog.Extensions.Logging and instantiate SerilogLoggerFactory. The samples demonstrate this.

Elmish.WPF logs to these categories:

  • Elmish.WPF.Update: Logs exceptions (Error level) and messages/models (Trace/Verbose level) during update.
  • Elmish.WPF.Bindings: Logs events related to bindings. Some logging is done at the Error level (e.g. developer errors such as duplicated binding names, using non-existent bindings in XAML, etc.), but otherwise it’s generally just Trace/Verbose for when you really want to see everything that’s happening (triggering PropertyChanged, WPF getting/setting bindings, etc.)
  • Elmish.WPF.Performance: Logs the performance of the functions you pass when creating bindings (get, set, map, equals, etc.) at the Trace/Verbose level. Use WpfProgram.withPerformanceLogThreshold to set the minimum duration to log.

The specific method of controlling what Elmish.WPF logs depends on your logging framework. For Serilog you can use .MinimumLevel.Override(...) to specify the minimum log level per category, like this:

myLoggerConfiguration
  .MinimumLevel.Override("Elmish.WPF.Bindings", LogEventLevel.Verbose)
  ...

elmish.wpf's People

Contributors

2scomplement avatar billhally avatar cmeeren avatar dharmaturtle avatar evangelink avatar forki avatar gdennie avatar huwmanatee avatar integerman avatar jdilenarda avatar jordanmarr avatar kxxt avatar lyndongingerich avatar magicmonty avatar marner2 avatar mrmattsim avatar nojaf avatar nukecoder avatar rfreytag avatar scotthutchinson avatar scottshingler avatar thejaymann avatar tysonmn avatar

Stargazers

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

Watchers

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

elmish.wpf's Issues

[FAQ] "Can I open new windows/dialogs?"

I'm reading this point in the FAQ:

there is no simple way for users to set the DataContext of new windows in Elmish.WPF without explicit support. In any case, making use of anything not available through bindings (such as opening and closing windows) would mean that your UI would no longer be a simple function of your model, which is a central point of the Elm architecture

Maybe I misunderstood, but since I want to try to develop my next WPF app with Elmish and deliver it to the users in production, I need to be sure that my way of doing similar things is ok.
So, first of all, this is an example of how I would achieve the goal of opening a new window and passing the DataContext to it.

Basically it all boils down to invoking the correct dispatcher: what is particularly difficult or not advisable here?

Howto bind to command for buttons in a list

Hi all,
To try out Elmish.WPF, I am writing a small starter tool, which has a configurable list of buttons which start programs when clicked.

In the elmish example, I found the line
| NumberToText id -> { model with Items = model.Items |> List.map (fun i -> if i.Id = id then numberToText i else i)}

Would that mean, I have to found the button in the list which was responsible for that event?
I think, I am still missing a link in my mindset. There should probably be a buttonUpdate method to handle this call. But I can't figure out how to do that. Has someone an example for that?

Binding.oneWayToSource

Hello,

When working with textboxes in WPF, I continue to come across an issue. If I am typing too fast in a textbox control bound like so:

<TextBox Text="{Binding Path=ImportantText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        "ImportantText"
        |> Binding.twoWay
            (fun m -> m.ImportantText)
            (fun it _ -> it |> ImportantText)

Then there comes an issue where the Elmish.WPF is still updating the UI in the backend while the user is still entering data. What this means is, the users' caret goes to the beginning of the textbox, causing them to "type behind" the newly updated text. For an example of this issue, you can type quickly in the NewWindow sample.

I feel that the solution is to support Binding.oneWayToSource, which would mean that the code behind has no impact on the view (as there is no point to it anyway). Unless, of course, there is already a solution implemented that I am unaware of.

In the meantime, assuming that there is no particular reason the view needs to be updated as the user is typing, then a potential solution is to use WPF with InputBindings like so:

<TextBox Text="Test">
    <TextBox.InputBindings>
        <KeyBinding Command="{Binding ImportantText}" Key="Return" />
        <KeyBinding Command="{Binding ImportantText}" Key="Enter" />
    </TextBox.InputBindings>
</TextBox>

Or a command binding attached to a "submit" button.

Thoughts?

Merging of cleanup/re-implementation of Elmish.WPF

In my quest for recursive sub-model lists, I ended up rewriting most of Elmish.WPF from scratch, cleaning up some cruft and making names and types clearer (IMHO). I find the new implementation easier to grasp (says the author…), and I think you'll find there's quite a bit of attention to detail. In any case, I’d like to explain why and would like an open discussion on how we should proceed (use all/most of the changes vs. rewriting the features that are actually new into the existing implementation).

The new implementation is here. I haven’t made a PR because the diff would be pointless, but let me know if you want one anyway.

My implementation solves/supersedes #5, #38, #41, #42, #43, #44, and #45.

Disclaimer: The views expressed here are subjective. It is not my intention to step on any toes. But I don’t want to waste anyone’s time sugarcoating or beating around the bush, so I’ll call ’em like I see ‘em.

Why a re-implementation?

Simply put, I had trouble understanding the existing implementation. Not because there’s anything fundamentally wrong with it – it’s great, and mine is fundamentally similar. But all the type aliases confounded me, and it’s clear that the ViewModelBase, ViewBinding, ViewBindings, Variable, and PropertyAccessor types weren’t made with sub-model usage in mind, or lazy evaluation (which I also wanted).

So I did the only thing that made sense to me in order to understand the architecture: Using the existing implementation as a guide, I rewrote Elmish.WPF mostly from scratch, only complexifying as needed and in the direction that made most sense to me considering the final features I had in mind.

My rewrite turned out very similar, but types and functions relating to bindings and the view model is refactored quite a bit compared to the existing implementation.

Significant internal changes

  • The binding union types have been rewritten from scratch and IMHO have a clearer function. BindingSpecData is generated by the Binding functions and contains everything the view model needs to set up a binding. Binding contains all data used by active bindings in the view model (including mutable types / ref cells where relevant).
  • ViewModelBase (now called ViewModel) has been rewritten from scratch (but works in fundamentally the same way)
  • More samples, and all samples are consistently designed and implemented.
  • All projects use the new fsproj/csproj formats.
  • All non-public stuff has been moved to an .Internal namespace.

Public breaking changes

Note that I don’t care about binary breaking changes here, only source breaking changes. These changes can of course be tweaked/reverted.

  • twoWayValidation is called twoWayIfValid (because that’s what it is, and it clearly separates it from the new twoWayValidate)
  • oneWayMap is called oneWayLazy (its implementation has changed, and the use case has expanded, but is similar)
  • cmd and cmdIf have been renamed paramCmd and paramCmdIf (the old names exist but have new signatures / use-cases, see below)
  • model has been renamed subModel because it’s more clear
  • Program.runDebugWindow has been removed in favour of Program.runWithConfig (see below)

Public new stuff

  • Improved documentation, particularly in the Binding module
  • Binding.oneWayLazy replaces oneWayMap and is more efficient
  • Edit: Binding.oneWaySeq for avoiding re-rendering whole lists etc. each time an item changes
  • Binding.twoWayValidate is just like twoWay but with a separate validate function. The benefits of this is explained in the documentation and the Validation sample.
  • Binding.cmd and Binding.cmdIf depend only on the model, which allows us to trigger CanExecuteChanged only when changed instead of for each update
  • Binding.cmdIfValid allows better re-use and type safety, see the documentation for details
  • Binding.paramCmd and Binding.paramCmdIf take an extra parameter to control whether to use CommandManager.RequerySuggested. In many cases (when not binding CommandParameter to another UI control) it’s not needed, and it triggers all. the. time., so this could be a good optimization.
  • Binding.subModelSeq, perhaps the central new feature.
  • Edit: Binding.subModelOpt, allowing a sub-model wrapped in option. This makes it easier to make illegal states unrepresentable in the model (though it's still limited by the fact that Elmish.WPF uses bindings instead of dynamically generated views). None models are surfaced as null to the XAML bindings.
  • New configuration record type passed to new Program.runWithConfig function that allows extensible configuration (e.g. log to trace or console or both, in the future perhaps verbosity level and who knows what else).
  • Contains the change from #42 (use existing Application instance if it exists)

Small changes

  • More generous use of comments throughout
  • No type aliases. IMHO, 'model -> obj is much more clear than Getter<'model>, and there is five of these (and more for new functions), so I constantly had to look them up.
  • Code style is a bit cleaner IMHO – for example, no more 150 character lines, and I’ve been slightly more generous with whitespace. (The 2 space indentation can of course be changed to 4 spaces.)
  • I may have made some other performance improvements I haven't mentioned

Things I didn’t copy over

  • The try/with around the GetSetValidate setter. I don't see why it should be a good idea to just drop all exceptions.
  • Cmd.ofAsyncCallback – I don’t know the use-case for this. Wouldn’t it be better to just implement it with try ... with inside the async, and couldn’t users easily do that themselves?
  • Program.runWindowWith – I don’t see why we need a separate function just to pass an initial parameter to the init function, because you can just do that with partial application (or wrapping in a lambda) before calling Program.runWindow. IMHO that’s easier for consumers of the library, because it’s one less thing to learn. But I might have missed a valid use-case that requires this function.
  • The Performance and DesignDataContext samples

Final notes

As said, I encourage you to take a careful look at the implementation.

I like the rewrite (well duh), and think it’s clearer for newcomers wanting to contribute to Elmish.WPF as well as making future development slightly easier due to the architecture being a bit more clear (the binding union types and ViewModel implementation). But I don’t want it hanging around on GitHub alongside Elmish.WPF doing exactly the same thing, potentially splitting the community.

How do we move forward from here? I see a couple of paths:

  • You say “thank you, but no thanks” and I pout for a while and continue to use my implementation privately because, while it won’t benefit anyone else (aside from those stumbling over it on GitHub) it does suit my own needs better.
  • You say “well, we really only want feature X and Y from this” and I may create PRs for just those, but no guarantees since half the reason I rewrote it in the first place was because I had trouble with the recursive submodel list stuff in the existing framework.
  • You say ”this looks nice, let’s make some adjustments here and there and we’ll take it” and I’ll happily work with you on that.

Any input is most welcome.

Two-way bindings gives 'Cannot save value from target back to source' in debug output

On a simple two-way binding from a textbox to a string on a model, I see the following output when running: -

System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=Name; DataItem='ViewModelBase`2' (HashCode=64479624); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') InvalidOperationException:'System.InvalidOperationException: Property path is not valid. 'System.Dynamic.DynamicObject+MetaDynamic' does not have a public property named 'Items'.
   at CallSite.Target(Closure , CallSite , Object , Object )
   at MS.Internal.DynamicPropertyAccessorImpl.SetValue(Object component, Object value)
   at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value)
   at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value)
   at System.Windows.Data.BindingExpression.UpdateSource(Object value)'

This doesn't crash the app, but repeated errors like these have an effort on WPF perf when they mount up.

UWP support

Are there any plans towards supporting UWP ?

(Or is the a simple solution to just setup the "Program" for UWP instead of WPF ?)

Twoway binding Textbox to int value give BindingExpression

In a Elmish.WPF project i am trying to bing an integer value to TextBox and when i'm changing value in the textbox with a valid integer i have the Following exception :

System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=Count; DataItem='ViewModelBase 2' (HashCode=28141317); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') InvalidOperationException:'System.InvalidOperationException: Chemin de propriété non valide. 'System.Dynamic.DynamicObject+MetaDynamic' n'a pas de propriété publique nommée 'Items'.

Code for binding looks like this :

https://gist.github.com/ArthurFSharp/376752dcd8204674e74d06c90e7f32b3

and XAML:

<TextBox Text="{Binding Count, Mode=TwoWay}" />

UserControl inside ListView

Hello,

First of all, a huge thanks for this library.
I tryed to develop Unidirectional WPF and I really like your approach.

I am wondering what is the best approach to use an UserControl inside a ListView.

I have started a demo application here: https://github.com/bcachet/Elmish.WPF-Sample/blob/master/src/Counters/App.fs

I have bound the DataContext of the ListView (using Binding.oneWay) but I do not know how to bind DataContext of ListView's items to the ViewBindings representing my UserControl.

Any hint on how to do this ?

Have a nice day

Bertrand

Discussion on Integrating ElmishWPFLearning project to Elmish.WPF

I have been working on a project called ElmishWPFLearning, which purpose is to learn more about MVU paradigm, and how it can be applied to WPF, by doing the implementation of everything from scratch.

The current implementation can be simplified to be as followed :

  • An opinionated DSL on top of the WPF API, where I have selected a subset of the WPF Elements accessible that I thought were enough to have something nice working, to handle the View, so that means we have dynamic views.
    The way you define a WPF Element is with 4 criteria :
    • what it is. For instance a button, a textbox ...
    • the information specific that it tries to relay. For instance it's content, if the checkbox is checked or not ...
    • It's style. For instance, it's background is blue ...
    • The events related to user interactions. For instance, the clicked event, when the user has clicked on a button... (To also know the handling of events is a bit particular in that DSL, you can ask me more about them if that interest you)
  • A treediff algorithm which finds a set of difference to be applied to the old tree in order to update the UI to represent the new view. It is not the minimal set of difference, because if that was the case, then in fact, I would argue that finding that minimal set of difference would take much longer than applying those difference (might not be true in the case that we have to do big layout change, as the UI might take time rendering those updates. I haven't came across that situation yet, with my tree diff algorithm.)
  • 2 mailboxprocessors, (1 handling the whole loop and switching the UI thread only for updating the UI with the updates. The other 1 handling the different subscriptions.)
  • A "virtualDom" for handling the WPF tree, with virtual events, virtual properties, default values in case we don't set a property any longer...

There are things I have forgotten, but if you have any question about anything do not hesitate to ask!!

I have to add 1 thing, there are problems that I haven't handled yet but I wanted to handle in the near future, and I have an idea on how to do that, or at least I think it should be feasible:

  • 3rd party control (but we lose low-level tree diff granularity, if I want to do it in a simple manner)
  • something I call EtVL(Event trigger View Loop, or call it the way you want it, I am trying to be fancy with the name here 😄 ), which consist in updating the UI without going through the whole MVU loop, when you only do something, on the UI, that doesn't impact your model. For instance, when you hover over a button and you want to update the button style, without updating the model...

Finaly, bear in mind the implementation is not perfect at all, there is a lot of small, just started, projects, next to the project called Elmish, which are just small experiment on either implementing a small library on top of the DSL (Algae,ElmishStyle), or small exemples of the usage of the library (simpleApplication,SoundProcessing).

After a proposal from @et1975 to possibly contribute with that work, I came here to engage a discussion to see if my approach on the problem fits Elmish.WPF approach, or if it is not the case and we should think about something else. So I hope we can start the discussion on that 😄 .

Thank you everyone.

How are validation/error messages propagated back to a control?

Great project :)

In my experiment I couldn't find a way to send error notifications via the binding. As a last resort I tried throwing exceptions in the setter function of a two-way binding, that also doesn't propagate back.

Looking at the code ViewModelBase doesn't implement INotifyDataErrorInfo.

How can this be achieved?

.net framework 4.5.2

I am unable to install to a .net 4.5.2 project via NuGet. I was able to get it working by upgrading the project 4.6.1. It looks like Elmish does not work with 4.5.2.

It would be nice if the Elmish.WPF NuGet package had a hard dependency on 4.6.1 so that installing into a 4.5.2 project was disallowed in the first place. Or maybe even a note on the README.md saying that 4.5.2 is not supported would be helpful.

How to build/install?

Hi, everyone

I'm having trouble getting this installed. 'Elmish.WPF' doesn't show up in NuGet (with Visual Studio 2017 Enterprise Edition), and if I run '.\build.cmd', I get this error (with the top line in red):

The request was aborted: Could not create SSL/TLS secure channel. (Github - cached)
The system cannot find the path specified.

The error message doesn't change if I run '.\build.cmd NuGet', nor does it matter if I leave off the leading ".".

Does anyone know what I'm missing?

Parent-child composition

Hi,

Perhaps I'm missing something but I don't understand how you can compose easily your "mvu functions" (as you can do with elm). init and update seems ok to me but view is only defining bindings, not what has to be rendered :

let view _ _ = 
    [ "Increment" |> Binding.cmd (fun m -> Increment)
      "Decrement" |> Binding.cmdIf (fun m -> Decrement) (fun m -> m.StepSize = 1)
      ...
    ]

The "real" view is the xaml window which is only used when you make the final application :

let main argv = 
  Program.mkSimple init update view
    |> Program.withSubscription subscribe
    |> Program.runWindow (Elmish.CounterViews.MainWindow())

With this kind of architecture, mvu functions do not represent "isolated" elements (because they are not responsible of the rendering), and it becomes tedious to use composition (you need to compose your xaml elements somewhere else to make it work).

Parent-child composition (including view composition) should still be possible if view functions returned xaml UI elements and corresponding bindings. Is there a plan for that ? Am I missing something ?

Thanks

Two-way binding causes System.Windows.Data Error: 8

I'm just playing around with this for now. Generally works very nicely.

I have a slider control with a TwoWay binding to the Value property.

Every time I move the slider I get this error message:
System.Windows.Data Error: 8 : Cannot save value from target back to source.

Is there a workaround? Is it a bug?

It looks like the problem is that the value change creates a message which updates the model, and then the model change triggers a view update with the value that was just supplied.

I can provide a repro if needed.

WPF Commanding support

I have been thinking about the best way commanding support could be added to this project. WPF Commanding has mostly been abandoned in MVVM style applications in favor of command binding.

WPF Commanding (RoutedCommands) originally served two purposes. First was to be able to map multiple command sources (buttons, menus, toolbars, keyboard shortcuts, and mouse gestures) to the same command. MVVM supports this same scenario by simply binding the various command sources to the same command.

However, the second purpose was to be able to map a single command to multiple command targets, allowing different functionality based on which target is receiving the command. A good example of this would be a text editor application with a folder view, where one may want to be able to copy text in the editor, while also being able to copy a file or folder in the folder view. By binding the ApplicationCommands.Copy command to the command sources, the built in text editor control already knows how to handle the Copy command, thus you don't need to code anything. However, if using the built in TreeView to display a folder view, it does not handle Copy command. The typical way to do this in classic WPF would be to add a command binding, which would bind event handlers defined in the code behind to the commands for the command target. This approach does not work too well for either MVVM style nor Elm style.

Given an instance of the UIElement which will act as a command target, two (or four) functions which will act as event handlers for Execute and CanExcecute (and Preview*), and the RoutedCommand for responding, it would be trivial to create a CommandBinding and add it to the CommandBindings collection of the UIElement.

I have been thinking about how the public API could be defined to allow for this design. First, it must be possible to get an instance of the UIElement. My thought would be to create an attached property. The value of this property would be a string which would contain the name of the command binding specified in the view binding. For the view bindings, a new function routedCommands with the definition (RoutedCommand * Execute<'model,'msg> * CanExecute<'model>) list -> string -> ViewBinding<'model,'msg>. This would be a list of commands, along with the functions to execute when the command is invoked, as well as a name so they can be discovered via the attached property.

As an example:

"FolderViewCommands"
  |> Binding.routedCommands [
    ApplicationCommands.Copy, copyFile, canCopyFile
    ApplicationCommands.Cut, cutFile, canCutFile
    ApplicationCommands.Paste, pasteFile, canPasteFile
  ]

And, in the XAML:

<TreeView elmish:CommandBindings="FolderViewCommands"/>

Internally, the view model would need to query the list of UIElement and binding name pairs and match them to the list of routedCommands so appropriate CommandBindings can be created and added to the UIElements.

I'd like to know if this has any use for anyone beyond me, if there are any changes or improvements on top of what I stated, if there are any simpler methods than what I outlined, and if this would be worth implementing at all.

Propositions for a next version - multi-agent application and dialog/window utilities

As I said in #24, I wrote a version you can see here.

This development came in 2 steps :

  • first, trying to write an AutoCAD palette with Elmish.WPF
  • then thoughts on dealing with multiple windows (modal and modeless).

For the first step, I wrote a function that can bind a UserControl to an Elmish program and modified ViewModel so its constructor take a WPF thread dispatcher as a parameter (the AutoCAD thread dispatcher can't be retrieved with Application.Current.Dispatcher).

The second step came when #24 was reopen. It made me theorizing about the way to deal with multi-windowed application and realizing that what I did for user control can be used as well with in-app windows. Most of this work was trying to set up best practices to do so, through writing some sample projects, and also led to include some utilities function for dealing with Elmish loop as DataContext.

See sample projects ModelessWindows.SharedLoop and ModelessWindows.SeparatedLoop. Get a look at the code, this is where I try to find the best way to work with multi window applications. Note that it may work as well with controls. See also OpenDialog, though this one is more raw-coded. Notice that all window management is made in the bindings/views function, not in the update loop.

To boil it down, what does this version allow ?

  • writing "multi-agent" app, where windows and controls can run their own Elmish loop. Useful for keeping models light and focused on an application heavy on data navigation (be it through windows or pages), and working asynchronously on some data (like a submit palette control/window).
  • writing controls or windows libraries.
  • writing plug-ins for extensible applications.

There are a few thing unfinished where I may need help, notably a function that would be able to apply a XAML binding path-like string to a ViewModel (for now, I use an abstract construct to do so).

Also, check for the ModelessWindows.* projects and tell me if the practices I try to set are good or not.

And let me know if it is worthy of a pull-request.

Structural comparison vs. performance of oneWayLazy

Consider the following lines used for updating a oneWayLazy binding and indicating if PropertyChanged should be triggered (this is from the rewrite, but it works similarly in the old implementation):

| OneWayLazy (currentVal, get, map) ->
// TODO: use reference equality? we might be comparing large arrays...
if get newModel = get currentModel then false
else
currentVal := lazy (newModel |> get |> map)
true

Half the point of this oneWayLazy is to avoid running map unless its input (i.e., the output from get) has changed, so there must be some kind of equality check here. However, as it's currently implemented, it compares using = which means that we might potentially be structurally comparing, say, large lists of records (certainly not a far-fetched assumption considering the usecase of oneWayLazy). AFAIK this can be an expensive operation itself, and this would be called for absolutely all updates for this particular (sub)model.

I'm wondering if anyone has any tips on how to improve this performance hit.

As can be seen from the code comment, I originally thought about using reference equality, but this won't work because if get extracts several items from the state and returns them in a tuple (or any other type, for that matter), Object.ReferenceEquals will return false.

(There are also other problems with reference equality, such as change detection potentially yielding false positives for different objects that are structurally identical, or false negatives for if get returns a mutable object, but those aren't as serious if one has an idiomatic, immutable model. I originally wrote a bit about those two issues here, but I then realized that reference equality wasn't feasible at all due to the aforementioned problem, so I'll spare you the details.)

How to use invalid input in update function?

Consider a form with a single input field. It must be non-empty to be valid. If it's valid, the Submit button is enabled; if it's invalid, the button is disabled.

This simple (and I'd guess common) use case seems impossible in Elmish.WPF.

The reason: AFAIK, in Elmish.WPF, Binding.twoWayValidation is the only way to trigger validation messages. twoWayValidation only sends a message when the input is valid. This means that changes to an input that moves the input from valid to invalid are not sent, and thus validation state (or invalid input) cannot be a part of the model.

Unless I'm mistaken, this means that it's impossible to e.g. disable a button whenever the input is invalid, because if the user moves from a valid state (text in the input field) to an invalid state (empty input field), no message is sent and the state keeps the (now outdated) previous valid value, happily ignorant that the input field is now in fact invalid.

In short, there needs to be another way to trigger validation in Elmish.WPF that still allows messages (which can then contain the raw, invalid input) to be sent to Elmish even for invalid input.

Support for dialogs

It's not clear how to implement a modal dialog in Elmish.WPF.

Seems like using Window.ShowDialog() should be the way to go, but it's not clear how to hook into the dialog's dispatch loop. Is there a need for explicit dialog support, or is it just a case of providing an example?

I'd be happy to provide a sample if someone could give me some guidance.

Conditional elements of views

One of the nice things about the Elmish React programming model is the way the view function can be used to conditionally compute the view, e.g. based on whether the user is logged in or not. A sample is here (a non-None model indicates the user is logged in, the UI updates accordingly).

The Elmish WPF and Elmish Xamarin Forms approach appears to be to put this state into the binding and use various Xaml conditional view logic things, e.g. https://github.com/Prolucid/Elmish.WPF/blob/master/src/Samples/Elmish.Samples.DesignDataContext/Program.fs#L48. The removal of this kind of control state into regular program logic is however an important "win" of the Elmish model.

My question is whether anyone has explored a happy medium between these two somehow. I understand that the view function is currently only called once in the Elmish.WPF/XF model. I'm thinking of some setup where the view function is called repeatedly and the results indicate both bindings and visibility, and the results are differentially applied (note: I'm not thinking of full differential update of the whole WPF visual tree as in React, which really requires changes inside WPF or Xamarin Forms as far as I understand, though those are interesting in their own right for Xamarin Forms)

To put it another way, have people fiddled with the Elmish.WPF architecture to allow more control logic to be taken out of explicit bindings (e.g. into some variant of a repeatedly-called view function)

Binding.subModelSeq : passing sub-model to the sub-model bindings function

Say you have a list of items you want to display. These items can be of different types, complex enough to have distinct models, bound to different Xaml representations (the xaml code pick them through ItemsControl's ItemTemplate or other ways).

Those different types of item, if displayed through Binding.subModelSeq, must share the same SubModelSeqSpec, and it must contains the bindings of all possible types of item. For instance, one of your item is a DateTime to be displayed with a DatePicker with bindings on SelectedDate, DisplayDateStart and DisplayEndDate ; another is a choice displayed through a CombBox with bindings on SelectedItem and ItemsSource ; then the bindings list has to have binding specs to all these properties, no matter that the date item don't need the list of choice or the choice item don't need the date limits.

So the idea is that you can pick the right bindings list when the Binding.subModelSeq function is called.

Its signature would change like that :

subModelSeq
      getModels: ('model -> #seq<'subModel>) ->
      getId: ('subModel -> 'id) ->
      bindings: ('subModel -> BindingSpec<'subModel, 'subMsg> list) ->
      toMsg: ('id * 'subMsg -> 'msg) ->
      name: string ->
      BindingSpec<'model,'msg>

or maybe :

subModelSeq
      getModels: ('model -> #seq<'subModel>) ->
      getId: ('subModel -> 'id) ->
      bindings: ('id -> 'model -> BindingSpec<'subModel, 'subMsg> list) ->
      toMsg: ('id * 'subMsg -> 'msg) ->
      name: string ->
      BindingSpec<'model,'msg>

From there, the sub-model bindings function can pick a fitting list of binding from the sub-model type.

It breaks backward compatibility, but the changes are easy to handle for someone who don't care. Just change your sub-model bindings definition from

let bindings () = ...
to
let bindings _ = ...

A similar change could be done to Binding.subModel and Binding.subModelOpt.

Review of paket setup

@et1975 I have set up paket on the rewrite branch now. Does it seem sane? I'm mostly thinking of these two files:

framework: auto-detect
source https://api.nuget.org/v3/index.json
nuget Elmish prerelease
nuget FSharp.Core >= 4.1.17 redirects:force

Elmish.WPF/paket.lock

Lines 1 to 6 in 1a0c89e

RESTRICTION: || (== net461) (== net471)
NUGET
remote: https://api.nuget.org/v3/index.json
Elmish (2.0.0-beta-4)
FSharp.Core (>= 4.5.2)
FSharp.Core (4.5.2) - redirects: force

I have not set up any template file yet, since that is dependent on the build script I'll be getting to soon.

Application must be instantiated before call to window constructor due to resources in App.xaml

When using Program.runWindow, the window must necessarily be instantiated first. Since Application isn't instantiated before calling runWindow, I get the following exception on the InitializeComponent() call in the window constructor:

Exception: Cannot find resource named 'MaterialDesignMultiFloatingActionAccentPopupBox'. Resource names are case sensitive.

This error occurs because the resource in question is defined in my App.xaml:

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
      <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml" />
      <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>

Since this is not instantiated/loaded before the window is initialized, I am unable to initialize my main window to start the app.

What is the best way to solve this? Is there something I can do, or must this be fixed in Elmish.WPF?

Remove oneWayLazy (and keep only oneWayLazyWith)?

The more I use oneWayLazy/oneWayLazyWith, the more I see that in order to use this as an effective optimization (which is the sole purpose of this function) users have to take an active stance on how the output of get should be compared (i.e, have to use oneWayLazyWith in order to control the equality comparison).

I have begun considering removing oneWayLazy altogether and just keep oneWayLazyWith because the former can lure users into a false sense of added performance. I have not fully decided yet, and am open to counter-arguments. The reasons for this suggestion are:

  • Not performant: In many cases, oneWayLazy (using the default equality comparison, (=)) will not be performant, and thus oneWayLazyWith must be used instead. This can for example happen if the output of get is a large list/map of records, which will be compared structurally. If you have an immutable model (which I'd guess most users have), you can use reference equality (e.g. LanguagePrimitives.PhysicalEquality), which will be much more performant. (If you select more than a single item in get, e.g. in a tuple, you can write an equals function that compares each item individually, using referential comparison for the relevant items.)

  • Avoids misleading users: Keeping oneWayLazy as an "official" function in the Binding module could give users the impression that they can just use this instead of oneWay to gain added performance, whereas in many cases this will not be true (as explained above).

  • Simplifies the API:

    • Removing oneWayLazy simplifies the API, removing an "overload" that might be harmful.
    • This is further relevant since I have added oneWaySeqLazyWith (on master, not yet released). In this case, it's almost entirely pointless to use a oneWaySeqLazy with default equality comparison, since the output of get will likely contain a large sequence (which is the primary use-case of oneWaySeq in the first place).
    • Additionally, the ...lazyWith functions could be renamed ...lazy for brevity.
  • Just as easy to use: If users really, actually want to use the default equality comparison (there are always some valid use-cases), then oneWayLazyWith is really just as easy to use as oneWayLazy; just use (=) for the equals parameter. That's four extra characters (including space), and the bonus is that users have now taken an explicit stance on the equality comparison, Furthermore, if they should need a more complicated comparison, the bar is now lower than it would have been if they had to switch to oneWayLazyWith if they haven't used it before.

  • Not that breaking: Since oneWayLazy is relatively new, there likely won't be too many breaks in practice, and if there are, the migration migration path from oneWayLazy to oneWayLazyWith is clear and trivial.

Opinions welcome.

[Question] Use a strongly typed ViewModel

Hi,

I have to admit that I haven't yet had much time to play with it, I just peeked a bit at the samples.
Do you think it would be possible to base the ViewModel that's passed to the view on something strongly typed?

Rider (and to a lesser degree VS) has support for IntelliSense and CodeCompletion inside Xaml. Supporting that would help a lot to avoid typos (or missed refactorings) inside the views.

I'm not quite sure how the programming model for that could look. One idea may be to define an interface, which is used at design time. Then at runtime, when the viewModel is filled, it is checked that each property also exists in the interface.

Taking the example from the readme:

    let view _ _ = 
        let clockViewBinding : ViewBindings<ClockModel,ClockMsg> =
            [ "Time" |> Binding.oneWay (fun m -> m.Time) ]

        [ "Increment" |> Binding.cmd (fun m -> Increment)
          "Decrement" |> Binding.cmdIf (fun m -> Decrement) (fun m -> m.StepSize = 1)
          "Count" |> Binding.oneWay (fun m -> m.Count)
          "StepSize" |> Binding.twoWay (fun m -> (double m.StepSize)) (fun v m -> v |> int |> SetStepSize)
          "Clock" |> Binding.vm (fun m -> m.Clock) clockViewBinding ClockMsg ]

For this I would predefine an interface

type IMyViewModel =
  abstract Time : DateTime
  abstract Increment : ICommand
  abstract Decrement: ICommand
  abstract Count: int
...

Then if I wrote "Incrmnt" |> Binding.cmd (fun m -> Increment) by mistake, it would fail (or at least print to debug output) that Incrmnt is not defined in the interface.


My broad wish is just to be able to use a strongly typed view-model, there is probably a better solution than my rambling above. =)

Binding Collections

If my model contains a list and I have Add and Remove messages to add, how would I bind the list to a grid or listview in the view? Thanks.

Change order of oneWayLazyWith arguments?

The order of the arguments to oneWayLazyWith doesn't sit right with me. The current order (excluding name and adjusting type parameter names for clarity) is:

(equals: 'intermediate -> 'intermediate -> bool)
(get: 'model -> 'intermediate)
(map: 'intermediate -> 'final)

(The oneWayLazy function is the same, but doesn't have the equals parameter.)

Placing equals first only benefits partial application usages, which is irrelevant for this function. After having used it some, I find it unnatural to specify the equality check (for the intermediate values) before specifying the getter that maps the model to the intermediate values.

My suggestion is to move equals either between get and map:

(get: 'model -> 'intermediate)
(equals: 'intermediate -> 'intermediate -> bool)
(map: 'intermediate -> 'final)

Or alternatively after map:

(get: 'model -> 'intermediate)
(map: 'intermediate -> 'final)
(equals: 'intermediate -> 'intermediate -> bool)

This is, of course a breaking change. We're still on 2.0.0-beta and this is a relatively new function, so I'm not too concerned about this, but opinions are welcome.

Command source not always updated when command can execute

After updating to the latest release and modifying my code to make use of command parameters rather than one way bindings updating the model, the command sources no longer check whether the command can execute when the UI changes (specifically, when the UI property bound to the CommandParameter changes). Also, CommandManager.InvalidateRequerySuggested had no effect. When I looked at the implementation of Command, I see that it has a CanExecuteChanged event implemented such that it is only raised when the view model is updated, as part of its notify function. If it could be augmented to raise the event when the CommandManager.RequerySuggested event is raised, then it should allow UI changes which do not modify the view model to also update commmand sources.

Access to WPF Application instance

I wanted to use Application.DispatcherUnhandledException. In Program.fs of Elmish.WPF we can see the application object being created, but not exposed in any way. However, it is called with "window" - that would be the main window in this case - as argument. I added a handler to the main window's Loaded event, and in that handler Application.Current is available (no longer null, to be precise) and its properties can be set, among them DispatcherUnhandledException. In that handler, the argument property "Handled" can be set to true to prevent further processing of the exception, and so hopefully keep the application running. Many developers choose to implement a dialog with Ignore/Abort or something in this handler, and also logging of unhandled exceptions. I also have a habit of setting app.ShutdownMode to OnMainWindowClose, since I believe that's what most users would expect.

Does this make sense? Is there another more elegant way to access the application object? If not, should Elmish.WPF somehow expose the application object?

How can I send messages / cmds from a ui control ?

Hi,

my problem is that I work with a grid where the SelectedItems property isn't updated, only the list changes but no PropertyChanged event is raised, so I have to use the SelectionChanged event without binding.
I thought about something like this, in the code behind of my user control, where Program.Select is the message type.

this.Grid.SelectionChanged 
 |> Event.map currentItems
 |> Event.map Enumerable.OfType<Service.Patient>
 |> Event.map (List.ofSeq >> Program.Select)
 |> Event.add dispatchToProgram

New binding SubBindingSeq: Access to main model, dispatch main messages, less boilerplate

I'd like to announce and explain a new binding I think makes Elmish.WPF much more usable and conformant to The Elm Architecture (TEA). I just checked it into master (c75cf36), and I welcome opinions before I release it. See the end of the post for the documentation and signature of the new binding.

I have in several issues in this repo lamented the fact that collections seemed to require dedicated sub-components with their own TEA models, message types, and update functions. This has certain drawbacks, most notably the fact that any main component state needed in the child component bindings needs to be duplicated in the child component model, and thus also needs to be kept up to date using dedicated child messages. This leads to a lot of boilerplate and potential for bugs as you extend your app and have to add more and more child components and/or more and more global state needed by child components.

Well, think long and hard enough and you might come up with a good solution. I have arrived at a binding that has actually been supported all along using subModelSeq, but the necessary (non-trivial) boilerplate is now done in a new binding called subBindingSeq.

As a first-order approximation it works similar to subModelSeq (and indeed uses the exact same internals), but the new binding is different in several important ways that makes it much more similar to dynamic views in TEA. First of all, the model you have access to in the sub-bindings is a tuple where the first element is the main model, and the second element is the child element (similar to in TEA where you'd map over a collection, having access to each item and closing over the main model). Secondly, there is no child message type; the bindings work with the main message type (implying that these should carry an identifier of the child element for use in the main update function, like in TEA). Finally, the sub-elements are not full TEA components; they are in fact domain models that are already naturally part of your state.

As you perhaps can see, this means that the method for creating bindings for collection elements is significantly more like in proper TEA. While views are still static, you can at least now compose bindings without needing full TEA child components.

("Where's the sub-binding function for a single sub-element", you ask? There is no such function. These bindings should be part of the root bindings as per proper TEA.)

You can check out the SubBindings sample for an example. This is an adapted version of the SubModelCollection sample that I like significantly better due to the simpler architecture. Not having separate main and child messages for the recursive counters also allowed me to fix the
problem present in SubModelCollection where I have no idea how to dispatch the relevant main messages from the top level of the HierarchicalDataTemplate tree.

I'll release this sooner or later, but any opinions are welcome and might impact the final form of the binding. You can play around with it right now by cloning the latest master branch.

Below is the documentation and signature of the new binding.

/// <summary>
///   Creates a binding to a sequence of sub-items (but not sub-components),
///   each uniquely identified by the value returned by the getId function
///   (as determined by the default equality comparer). You typically bind
///   this to the ItemsSource of an ItemsControl, ListView, TreeView, etc.
///   Analogous to a real Elm architecture, the child bindings have access to
///   the main model state along with the sub-item, and dispatch the top-level
///   message type. The model in the sub-bindings is a tuple with the main model
///   as the first element and the child item as the second element.
/// </summary>
/// <param name="getMainModel">
///   Gets the main model from the current model. This is typically 'id' when
///   called from top-level bindings, or 'fst' when called from sub-bindings
///   at any level.
/// </param>
/// <param name="getSubItems">Gets the sub-items from the current model.</param>
/// <param name="getId">Gets a unique identifier for a sub-item.</param>
/// <param name="bindings">
///   A function that returns the bindings for the sub-item.
/// </param>
/// <param name="name">The binding name.</param>
let subBindingSeq
    (getMainModel: 'currentModel -> 'mainModel)
    (getSubItems: 'currentModel -> #seq<'subModel>)
    (getId: 'subModel -> 'id)
    (bindings: unit -> BindingSpec<'mainModel * 'subModel, 'msg> list)
    (name: string)
    : BindingSpec<'currentModel, 'msg>

Error on adding NuGet Package (2.0.0-beta-6)

Hey,

trying to add Elmish.WPF to a F# ConsoleApplication (.NET Framework 4.7.2) following the Quick Start in the Readme.
Nuget breaks with error
"Unable to find a version of 'Elmish' that is compatible with 'Elmish.WPF 2.0.0-beta-6 constraint: Elmish (>= 3.0.0-prerelease)'."

Manually installing latest pre-release Elmish (3.0.0-beta-2) does not solve the error, NuGet tells me the version of Elmish is not compatible.
What am I doing wrong?

Command actions & Device.BeginInvokeOnMainThread/Dispatcher

It looks like Cmd actions are always executed on the Elmish dispatcher thread. What's the right way within the Elmish.WPF programming model to get a side-effecting action to happen on the Dispatcher (i.e. the GUI thread)? Should we explicitly invoke via the WPF dispatcher or should there be something standard in the programming model to do this?

context: When translating a sample I came across a case in Elm,ish.XamarinForms where the action of a command on a button is to call

Device.OpenUri(uri); 

which is has to be run on the main thread. My first instinct was to do this:

type Msg = 
    | OpenUri of Uri
    | ...

let update msg model =
    match msg with
    | OpenUri uri -> Device.OpenUri(uri); { model with Count = model.Count + 1 }
    | ...

let view _ _ =
    [ "OpenWebCommand" |> Binding.cmd (fun _ -> OpenUri (Uri("https://xamarin.com/platform"))) 
      "Title" |> Binding.oneWay (fun m _ -> "TTRTTTitle")

but both the Binding.cmd callback and "update" are called on the Elmish dispatcher thread. I could always get around this via Device.BeginInvokeOnMainThread but my code may end up littered with those

Although this is in the context of Elmish.XamarinForms I'd like to do the same thing here in this repo

Binding to events?

Hi all

I'm seeing plenty of examples of Xaml where binding to events via code behind is needed, e.g. see below for a snippet of Xamarin code.

My question is - what's the right way to go about this in Elmish.WPF?

From what I've seen there doesn't seem much alternative to writing the code-behind event handler (apart from massive things like this with very intrusive Xaml) - but how would the event handler capture the Elmish dispatch routine?

Xaml:

        <ListView x:Name="ItemsListView" ItemsSource="{Binding [Items]}" ItemSelected="OnItemSelected" ....  >          

code behind:

    async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
    {
        ...
    }

Commands take two arguments, the first is normally unused

The callbacks for Bindings.cmd and cmdIf and other callbacks take two arguments - the first being the object "sender" propagated from the view.

In Elmish.XamarinForms this sender was actually normally null (actually the boxed unit value) AFAICS and I've removed it.

See https://github.com/Prolucid/Elmish.WPF/blob/master/src/Elmish.WPF/Binding.fs#L10

Does anyone actually use this sender parameter in Elmish.WPF? The programming model would become a bit simpler and less error-prone if we dropped it.

Discussion: MVU architecture gives feedbak loop when UpdateSourceTrigger=PropertyChanged and update loop is sufficiently slow

I have never used Elmish.WPF, but I have a Xamarin.Forms app with a custom Redux-like architecture (which is in many ways similar to Elm). In developing that app I had to grapple with a problem that I upon experimentation also see is present in Elmish.WPF, but which is not a problem in MVVM (using simple data bindings, with no central update loops). I want to start a discussion/brainstorming around this problem and am wondering if any of the contributors to this repo, or @dsyme (cc due to Elmish.XamarinForms), or anyone else for that matter, have any input.

Consider the following situation:

  • You have a text input field
  • You want to update the UI (validation message, button enabled state, etc.) as the user types, and thus have to use UpdateSourceTrigger=PropertyChanged (AFAIK in Xamarin.Forms this is the default/only behaviour)
  • The Elmish/Redux update loop runs slower than the the user is typing (i.e., the user can type a second character before the update loop has completed for the first character)

In this situation, in both WPF and Xamarin.Forms, you get a feedback loop messing up the text in the input field. This should come as no surprise, as you are in effect triggering an event (TextChanged or whatever it's called) from its event handler (the dispatcher, through the subsequent view update function), it's just that in most cases, it works fine because the cycle completes before the user has done anything else, and the event is thus not fired since the input field value is still equal to what the update/view function want to set it to.

Specifically, here’s what happens:

  1. The user types a in the input field, causing TextChanged to fire on the input control
  2. Update loop 1 starts with message UpdateText "a"
  3. The user types b in the input field (before update loop 1 has completed)
  4. Update loop 2 starts with message UpdateText "ab"
  5. Update loop 1 completes, and the text field is updated to contain a. Since this is a change from its previous value ab, a new TextChanged is fired, and update loop 3 starts with message UpdateText "a".
  6. Update loop 2 completes, and the text field is updated to contain ab. Again a new TextChanged is fired, and update loop 4 starts with message UpdateText "ab".
  7. The cycle repeats until the (non-deterministic) timings happen to match up so that the input field value stabilizes and no more events are triggered.

This can easily be tested using the Counter sample in this repo with the following simple changes:

  • Add a new string field to the model
  • Add a new message case (e.g. UpdateText of string) to update it
  • Add the new case to the update function but run e.g. System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds 100.) first to ensure the update loop takes long enough for the problem to occur (I have of course observed this in a real app without having to block the update function with Thread.Sleep)
  • Add a new text box for the model’s new string field
  • Bind the Text property and set UpdateSourceTrigger=PropertyChanged (e.g. Text="{Binding MyText, UpdateSourceTrigger=PropertyChanged}")

Then start the app and type quickly into the text box to observe the behaviour.

A quick test (#40 (comment)) shows that in Elmish.XamarinForms, the problem does not occur because the UI is blocked while the update function runs. While this avoids the feedback loop, blocking the UI is also not optimal.

The way I have solved this in my real app:

I have used a mix of Redux (similar to Elmish) and normal, but simple view models. All of the important, central state is modified in the update function and stored in the central state object. But each view still has a view model that acts as the binding target. The view model receives model updates whenever an update loop completes (the code that instantiates the view model subscribes to model updates and sets the updated model – or rather, for simpler testing, the necessary subset of it – to a property on the view model). But the important part in this discussion is that text input fields do not go through the central update loop. They are bound only to mutable properties on the view model (as in normal MVVM), and button states/validation messages/etc. (read-only properties on the view model) are then based on these mutable properties together with the central model state.

For example, a view model might look like this (excluding INotifyPropertyChanged implementation) (writing this from the top of my head, so please excuse any syntax errors):

type MyVm(initialData: MainAppModel) =
  member val Data = initialData with get, set
  member val Username = initialData.PrefilledUsername with get, set
  member val Password = "" with get, set

  member this.ShowActivityIndicator =
    this.Data.IsSigningIn

  member this.SignInButtonEnabled =
    not this.Data.IsSigningIn
    && not (String.IsNullOrEmpty this.Username)
    && not (String.IsNullOrEmpty this.Password)

In spite of the mutability, this solution is still functional in nature. The view models are completely dumb and are in essence pure functions, whose inputs are their mutable fields (the main model, text input field values, a continually updated clock, etc.) and whose outputs are their read-only fields. They are, thus, fairly trivially testable.

(I know that was really quickly and roughly explained; let me know if more details are needed. I have considered presenting the solution in a blog post, but I have no immediate plans to do so unless requested.)

There are a few complications one might come across in real-life scenarios; for example, the VM might have to react to messages. For example, in the view model above, there is no way for a successful registration (done from another view/VM in the foreground, with the sign-in view in the background) to automatically fill the recently registered username. To provide this functionality, I have given the view models that need it a simple "update"-like function with the signature 'msg -> unit. For example:

member this.OnDispatch message =
  match message with
  | RegistrationCompleted { Username = u } -> this.Username <- u
  | _ -> ()

The code that instantiated the VM (which already subscribes to state updates and sets the Data property) then subscribes to messages that are dispatched (a simple thing to do since I'm using a homegrown architecture) and passes them to the OnDispatch function above.

One could then say that overall, these VMs, in addition to being binding targets, play the role of a mini Redux store for localized state in a single part of the app.

Another complication I won't go into detail about are view models for elements of ListView et al. I
like to avoid codebehind, but I needed a timer in order to update arbitrary elements of a ListView every second, and I found no better solution than to simply place the timer in the view codebehind, updating a Time property on the element's VM.

In any case, having used this solution for a while, I'm fairly happy with it. It's not technically purely functional, but most of us can't afford to be idealists. It's practically purely functional, and the important part is that it's clear (once you understand it; my explanation here may have been insufficient) and easily testable.


What I’m wondering about then, and hope to start a fruitful discussion about, is the general problem I have described with the update feedback loop in Elmish/Redux architectures. The problem is partly a general performance problem of an immutable architecture, but is also more specific than that. Some questions to get started:

  • Is there any way to solve this without reverting to partly mutable (half-MVU/half-MVVM) solutions as I have described?
  • Is this a problem in Elm (or Elmish), and if not, why not?
  • If a mutable compromise is needed (at least in WPF/Xamarin.Forms), are there “better” ways to solve the problem than what I have described?

Tutorial miss a step

It’s very minor, but the tutorial from the readme file miss a step to define the init function.

How to send subsequent messages?

As in another thread written, I have a list of buttons with their commands. When such a command is executed, I want to send a message to the status bar with either success or error message.

    let buttonUpdate (model:ButtonModel) =
        match model.Function with
        | Command cmd -> commandExecute cmd |> ignore
     sendMsg "Program executed"

Probably all I need is an example of Cmd.performFunc, I assume?

DataGrid.ItemSource SubModelSeq binding SelectedItem problem

I'm moving my code to the new Elmish.WPF version and I'm having a problem making a DataGrid behave. The problem is related to the DataGrid tracking the SelectedItem (or SelectedIndex, or SelectedValue, the symptom is the same):

If you bind a SubModelSeq to a DataGrid.ItemSource and let the rows have a TwoWay binding to the elements, the DataGrid will lose the SelectedItem as soon as any change happens. So if you select a cell to edit it, the row gets selected, you type one letter (in case of a UpdateSourceTrigger=PropertyChanged binding), the row loses the selection.

The data flow works nicely both ways through model changes and messages, but the selection being lost, besides the fact that you might need to know the selected item as I do, also causes UI problems, the DataGridCells don't leave edit mode properly because of it.

For the Elmish approach, using a TwoWay SelectedValue Binding + SelectedValuePath that points to the Id on a DataGrid looks perfect, the values should be able to change and the datagrid should match the items by comparing ids, but it doesn't work, the DataGrid still loses the selection after each update.

I found a suggestion, that having the same GetHashCode on the item before and after the edit would solve the problem, but since the ViewModels are updated and not replaced that should already be true, right?

If the cause and solution are non-obvious, I will prepare a reproduction example.

Design time DataContext fails with "Method not found .. Elmish.WPF.Utilities.ViewModel.designInstance .."

My WPF user controls have their design time DataContext set like this. Some relevant lines shown.

<UserControl x:Class="TdClient.Views.AboutPane"

        xmlns:models="clr-namespace:TdClient.Models;assembly=TdClient.Models"

        d:DataContext="{x:Static models:AboutPane.designTimeViewModel}"

The designTimeViewModel looks like this. Some relevant lines shown.

namespace TdClient.Models

module AboutPane =

    let designTimeViewModel =
        let m = {
            VersionString = "1.2.3.4"
            }
        Elmish.WPF.Utilities.ViewModel.designInstance m (bindings())

Some months ago I started getting this error message in all the controls that use a design time model, and the design time data does not show in the designer.

Method not found: 'Elmish.WPF.Internal.ViewModel'2<!!0,!!1> Elmish.WPF.Utilities.ViewModel.designInstance(!!0, Microsoft.FSharp.Collections.FSharpList'1 <Elmish.WPF.Internal.BindingSpec'2<!!0,!!1>>)'.

(I had to replace backticks with plain ticks in the error message, to make the markup happy.)

This error message is displayed in a box above the line with DataContext in the XAML. That entire line is also underscored with a wavy blue line.

VS 2017, latest update. Elmish.WPF 2.0.0-beta-8

Performance suggestion: Make the mapper in oneWayMap bindings lazy or memoized

As far as I can see, oneWayMap represents an optimization over oneWay in the sense that PropertyChanged will not be triggered unless the return value of getter has changed. However, if there are multiple bindings from XAML to a single oneWayMap (e.g. one to display text and another to select a color based on the text using an IValueConverter), then the mapper will still be called several times.

It seems to me there should be a fairly simple fix wherein Elmish.WPF could use lazy to ensure that until the getter output has changed (or, if simpler, until the model is updated, though that's a lesser optimization), mapper is called at most once.

I haven't been able to figure out how, though. Any thoughts?

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

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

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.