forked from xamarin/Xamarin.Forms
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement OnPlatform/OnIdiom markup extensions (xamarin#2615)
* Added OnPlatform markup extension supporting iOS/Android/UWP for xamarin#2608 * Add Default and Other properties to OnPlatformExtension This allows setting a default value for unknown platforms, as well as specify values for arbitrary platforms by using a named parameter like syntax: `{OnPlatform Android=15, iOS=10, UWP=12, Default=11, Other=Tizen:20}` The `Other` supports multiple semi-colon separated values. By using this format, we can make the string more concise than if we used `=` which would have to be escaped in quotes. For example: `{OnPlatform Default=10, Other=Tizen:22;Xbox:20;Switch=25;PlayStation=22}` Added unit tests that verify all the supported combinations. * Add OnIdiomExtension The extension allows the following syntax: `{OnIdiom Phone=23, Tablet=25, Desktop=26, TV=30, Watch=10, Default=20}` At least one value or `Default` must be specified. `Default` is returned whenever the specific idiom was not specified. * Add missing known platforms and return Default if missing Add all strings that are provided in `Device`. Convert individual asserts into test cases for better reporting. Also, whenever a matching platform value isn't specified, return Default instead of null. * Turn OnIdiom asserts into test cases This makes for easier to read, document and report tests. * Add missing platforms to null check * Make Default the content property * Perform type conversion as expected by XamlC Leverage the conversion that is used elsewhere, to return a properly typed object that can be assigned directly to the property. Update tests with typed values since now we get integers, rather than strings out of the parser. * Add Converter/ConverterParameter support * Message should state that the value must be non-null * Remove Unsupported idiom since it's not useful to set You can use Default instead. * Use new GetService<T> extension method for conciseness * Don't fail if service provider is null * Remove Other from OnPlatformExtension As suggested, this might come back in the future in some other form.
- Loading branch information
1 parent
bf8d706
commit dc62dc1
Showing
6 changed files
with
235 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,22 @@ | ||
using Microsoft.Windows.Design; | ||
using System; | ||
using System.ComponentModel; | ||
using Microsoft.Windows.Design; | ||
|
||
namespace Xamarin.Forms.Xaml.Design | ||
{ | ||
class AttributeTableBuilder : Microsoft.Windows.Design.Metadata.AttributeTableBuilder | ||
{ | ||
public AttributeTableBuilder() | ||
{ | ||
// Turn off validation of values, which doesn't work for OnPlatform/OnIdiom | ||
AddCustomAttributes(typeof (ArrayExtension).Assembly, | ||
new XmlnsSupportsValidationAttribute("http://xamarin.com/schemas/2014/forms", false)); | ||
|
||
AddCallback(typeof(OnPlatformExtension), builder => builder.AddCustomAttributes(new Attribute[] { | ||
new System.Windows.Markup.MarkupExtensionReturnTypeAttribute (), | ||
})); | ||
AddCallback(typeof(OnIdiomExtension), builder => builder.AddCustomAttributes(new Attribute[] { | ||
new System.Windows.Markup.MarkupExtensionReturnTypeAttribute (), | ||
})); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using System; | ||
using System.Globalization; | ||
using System.Reflection; | ||
|
||
namespace Xamarin.Forms.Xaml | ||
{ | ||
[ContentProperty("Default")] | ||
public class OnIdiomExtension : IMarkupExtension | ||
{ | ||
// See Device.Idiom | ||
|
||
public object Default { get; set; } | ||
public object Phone { get; set; } | ||
public object Tablet { get; set; } | ||
public object Desktop { get; set; } | ||
public object TV { get; set; } | ||
public object Watch { get; set; } | ||
|
||
public IValueConverter Converter { get; set; } | ||
|
||
public object ConverterParameter { get; set; } | ||
|
||
public object ProvideValue(IServiceProvider serviceProvider) | ||
{ | ||
var lineInfo = serviceProvider?.GetService<IXmlLineInfoProvider>()?.XmlLineInfo; | ||
if (Default == null && Phone == null && | ||
Tablet == null && Desktop == null && TV == null && Watch == null) | ||
{ | ||
throw new XamlParseException("OnIdiomExtension requires a non-null value to be specified for at least one idiom or Default.", lineInfo ?? new XmlLineInfo()); | ||
} | ||
|
||
var valueProvider = serviceProvider?.GetService<IProvideValueTarget>() ?? throw new ArgumentException(); | ||
|
||
var bp = valueProvider.TargetProperty as BindableProperty; | ||
var pi = valueProvider.TargetProperty as PropertyInfo; | ||
var propertyType = bp?.ReturnType | ||
?? pi?.PropertyType | ||
?? throw new InvalidOperationException("Cannot determine property to provide the value for."); | ||
|
||
var value = GetValue(); | ||
var info = propertyType.GetTypeInfo(); | ||
if (value == null && info.IsValueType) | ||
return Activator.CreateInstance(propertyType); | ||
|
||
if (Converter != null) | ||
return Converter.Convert(value, propertyType, ConverterParameter, CultureInfo.CurrentUICulture); | ||
|
||
var converterProvider = serviceProvider?.GetService<IValueConverterProvider>(); | ||
|
||
if (converterProvider != null) | ||
return converterProvider.Convert(value, propertyType, () => pi, serviceProvider); | ||
else | ||
return value.ConvertTo(propertyType, () => pi, serviceProvider); | ||
} | ||
|
||
object GetValue() | ||
{ | ||
switch (Device.Idiom) | ||
{ | ||
case TargetIdiom.Phone: | ||
return Phone ?? Default; | ||
case TargetIdiom.Tablet: | ||
return Tablet ?? Default; | ||
case TargetIdiom.Desktop: | ||
return Desktop ?? Default; | ||
case TargetIdiom.TV: | ||
return TV ?? Default; | ||
case TargetIdiom.Watch: | ||
return Watch ?? Default; | ||
default: | ||
return Default; | ||
} | ||
} | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
Xamarin.Forms.Xaml/MarkupExtensions/OnPlatformExtension.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using System; | ||
using System.Globalization; | ||
using System.Reflection; | ||
|
||
namespace Xamarin.Forms.Xaml | ||
{ | ||
[ContentProperty("Default")] | ||
public class OnPlatformExtension : IMarkupExtension | ||
{ | ||
public object Default { get; set; } | ||
public object Android { get; set; } | ||
public object GTK { get; set; } | ||
public object iOS { get; set; } | ||
public object macOS { get; set; } | ||
public object Tizen { get; set; } | ||
public object UWP { get; set; } | ||
public object WPF { get; set; } | ||
|
||
public IValueConverter Converter { get; set; } | ||
|
||
public object ConverterParameter { get; set; } | ||
|
||
public object ProvideValue(IServiceProvider serviceProvider) | ||
{ | ||
var lineInfo = serviceProvider?.GetService<IXmlLineInfoProvider>()?.XmlLineInfo; | ||
if (Android == null && GTK == null && iOS == null && | ||
macOS == null && Tizen == null && UWP == null && | ||
WPF == null && Default == null) | ||
{ | ||
throw new XamlParseException("OnPlatformExtension requires a non-null value to be specified for at least one platform or Default.", lineInfo ?? new XmlLineInfo()); | ||
} | ||
|
||
var valueProvider = serviceProvider?.GetService<IProvideValueTarget>() ?? throw new ArgumentException(); | ||
|
||
var bp = valueProvider.TargetProperty as BindableProperty; | ||
var pi = valueProvider.TargetProperty as PropertyInfo; | ||
var propertyType = bp?.ReturnType | ||
?? pi?.PropertyType | ||
?? throw new InvalidOperationException("Cannot determine property to provide the value for."); | ||
|
||
var value = GetValue(); | ||
var info = propertyType.GetTypeInfo(); | ||
if (value == null && info.IsValueType) | ||
return Activator.CreateInstance(propertyType); | ||
|
||
if (Converter != null) | ||
return Converter.Convert(value, propertyType, ConverterParameter, CultureInfo.CurrentUICulture); | ||
|
||
var converterProvider = serviceProvider?.GetService<IValueConverterProvider>(); | ||
|
||
if (converterProvider != null) | ||
return converterProvider.Convert(value, propertyType, () => pi, serviceProvider); | ||
else | ||
return value.ConvertTo(propertyType, () => pi, serviceProvider); | ||
} | ||
|
||
object GetValue() | ||
{ | ||
switch (Device.RuntimePlatform) | ||
{ | ||
case Device.Android: | ||
return Android ?? Default; | ||
case Device.GTK: | ||
return GTK ?? Default; | ||
case Device.iOS: | ||
return iOS ?? Default; | ||
case Device.macOS: | ||
return macOS ?? Default; | ||
case Device.Tizen: | ||
return Tizen ?? Default; | ||
case Device.UWP: | ||
return UWP ?? Default; | ||
case Device.WPF: | ||
return WPF ?? Default; | ||
default: | ||
return Default; | ||
} | ||
} | ||
} | ||
} |