Bug 44213 - Can't bind to specific properties through OnPlatform
Summary: Can't bind to specific properties through OnPlatform
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms (show other bugs)
Version: 2.3.2
Hardware: PC Windows
: --- enhancement
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-09-12 18:46 UTC by Clint
Modified: 2017-12-28 15:08 UTC (History)
7 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Clint 2016-09-12 18:46:18 UTC
It would be great if we could do OnPlatform binding to specific properties of a control.
It would at least give a way to band-aid this other bug:
https://bugzilla.xamarin.com/show_bug.cgi?id=43664

Let's say you have an ```Entry```, and you want to bind the ```HeightRequest``` with platform-specific values which themselves are bindings.

So not this kind of simple hard-coded values:
```xml
                  <Entry>
                    <Entry.HeightRequest>
                      <OnPlatform x:TypeArguments="x:Double">
                        <OnPlatform.iOS>40</OnPlatform.iOS>
                        <OnPlatform.Android>60</OnPlatform.Android>
                      </OnPlatform>
                    </Entry.HeightRequest>
                  </Entry>
```

but something dynamic more like this:
```xml
                        <Entry.HeightRequest>
                            <OnPlatform x:TypeArguments="x:Double">
                                <OnPlatform.iOS>
                                    <Binding Converter="{StaticResource TimesxConverter}"
                                             ConverterParameter="1.25"
                                             Path="FontSize"
                                             Source="{x:Reference MyEntryControl}" />
                                </OnPlatform.iOS>
                            </OnPlatform>
                        </Entry.HeightRequest>
```
What this would do is take the ```FontSize``` of the ```MyEntryControl```, run it through a converter to get a value * 1.25, then use that result as the ```HeightRequest```.
Problem: This throws a run-time exception of TypeMismatch between ```Binding``` and ```System.Double```.  In other words it doesn't seem to accept that the binding can return a double.

The converter and binding works if not use with OnPlatform:
```xml
                                <Entry x:Name="MyEntryControl"
                                       HeightRequest="{Binding Path=FontSize,
                                                               Source={x:Reference MyEntryControl},
                                                               Converter={StaticResource TimesxConverter},
                                                               ConverterParameter=1.25}"
                                       Placeholder="Pro"
                                       PlaceholderColor="Gray"
                                       Text="{Binding StockNumber}"
                                       Style="{StaticResource EntryStyleLrg}" />
```
Comment 1 Rui Marinho 2016-10-04 18:25:19 UTC
Why dont you check on your converter or view model?
Something like

> if(Device.OS == TargetPlatform.iOS)
Comment 2 Rui Marinho 2016-10-04 18:31:18 UTC
This s a new feature request and will be added to our backlog. 

Thanks.
Comment 3 Clint 2016-10-04 18:34:52 UTC
Using a converter is a band-aid for the actual problem, which is that we can't bind properties directly within OnPlatform in XAML.

Additionally, if we can't supply a value from the XAML then the converter would have to be hard-coded and not be very reusable.  I might need a scale factor of 1.25 for one control and 2.50 on another. 


As for ViewModel - We're talking about the UI... ViewModel shouldn't know anything about UI; that would be a complete violation of MVVM design patterns. 

Thank you for adding it as a feature request
Comment 4 Stephane Delcroix 2017-01-24 13:17:13 UTC
the proposed XAML is invalid, as that would mean the OnPlatform object is the target of the binding to double.

but this makes sense to support:

<OnPlatform x:TypeArguments="BindingBase">
  <OnPlatform.iOS>
    <Binding Converter="{StaticResource TimesxConverter}"
             ConverterParameter="1.25"
             Path="FontSize"
             Source="{x:Reference MyEntryControl}" />
  </OnPlatform.iOS>
</OnPlatform>

That'd mean the OnPlatform returns one Binding or the other depending on the platform.

I made the required change to support that in https://github.com/xamarin/Xamarin.Forms/pull/709
Comment 5 Clint 2017-01-24 13:26:34 UTC
@Stephane.Delcroix

That's wonderful.  Thank you!
Comment 6 Clint 2017-01-24 13:27:56 UTC
OH... Any guess when that might make its way to a stable update of Xamarin.Forms?  I don't get to use 'experimental' code at work.  If its not in the official update we don't put it in product code.
Comment 7 Samantha Houts [MSFT] 2017-02-01 18:12:25 UTC
Should be fixed in 2.3.5-pre1. Thank you!
Comment 9 Carlos Martínez 2017-10-03 07:18:19 UTC
I'm not able to do this.... I do not know if is the binding or the converter: 

Using Xamarin.forms 2.4.0.282



<Image
                                        Grid.Column="4">
                                        <Image.Source>
                                            <OnPlatform
                                                x:TypeArguments="ImageSource"
                                                Android="{Binding Reservation.MailEnviado, Converter={StaticResource BoolToImageConverter}}" />
                                        </Image.Source>
                                    </Image>
                                    <Button
                                        Grid.Column="4"
                                        Command="{Binding ShowWaitingListCommand}">
                                        <Button.Effects>
                                            <renderers:TransparentSelectableEffect />
                                        </Button.Effects>
                                        <Button.Image>
                                            <OnPlatform
                                                x:TypeArguments="FileImageSource"
                                                iOS="{Binding Reservation.MailEnviado, Converter={StaticResource BoolToImageConverter}}" />
                                        </Button.Image>
                                    </Button>
Comment 10 Mike Miller 2017-12-28 15:08:42 UTC
This throws an object reference error for me - Object not set to an instance of an object.

<ct:StyledPicker Grid.Column="1" Grid.Row="8"    
                            Style="{StaticResource FormPickerStyle}"
                            ItemsSource="{Binding TimeZones}" 
                            ItemDisplayBinding="{Binding DisplayName}"
                            SelectedItem="{Binding TimeZoneOffset}">
                        <ct:StyledPicker.Title>
                            <OnPlatform x:TypeArguments="x:String" Android="{Binding Resources[adminPlaceholderTimeZone]}" iOS="{Binding Resources[adminPlaceholderTimeZone]}" Default="" />
                        </ct:StyledPicker.Title>
                    </ct:StyledPicker>

This is just a control based on picker with some additional visual properties.  It works fine if the values are static or if I comment out the Picker.Title.  It won't build with what I have here.