hashimks
hashimks

Reputation: 1225

Gradient stacklayout for xamarin forms

I am working on Xamarin forms now. I have a requirement to have a gradient stacklayout view with some width set to the stack layout which is greater than the device's portrait width. Setting this width will ensure that the stacklayout will be fit in portrait view and in landscape the stacklayout with the width can be seen. Here I need a top to bottom gradient stacklayout. I have seen some of the posts with custom renderers for stacklayout and that works good.

But when i used those renderers, i could see that on changing the orientation from portrait to landscape in live scenario, the drawn gradient layout width is seen as the portrait width with the remaining area as empty. See the attached images for

portrait

[![portrait][1]][1]

and landscape

landscape

Here is the XAML code imageenter image description here

<AbsoluteLayout>
<Image Source="img_bg.png" Aspect="AspectFill" VerticalOptions="FillAndExpand" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All"/>
<StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" Spacing="0" Padding="0,20,0,0">
  <Image Source="img_logo.png" HorizontalOptions="Center" WidthRequest="200" HeightRequest="80"/>
  <ScrollView>
          <local:GradientStack StartColor="#99ddde" EndColor="#2896b1" HeightRequest="375" WidthRequest="300" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="25,5,25,0" Margin="20,25,20,0" Spacing="0">
      <Label Text="Log in" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="34"/>
      <Label Text="Username" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="20" Margin="0,10,0,0"/>
      <local:CustomBorderEntry x:Name="txtUserName" HorizontalOptions="FillAndExpand"
                               HeightRequest="38" Margin="0,5,0,0"
                               BackgroundColor="#e5f8fc" FontSize="16"
                               TextColor ="#201f1f"
                               HorizontalTextAlignment="Start" />
      <Label Text="Password" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="20" Margin="0,10,0,0"/>
      <local:CustomBorderEntry x:Name="txtPassword" HorizontalOptions="FillAndExpand"
                               HeightRequest="38" Margin="0,5,0,0"
                               BackgroundColor="#e5f8fc" FontSize="16"
                               TextColor ="#201f1f"
                               HorizontalTextAlignment="Start" IsPassword="true"/>
      <Label Text="Event Code" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="20" Margin="0,10,0,0"/>
      <local:CustomBorderEntry x:Name="txtEventCode" HorizontalOptions="FillAndExpand"
                               HeightRequest="36" Margin="0,5,0,0"
                               BackgroundColor="#e5f8fc" FontSize="16"
                               TextColor ="#201f1f"
                               HorizontalTextAlignment="Start"/>
      <StackLayout Margin="0,20,0,0" Padding="0,1,0,0" BackgroundColor="#e5f8fc" HorizontalOptions="FillAndExpand" HeightRequest="45">
        <local:GradientStack StartColor="#2c7f9d" EndColor="#0e5a8c" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" x:Name="slLogin">
          <Label Text="Log in" FontSize="20" TextColor="#e5f8fc" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"/>
        </local:GradientStack>            
      </StackLayout>
    </local:GradientStack>
  </ScrollView>
</StackLayout>

custom control:

public class GradientStack : StackLayout
{
    public Color StartColor { get; set; }
    public Color EndColor { get; set; }
}

custom Renderer:

public class GradientStackRenderer : VisualElementRenderer<GradientStack>
{
    public override void Draw(CGRect rect)
    {
        base.Draw(rect);
        CAGradientLayer layer = new CAGradientLayer();
        layer.Frame = rect;
        layer.Colors = new CGColor[] {
            Element.StartColor.ToCGColor(),
            Element.EndColor.ToCGColor()
        };
        Layer.InsertSublayer(layer, 0);
    }
}

Upvotes: 1

Views: 2744

Answers (2)

Sandip Ahluwalia
Sandip Ahluwalia

Reputation: 53

To avoid a null exception in the solution above when the redraw happens, you need to make sure that you check the Sublayers property for null like below:

if (Layer.Sublayers?[0] is CAGradientLayer)
{
    Layer.ReplaceSublayer(Layer.Sublayers[0], gradientLayer);
}
else
{
    Layer.InsertSublayer(gradientLayer, 0);
}

Upvotes: 0

Yuri S
Yuri S

Reputation: 5370

Here is the solution. When size of the stack layout changed we need to force redraw.

[assembly: ExportRenderer(typeof(GradientStack), typeof(GradientStackRenderer))]

namespace yournamespace.iOS
{
    class GradientStackRenderer : ViewRenderer<StackLayout, UIView>
    {

        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
            CAGradientLayer layer = new CAGradientLayer();
            layer.Frame = rect;
            layer.Colors = new CGColor[]
            {
                ((GradientStack)Element).StartColor.ToCGColor(),
                ((GradientStack)Element).EndColor.ToCGColor()
            };

            if(Layer.Sublayers[0] is CAGradientLayer)
                Layer.ReplaceSublayer(Layer.Sublayers[0], layer);
            else
                Layer.InsertSublayer(layer, 0);

        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if(e.PropertyName=="Width")
                SetNeedsDisplay();
        }


    }
}

We also don't want to add a layer every time we draw, so we check and replace our layer if already in sublayers.

I also would remove unnecessary size requests

    <AbsoluteLayout>
        <Image Source="img_bg.png" Aspect="AspectFill" VerticalOptions="FillAndExpand" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All"/>
        <StackLayout  AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" Spacing="0" Padding="0,20,0,0">
            <Image Source="img_bg.png" HorizontalOptions="Center" WidthRequest="200" HeightRequest="80"/>
            <ScrollView>
                <local:GradientStack StartColor="#99ddde" EndColor="#2896b1" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="25,5,25,20" Margin="20,25,20,0" Spacing="0">
                    <Label Text="Log in" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="34"/>
                    <Label Text="Username" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="20" Margin="0,10,0,0"/>
                    <local:CustomBorderEntry x:Name="txtUserName" HorizontalOptions="FillAndExpand"
                           HeightRequest="38" Margin="0,5,0,0"
                           BackgroundColor="#e5f8fc" FontSize="16"
                           TextColor ="#201f1f"
                           HorizontalTextAlignment="Start" />
                    <Label Text="Password" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="20" Margin="0,10,0,0"/>
                    <local:CustomBorderEntry x:Name="txtPassword" HorizontalOptions="FillAndExpand"
                           HeightRequest="38" Margin="0,5,0,0"
                           BackgroundColor="#e5f8fc" FontSize="16"
                           TextColor ="#201f1f"
                           HorizontalTextAlignment="Start" IsPassword="true"/>
                    <Label Text="Event Code" HorizontalOptions="Start" TextColor="#e5f8fc" FontSize="20" Margin="0,10,0,0"/>
                    <local:CustomBorderEntry x:Name="txtEventCode" HorizontalOptions="FillAndExpand"
                           HeightRequest="36" Margin="0,5,0,0"
                           BackgroundColor="#e5f8fc" FontSize="16"
                           TextColor ="#201f1f"
                           HorizontalTextAlignment="Start"/>
                    <StackLayout Margin="0,20,0,0" Padding="0,1,0,0" BackgroundColor="#e5f8fc" HorizontalOptions="FillAndExpand" HeightRequest="45">
                        <local:GradientStack StartColor="#2c7f9d" EndColor="#0e5a8c" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" x:Name="slLogin">
                            <Label Text="Log in" FontSize="20" TextColor="#e5f8fc" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"/>
                        </local:GradientStack>
                    </StackLayout>
                </local:GradientStack>
            </ScrollView>
        </StackLayout>
    </AbsoluteLayout>

Upvotes: 2

Related Questions