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

[C] Allow OnIdiom/OnPlatform to return a Binding #24594

Merged
merged 2 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ static bool CanSetValue(FieldReference bpRef, bool attached, INode node, IXmlLin
if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object")
return true;

return varValue.VariableType.InheritsFromOrImplements(context.Cache, bpTypeRef);
return varValue.VariableType.InheritsFromOrImplements(context.Cache, bpTypeRef) || varValue.VariableType.FullName == "System.Object";
}

static bool CanGetValue(VariableDefinition parent, FieldReference bpRef, bool attached, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
Expand Down Expand Up @@ -1302,11 +1302,37 @@ static IEnumerable<Instruction> SetValue(VariableDefinition parent, FieldReferen
}
else if (elementNode != null)
{
var @else = Create(OpCodes.Nop);
var endif = Create(OpCodes.Nop);

if(context.Variables[elementNode].VariableType.FullName == "System.Object")
{
//if(value != null && value.GetType().IsAssignableFrom(typeof(BindingBase)))
yield return Create(Ldloc, context.Variables[elementNode]);
yield return Create(Brfalse, @else);

yield return Create(Ldtoken, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindingBase")));
yield return Create(Call, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Type"), methodName: "GetTypeFromHandle", parameterTypes: new[] { ("mscorlib", "System", "RuntimeTypeHandle") }, isStatic: true));
yield return Create(Ldloc, context.Variables[elementNode]);
yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Object"), methodName: "GetType", paramCount: 0));
yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Type"), methodName: "IsAssignableFrom", parameterTypes: new[] { ("mscorlib", "System", "Type") }));
yield return Create(Brfalse, @else);
//then
yield return Create(Ldloc, context.Variables[elementNode]);
yield return Create(Br, endif);
//else
yield return @else;
}
var bpTypeRef = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, module);
foreach (var instruction in context.Variables[elementNode].LoadAs(context.Cache, bpTypeRef, module))
yield return instruction;
if (bpTypeRef.IsValueType)
yield return Create(Box, module.ImportReference(bpTypeRef));

//endif
if(context.Variables[elementNode].VariableType.FullName == "System.Object")
yield return endif;

}
yield return Create(Callvirt, module.ImportMethodReference(context.Cache,
bindableObjectType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ namespace Microsoft.Maui.Controls.Build.Tasks
{
static class VariableDefinitionExtensions
{
public static IEnumerable<Instruction> LoadAs(this VariableDefinition self, XamlCache cache, TypeReference type, ModuleDefinition module)
public static IEnumerable<Instruction> LoadAs(this VariableDefinition self, XamlCache cache, TypeReference type, ModuleDefinition module, bool box = true, bool unbox = true)
{
var implicitOperator = self.VariableType.GetImplicitOperatorTo(cache, type, module);

yield return Instruction.Create(OpCodes.Ldloc, self);

if (!self.VariableType.InheritsFromOrImplements(cache, type) && implicitOperator != null)
yield return Instruction.Create(OpCodes.Call, module.ImportReference(implicitOperator));
else if (self.VariableType.IsValueType && !type.IsValueType)
else if (box && self.VariableType.IsValueType && !type.IsValueType)
yield return Instruction.Create(OpCodes.Box, module.ImportReference(self.VariableType));
else if (!self.VariableType.IsValueType && type.IsValueType)
else if (unbox && !self.VariableType.IsValueType && type.IsValueType)
yield return Instruction.Create(OpCodes.Unbox_Any, module.ImportReference(type));
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/BindableObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ public void SetValue(BindableProperty property, object value)
if (property == null)
throw new ArgumentNullException(nameof(property));

if (value is BindingBase binding && !property.ReturnType.IsAssignableFrom(typeof(BindableProperty))) {
SetBinding(property, binding);
return;
}

if (property.IsReadOnly)
{
Application.Current?.FindMauiContext()?.CreateLogger<BindableObject>()?.LogWarning($"Cannot set the BindableProperty \"{property.PropertyName}\" because it is readonly.");
Expand Down
9 changes: 9 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui18697.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui18697">
<ContentPage.ToolbarItems>
<ToolbarItem Text="{OnIdiom Desktop={local:Maui18697Translate ProfileToolBarText}}" />
</ContentPage.ToolbarItems>
</ContentPage>
85 changes: 85 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui18697.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Dispatching;

using Microsoft.Maui.Graphics;
using Microsoft.Maui.UnitTests;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui18697 : ContentPage
{
public Maui18697()
{
InitializeComponent();
}

public Maui18697(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
class Test
{
MockDeviceInfo mockDeviceInfo;

[SetUp]
public void Setup()
{
Application.SetCurrentApplication(new MockApplication());
DeviceInfo.SetCurrent(mockDeviceInfo = new MockDeviceInfo());
DispatcherProvider.SetCurrent(new DispatcherProviderStub());
}


[TearDown] public void TearDown()
{
AppInfo.SetCurrent(null);
DeviceInfo.SetCurrent(null);
}

[Test]
public void OnBindingRelease([Values(false, true)] bool useCompiledXaml)
{
mockDeviceInfo.Idiom = DeviceIdiom.Desktop;
var page = new Maui18697(useCompiledXaml);
Assert.That(page.ToolbarItems[0].Text, Is.EqualTo("_ProfileToolBarText"));


}
}
}

[ContentProperty(nameof(Name))]
public class Maui18697TranslateExtension : IMarkupExtension<BindingBase>
{
public string Name { get; set; }

public BindingBase ProvideValue(IServiceProvider serviceProvider)
{
return new Binding
{
Mode = BindingMode.OneWay,
Path = $"[{Name}]",
Source = new Dictionary<string, string>
{
{ "Hello", "_Hello" },
{ "ProfileToolBarText", "_ProfileToolBarText" }
}
};
}

object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return ProvideValue(serviceProvider);
}
}
9 changes: 9 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui19388.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui19388">
<Label Text="Hi"
x:Name="label0"
BackgroundColor="{OnPlatform Android={AppThemeBinding Light=Red, Dark=Blue}, Default=Green}" />
</ContentPage>
65 changes: 65 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui19388.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Dispatching;

using Microsoft.Maui.Graphics;
using Microsoft.Maui.UnitTests;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui19388 : ContentPage
{
public Maui19388()
{
InitializeComponent();
}

public Maui19388(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
class Test
{
MockDeviceInfo mockDeviceInfo;

[SetUp]
public void Setup()
{
Application.SetCurrentApplication(new MockApplication());
DeviceInfo.SetCurrent(mockDeviceInfo = new MockDeviceInfo());
DispatcherProvider.SetCurrent(new DispatcherProviderStub());
}


[TearDown] public void TearDown()
{
AppInfo.SetCurrent(null);
DeviceInfo.SetCurrent(null);
}

[Test]
public void OnPlatformAppThemeBindingRelease([Values(false, true)] bool useCompiledXaml)
{
Application.Current.UserAppTheme = AppTheme.Light;
mockDeviceInfo.Platform = DevicePlatform.iOS;
var page = new Maui19388(useCompiledXaml);
Assert.That(page.label0.BackgroundColor, Is.EqualTo(Colors.Green));

mockDeviceInfo.Platform = DevicePlatform.Android;
page = new Maui19388(useCompiledXaml);
Assert.That(page.label0.BackgroundColor, Is.EqualTo(Colors.Red));


}
}
}
7 changes: 7 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui22877.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui22877">
<Label x:Name="label0" Text="{OnIdiom {Binding BoundString}, Phone=Grade}" />
</ContentPage>
64 changes: 64 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui22877.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Dispatching;

using Microsoft.Maui.Graphics;
using Microsoft.Maui.UnitTests;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui22877 : ContentPage
{
public Maui22877()
{
InitializeComponent();
}

public Maui22877(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
class Test
{
MockDeviceInfo mockDeviceInfo;

[SetUp]
public void Setup()
{
Application.SetCurrentApplication(new MockApplication());
DeviceInfo.SetCurrent(mockDeviceInfo = new MockDeviceInfo());
DispatcherProvider.SetCurrent(new DispatcherProviderStub());
}


[TearDown] public void TearDown()
{
AppInfo.SetCurrent(null);
DeviceInfo.SetCurrent(null);
}

[Test]
public void OnBindingRelease([Values(false, true)] bool useCompiledXaml)
{
mockDeviceInfo.Idiom = DeviceIdiom.Phone;
var page = new Maui22877(useCompiledXaml) {BindingContext = new {BoundString = "BoundString"}};
Assert.That(page.label0.Text, Is.EqualTo("Grade"));

mockDeviceInfo.Idiom = DeviceIdiom.Desktop;
page = new Maui22877(useCompiledXaml) {BindingContext = new {BoundString = "BoundString"}};
Assert.That(page.label0.Text, Is.EqualTo("BoundString"));


}
}
}
7 changes: 7 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui24500.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui24500">
<Label x:Name="label0" IsVisible="{OnIdiom Default={Binding EditingMode}, Phone=False}" />
</ContentPage>
Loading
Loading