CommunityToolkit/Maui.Markup

[Bug] .Assign Extension does not work with inherited controls of Label and Entry

danielftz opened this issue · 4 comments

.Assign Extension does not work with inherited controls of Label and Entry.

Stack Trace

I get compiler error:

The type 'Microsoft.Maui.Controls.Label' cannot be used as type parameter 'TBindable' in the generic type or method 'BindableObjectExtensions.Assign<TBindable, TVariable>(TBindable, out TVariable)'. There is no implicit reference conversion from 'Microsoft.Maui.Controls.Label' to 'MyProject.MyLabel'.

Basic Information

  • Version with issue: Latest
  • Last known good version: 1.0.1
  • IDE: VS for mac
  • Platform Target Frameworks:
    • iOS:
    • Android:
  • Android Support Library Version:
  • Nuget Packages:
  • Affected Devices:

Thanks @danielftz! Please include a reproduction sample when you get a chance.

@danielftz I'm not able to reproduce this.

I've opened #136 to add the following Unit Tests that confirm inherited controls are supported for .Assign() and .Invoke():

[Test]
public void AssignCustomLabel()
{
  var createdLabel = new CustomLabel().Assign(out Label assignedLabel);
  Assert.That(ReferenceEquals(createdLabel, assignedLabel));
}

[Test]
public void InvokeCustomEntry()
{
  const string text = nameof(Invoke);
  
  var createdLabel = new CustomEntry().Invoke(l => l.Text = text);
  Assert.That(createdLabel.Text, Is.EqualTo(text));
}

class CustomLabel : Label
{

}

class CustomEntry : Entry
{

}

Sorry for the delay. Here is the link to the project. I think the issue for both of Label and Entry is that .TextCenter() always returns the base class. So if I use .TextCenter().Assign() that will cause compile error.

Thanks for the reproduction, @danielftz!

This bug can be worked-around by calling .Assign() first before calling other Maui.Markup extensions methods (example below).

The root cause actually runs a bit deeper, the ITextAlignment extensions return the Maui base type, eg Label, instead of the child class, class MyLabel : Label.

Workaround

public class MainPage : ContentPage
{
    private MyLabel lb;

    private MyEntry et;
    public MainPage()
    {
        Content = new VerticalStackLayout
        {
            Children =
            {
                new MyLabel
                {

                }.Assign(out lb) // Call `Assign` first
                 .TextCenter(),

                new MyEntry
                {

                }.Assign(out et) // Call `Assign` first
                 .TextCenter()
            }
        };
    }
}

public class MyLabel : Label
{

}

public class MyEntry : Entry
{

}