Elmish.wpf: Twoway binding Textbox to int value give BindingExpression

Created on 24 May 2018  路  12Comments  路  Source: elmish/Elmish.WPF

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}" />

Most helpful comment

I know it's dead horse, but I'm going to kick it again...
F# and Elm(ish) are very explicit about any and all conversions that might happen to your data. Given that as a context, saying that something works automagically in C#/XAML is an invalid argument. An obvious counter-example to automatic string -> int conversion: what if I want to enter in base-16?
In general, automation of data forms has been discussed elsewhere in elmish, with assumptions like "ints will be only entered in base-10" in mind, and the feeling is this should be implemented (just like in Elm) as a layer above the primitives of elmish and wpf, i.e. a separate library designed specifically for making data entry forms easier to construct.

All 12 comments

Since the TextBox.Text property is of type String, you'll have to cast in both directions (you're already doing it for the setter):

Binding.twoWay (fun m -> (string m.Count)) (fun v m -> v |> int |> SetCount)

This also encourages you to think about what happens if the user enters something other than an int. There is also an example of this in the samples.

ah yes, that was a stupid mistake. Thanks for help ;)

@giuliohome This seems to be working as intended - implementing your desired functionality is trivial.

From what I understand, you have a model like this

```f#
type Model = { MyInt: int }
type Msg = SetMyInt of int

And you want to set up a binding like this:

```f#
"MyInt" |> Binding.twoWay
  (fun m -> m.MyInt)
  (fun v _ -> SetMyInt v)

As you have seen, that doesn't work, because in the setter above, v is of type string (e.g., it's "42", not 42). I expect the reason for this is that the DynamicObject we use as a view model can not provide type information to the bindings (everything is obj), but I'm not sure.

In any case, the point is rather moot, becuase it's trivial to replicate the XAML behavior you desire. Just define the following function anywhere in your app:

```f#
let parseInt str =
match Int32.TryParse str with
| true, i -> Ok i
| false, _ -> Error "" // Your error message here

Then you can write all your `int` binding like this:

```f#
"MyInt" |> Binding.twoWayIfValid
  (fun m -> string m.MyInt)
  (fun v _ -> v |> parseInt |> Result.map SetMyInt)

As an added bonus, it's really easy to set your own error message, add more validation (using e.g. Result.bind for composition), etc. You are much more flexible this way, because you are not bound by what rudimentary validation functionality XAML already provides, and as you can see it's fully type safe and not significantly more verbose than a simple Binding.twoWay. It's even easy to change to Binding.twoWayValidate if you want to inform your model of invalid values in order to use it for other purposes (e.g. disabling a button, as shown in the Validation sample).

In general, F# and functional programming favors explicit over implicit, and has a syntax that means explicitness does not necessarily come at the expense of significantly increased verbosity.

I apologize if I have in any way come off as condescending; that has certainly not been my intention. If I have misunderstood your message, I kindly ask you to enlighten me on the following points so we can understand each other:

  • Have I misunderstood your use-case? (I described my understanding of this in the first two code snippets of my reply.)
  • You state that my "workaround" "is wrong" - if there is anything that is impossible or much more difficult to accomplish using Binding.twoWayIfValid rather than binding directly using ints in Binding.twoWay, please describe this situation so I can understand it.

There's a TryConvert method in DynamicObject, I can't look into further details now, but that's maybe where the solution stands.

I don't think that'll work; if I understand the documentation correctly, DynamicObject.TryConvert is used for converting the entire DynamicObject instance, not its members.

@cmeeren : my bad, you're right.

This line seems to be a point where to do a type cast/check at first glance.

If I understood your initial report correctly (again, please clarify if not), you were using twoWay, not twoWayValidate or twoWayIfValid. The line you link to is only relevant for twoWayValidate.

In any case: I'm no expert at WPF internals and how it handles bindings to a DynamicObject, but I would guess that since all members are surfaced as object (since that's what TryGetMember returns via its out parameter), it treats the property as the default type for the binding target, which in the case of TextBox.Text I would expect to be string. (Or, to put it another way: I expect that if TextBox.Text is bound to a strongly typed property of type int, the binding system uses reflection to obtain that type information and automatically uses a simple converter that throws an exception if the input text is not a valid int, whereas for a DynamicObject view model this isn't possible since you can't use reflection to obtain member information.)

I'm also available to try to fix this, with low priority, in case it is reopened.

As I have said, I am not convinced there is a problem here at all (please clarify according to my previous questions), but if you think you have an improvement of the current behavior, feel free to submit a PR and we can have a look at it.

According to this SO question and answer, it seems my suspicions regarding binding to object members was correct. I can't see any way of telling the XAML binding engine which type a DynamicObject member has.

I know it's dead horse, but I'm going to kick it again...
F# and Elm(ish) are very explicit about any and all conversions that might happen to your data. Given that as a context, saying that something works automagically in C#/XAML is an invalid argument. An obvious counter-example to automatic string -> int conversion: what if I want to enter in base-16?
In general, automation of data forms has been discussed elsewhere in elmish, with assumptions like "ints will be only entered in base-10" in mind, and the feeling is this should be implemented (just like in Elm) as a layer above the primitives of elmish and wpf, i.e. a separate library designed specifically for making data entry forms easier to construct.

I think you are the one misunderstanding the context here based on some expectation that things are supposed to work a certain way simply because they work that way in other libraries.

A TextBox always takes input a string input. Sure, you can type a number, and perhaps default xaml binding will auto-cast it for you, but the point here is to have that transformation and associated validation be explicit, in following with the F#/elmish paradigm as explained by @et1975. Other controls, for example IntegerUpDown, might take an int as input, in which case no validation would be required since the destination in your model matches the underlying control type. Depending on the use case, that could be a better choice for an int property in your model.

If you prefer to use Gjallarhorn, or regular xaml, no one is stopping you. Also, unless you can be constructive in your comments rather than confrontational as you have been, please do as you keep threatening to and unsubscribe.

@2sComplement I think your comment is completely useless

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TysonMN picture TysonMN  路  7Comments

BentTranberg picture BentTranberg  路  13Comments

Evangelink picture Evangelink  路  7Comments

TysonMN picture TysonMN  路  10Comments

felipeleon73 picture felipeleon73  路  14Comments