microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.34k stars 677 forks source link

x:Bind Function with Boolean return can't be bound to Visibility property #5514

Open michael-hawker opened 3 years ago

michael-hawker commented 3 years ago

Describe the bug If you have a function you can use with x:Bind that returns a bool, it can't also be used for a property expecting a Visibility type. This works fine when binding a bool based property to a Visibility type with x:Bind, but not with a function. Functions are missing the implicit type conversion step done elsewhere for XAML. 🙁

This means that we have to duplicate our function to work for both boolean and Visibility based properties. 🙁

It'd be great for the x:Bind system to be able to do the same implicit type conversions done elsewhere with properties.

Steps to reproduce the bug You can see in the following example that a bool value can be bound directly to the visibility of an item (the green rectangle) as well as negated with a function and bound to a bool based property (checkbox).

However, trying to use that same function to bind to a Visibility based property results in an error about types:

    <StackPanel
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Orientation="Vertical"
        Spacing="8">
        <TextBlock>
            <Run Text="Boolean Value: " />
            <Run Text="{x:Bind MyBoolean}" />
        </TextBlock>

        <TextBlock Text="Click button to toggle" />
        <Button x:Name="myButton" Click="myButton_Click">Click Me</Button>

        <TextBlock Text="Checkbox is bound to a function to negate the value:" />
        <CheckBox IsChecked="{x:Bind local:MainWindow.Negate(MyBoolean)}" IsEnabled="False" />

        <Rectangle
            Width="200"
            Height="100"
            Fill="Green"
            Visibility="{x:Bind MyBoolean}" />
        <!--  Return type of 'Negate' must be of type 'Visibility'.  -->
        <!--
        <Rectangle Visibility="{x:Bind local:MainWindow.Negate(MyBoolean)}" Width="200" Height="100" Fill="Red"/>
        -->
    </StackPanel>
    public sealed partial class MainWindow : Window, INotifyPropertyChanged
    {
        private bool _value;
        public bool MyBoolean
        {
            get { return _value; }
            set {
                _value = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyBoolean)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void myButton_Click(object sender, RoutedEventArgs e)
        {
            MyBoolean = !MyBoolean;
        }

        public static bool Negate(bool value)
        {
            return !value;
        }
    }

Expected behavior Able to use the same bool based function with x:Bind for Visibility based types. Ideally, any implicit conversions between types can be performed.

Because of this issue it means you have to create functions explicitly for both boolean values and Visibility values even if the logic and input to that function is the same.

Version Info The example above is with the Windows App SDK 0.8.1, but this is also a problem in WUX based OS xaml. I tested with WinUI 3 to ensure that it wasn't fixed already there, but the problem remains. 🙁

Windows app type: UWP Win32
Yes Yes
Windows 10 version Saw the problem?
21H1 Build (19043) Yes
October 2020 Update (19042)
May 2020 Update (19041)
November 2019 Update (18363)
May 2019 Update (18362)
October 2018 Update (17763)
April 2018 Update (17134)
Fall Creators Update (16299)
Creators Update (15063)
Device form factor Saw the problem?
Desktop
Xbox
Surface Hub
IoT

Additional context Related to:

StephenLPeters commented 3 years ago

@RealTommyKlein FYI

julianxhokaxhiu commented 2 years ago

This is still an issue today. Any progress on that regard?

hawkerm commented 2 years ago

Encountered this again today, as it's not just static functions but any functions.

One of the most common things to do is inverting a bool in order to show something when a value is false.

Ideally, one could just do this:

<Border Visibility="{x:Bind MyBoolProperty.Equals(x:False), Mode=OneWay}"/>

And be done, but with this bug, you now need a custom converter, or a custom function to return specifically a Visibility value. ☹

michael-hawker commented 1 year ago

Bump

Jay-o-Way commented 9 months ago

Hey all, I'm reading https://learn.microsoft.com/nl-nl/windows/uwp/xaml-platform/x-bind-markup-extension#remarks and there we see that the BoolToVisibility converter is built-in. Does that "negate" this issue?

hawkerm commented 9 months ago

@Jay-o-Way no, the built-in converter is only for properties, this bug is about the second part of the doc note about it not working automatically with functions that return a bool:

can bind a Visibility property to a Boolean without creating a converter. Note that this is not a feature of function binding, only property binding.

I imagine this should just be a matter of the x:Bind generator detecting a boolean return type of a function bound to a Visibility parameter and applying the same logic it does for properties?