David Nedrow
David Nedrow

Reputation: 1158

Unable to reference custom Xamarin PlatformEffect in XAML

====SOLVED==== Joe's answer was spot on and I was able to get this working using his advice. I basically just had to add the effect in my code behind and it worked.

Using information found on the net, I created a PlatformEffect for iOS that can be assigned to any element. It adjusts the view to take into account an iPhone notch, if present.

My problem is, I am unable to reference the platform effect in my XAML.

Theortically, given the code at the bottom of this message, I should be able to use the following to apply the effect:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"                 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="Enterprise.View.Features.Authentication.LoginView"                 
             xmlns:effect="clr-namespace:Enterprise.iOS.Effects">        
    <Grid>                      
        <Grid.RowDefinitions>               
            <RowDefinition Height="100"/>                
            <RowDefinition Height="*" />            
        </Grid.RowDefinitions>            
        <ContentView BackgroundColor="Green">                
            <ContentView.Effects>                    
                <effect:SafeAreaPaddingEffect />                
            </ContentView.Effects>                
            <Label Text="Hello, from XamarinHelp.com" />            
        </ContentView>                    
    </Grid>
</ContentPage>

However, the effect reference cannot be resolved in the ContentPage declaration. I'm probably doing something wrong, but I'm not sure what it is. I have not found anything via searches that answer my question.

Any thoughts? Here is the PlatformEffect file (located under an Effects folder in the iOS solution):

using Enterprise.iOS.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ResolutionGroupName("Enterprise.iOS")]
[assembly: ExportEffect(typeof(SafeAreaPaddingEffect), nameof(SafeAreaPaddingEffect))]
namespace Enterprise.iOS.Effects
{
    public class SafeAreaPaddingEffect : PlatformEffect
    {
        Thickness _padding;

        protected override void OnAttached()
        {
            if (Element is Layout element)
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
                {
                    _padding = element.Padding;
                    var insets =
                        UIApplication.SharedApplication.Windows[0].SafeAreaInsets; // Can't use KeyWindow this early
                    if (insets.Top > 0) // We have a notch
                    {
                        element.Padding = new Thickness(_padding.Left + insets.Left, _padding.Top + insets.Top,
                            _padding.Right + insets.Right, _padding.Bottom);
                        return;
                    }
                }

                // Uses a default Padding of 20. Could use an property to modify if you wanted.
                element.Padding = new Thickness(_padding.Left, _padding.Top + 20, _padding.Right, _padding.Bottom);
            }
        }

        protected override void OnDetached()
        {
            if (Element is Layout element)
            {
                element.Padding = _padding;
            }
        }
    }
}

Upvotes: 0

Views: 308

Answers (2)

David Nedrow
David Nedrow

Reputation: 1158

I was able to fix this by assigning the effect in the code behind. So the relevant XAML not looks like the following

<ContentView BackgroundColor="Green">                
    <Label x:Name="HelloLabel" Text="Hello, from XamarinHelp.com" />            
</ContentView>                    

and in my code behind, I added the followin immediately after initializing the component

HelloLabel.Effects.Add(Effect.Resolve("Enterprise.iOS.Effects.SafeAreaPaddingEffect"));

Upvotes: 0

Ben
Ben

Reputation: 2995

To implement an effect for usage in a XAML Xamarin Forms project the following classes might be defined:

MyEffects.EffectIds (namespace MyEffects, class name EffectIds) located in a Xamarin Forms or a Netstandard project (e.g. MyProject) to define the identifier of the Effect.

MyEffects.MyEffect (namespace MyEffects, class name MyEffect) located in a Xamarin Forms project or a Netstandard project (e.g. MyProject) to define the Xamarin Forms effect.

MyEffects.iOS.MyEffect (namespace MyEffects.iOS, class name MyEffect) located in an iOS project to implement the iOS effect.

Sample MyEffects.EffectIds:

using Xamarin.Forms;

[assembly: ResolutionGroupName(MyEffects.EffectIds.GroupName)]

namespace MyEffects
{
  public class EffectIds
  {
    public const string GroupName = "MyEffects";

    public static string MyEffect => typeof(MyEffect).FullName;

    //another effect not defined here
    public static string MyOtherEffect => typeof(MyOtherEffect).FullName;
    ...

Sample MyEffects.MyEffect:

using Xamarin.Forms;

namespace MyEffects
{
  public class MyEffect : RoutingEffect
  {
    public MyEffect() : base(EffectIds.MyEffect) { }
  }
}

Sample MyEffects.iOS.MyEffect:

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using PlatformEffects = MyEffects.iOS; 
using RoutingEffects = MyEffects; 

[assembly: ExportEffect(typeof(PlatformEffects.MyEffect), nameof(RoutingEffects.MyEffect))] 
namespace MyEffects.iOS 
{ 
  public class MyEffect : PlatformEffect 
  { 
    protected override void OnAttached() 
    { 
      ...

Sample usage in XAML:

<ContentPage

  xmlns:effects="clr-namespace:MyEffects;assembly=MyProject"
  ...
<Entry ... >

  <Entry.Effects>
    <effects:MyEffect />
  </Entry.Effects>

</Entry>

Upvotes: 1

Related Questions