Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Use BindBack function return value. #7888

Open
dag23 opened this issue Nov 5, 2022 · 5 comments
Open

Proposal: Use BindBack function return value. #7888

dag23 opened this issue Nov 5, 2022 · 5 comments
Labels
area-Binding feature proposal New feature proposal team-Markup Issue for the Markup team

Comments

@dag23
Copy link

dag23 commented Nov 5, 2022

Proposal: Use BindBack function return value.

Summary

BindBack does not do anything with the return value of the bound function. To match what an IValueConverter does, the return value of the function should be assigned to the function parameter of the function bound in x:Bind.
This should work:
<TextBox Text="{x:Bind converters:Converters.IntegerToString(ViewModel.MyInteger), BindBack=converters:Converters.StringToInteger, Mode=TwoWay}"/>
But instead it is necessary to do:
<TextBox Text="{x:Bind converters:Converters.IntegerToString(ViewModel.MyInteger), BindBack=ViewModel.SetMyIntegerWithString, Mode=TwoWay}"/>

Rationale

  • It would substitute 100% of a two way IValueConverter without having to create void Set[Property]With[Type] methods in the ViewModel exclusively for the purpose of BindBack.
  • The void Set[Property]With[Type] methods in the previous point break the MVVM separation since the methods are only for BindBack, which is a feature the ViewModel should not be aware of.

Important Notes

Since IValueConverter methods only have one value parameter, this only works when there is only one function parameter bound in x:Bind.

@dag23 dag23 added the feature proposal New feature proposal label Nov 5, 2022
@ghost ghost added the needs-triage Issue needs to be triaged by the area owners label Nov 5, 2022
@shelllet
Copy link

does bindback support addional parameters ?

such as:


<RadioMenuFlyoutItem x:Uid="InputFlyoutItem/Number" Text="Number" 
                     IsChecked="{x:Bind IsChecked2(local:UserInputType.Number), BindBack=NumberChecked, Mode=TwoWay}" 
                     Visibility="{x:Bind IsVisible(local:UserInputType.Number)}" GroupName="{x:Bind Header, Converter={StaticResource StringConverter}}"/>

<RadioMenuFlyoutItem x:Uid="InputFlyoutItem/Boolean" Text="Boolean" 
                     IsChecked="{x:Bind IsChecked2(local:UserInputType.Boolean), BindBack=BooleanChecked, Mode=TwoWay}" 
                     Visibility="{x:Bind IsVisible(local:UserInputType.Boolean)}" GroupName="{x:Bind Header, Converter={StaticResource StringConverter}}"/>

<RadioMenuFlyoutItem x:Uid="InputFlyoutItem/Point" Text="Point" 
                     IsChecked="{x:Bind IsChecked2(local:UserInputType.Point), BindBack=PointChecked, Mode=TwoWay}" 
                     Visibility="{x:Bind IsVisible(local:UserInputType.Point)}" 
                     GroupName="{x:Bind Header, Converter={StaticResource StringConverter}}"/>

I need pass addional param local:UserInputType and save a lot of bindback function.

@Scottj1s Scottj1s added area-Binding and removed needs-triage Issue needs to be triaged by the area owners labels Dec 2, 2022
@hawkerm
Copy link

hawkerm commented Dec 13, 2022

Yeah, this is not how I expected BindBack to work at all. Seems completely useless in its current form. It means I can't have a generalized conversion function for types (like I kind of have for OneWay bindings) and have to create separate individual functions for each property and need to have a reference to it???

I'm trying to convert from an enum value to a bool using x:Bind for a ToggleSwitch, but just going back to old IValueConverter seems like it's going to be the route I have to take... This seems like an oversight for a pretty standard scenario.

With all the new source generator work being done in .NET, it'd be nice to see the XAML Compiler be part of the WinUI repo so that improvements for UWP and WindowsAppSDK XAML based generated code could be improved upon and shipped for these types of QoL improvements. They seem like easy wins.


Agree that the output from the BindBack function should just be set to the property that's bound to (or if a function is called there, then whatever property is being used as the first parameter). That's going to be the common pattern in the majority of scenarios.

This is how the doc example seems to imply that this should work:

<TextBlock Text="{x:Bind a.MyFunc(b), BindBack=a.MyFunc2, Mode=TwoWay}" />

the function should take one argument which is the value that needs to be pushed back to the model.

pushed back here seems to be the part that is missing that b (the property we're binding to via conversion with a function) is not set with the result of MyFunc2 which is getting the value from the control when it is updated.


In another scenario of mine, I'm simply trying to bind an int? that I have in my ViewModel to a NumberBox control (which requires a double. Based on my intuition and what I was led to believe from the docs, I'd just want to do this:

<muxc:NumberBox LargeChange="10"
                Minimum="1"
                SmallChange="2"
                SpinButtonPlacementMode="Inline"
                Value="{x:Bind help:Bind.ToDouble(MyIntValue), BindBack=help:Bind.ToInt, Mode=TwoWay}" />

And so I created two static helper functions to support the binding to and back as I would have in the past used an IValueConverter for:

    public static double ToDouble(int? value) => value ?? default;

    public static int ToInt(double value) => (int)value;

But this doesn't work. ☹ As ToInt is called and the value just dropped instead of setting the value of (i.e. 'pushed back to') the MyIntValue property as one would expect in this scenario.

            // Generated binding code... does nothing...
            private void UpdateTwoWay_9_Value()
            {
                if (this.initialized)
                {
                    global::System.Double p0 = this.obj9.Value;
                    global::Helpers.Bind.ToInt(p0);
                }
            }

Expect:

            private void UpdateTwoWay_9_Value()
            {
                if (this.initialized)
                {
                    global::System.Double p0 = this.obj9.Value;
                    // TO ADD: Get 'obj' however the generator does for references...
                    obj.MyIntValue = global::Helpers.Bind.ToInt(p0);
                }
            }

@hawkerm
Copy link

hawkerm commented Dec 16, 2022

Yup, just gone back to IValueConverter... Works like a charm.

I guess it at least still gets called more directly with the x:Bind codegen, but I think it still does a lookup to find the converter from the resources...

Would be nice if we had a proper compiled x:Bind equivalent for this scenario.

@LeftTwixWand
Copy link
Contributor

Does anybody know, is it possible to use ternary operator in function binding?

@hawkerm
Copy link

hawkerm commented Mar 24, 2023

@LeftTwixWand not sure why you thought to comment on this issue. Probably best to start a new discussion or ask on StackOverflow? But no, you can't use expressions in functional bindings. You'd have to pass the parameters in as arguments and then call it in the backing function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Binding feature proposal New feature proposal team-Markup Issue for the Markup team
Projects
None yet
Development

No branches or pull requests

6 participants