首页 > 解决方案 > 在自定义 StackLayout(运行时)中所做的更改不会出现在视图中 (Xamarin.Forms)

问题描述

我已经实现了一个自定义布局,其主要特点是背景可以通过提供 2 种十六进制颜色来渐变。视图本身工作得很好,问题是程序运行时对这两种颜色所做的更改不会反映在应用程序中,自定义布局接收并更改新颜色并正确制作渐变,但它没有显示所做的更改。我认为问题在于我没有正确实现 INotifyPropertyChanged。

这是我的自定义视图的代码:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace MyProject.Renderers
{
    public class GradientLayout : StackLayout, INotifyPropertyChanged
    {
        public string ColorsList { get; set; }
        public Color[] Colors
        {
            get
            {
                //colorsList have the following format: "HexCode1,HexCode2"
                string[] hex = ColorsList.Split(',');
                Color[] colors = new Color[hex.Length];

                for (int i = 0; i < hex.Length; i++)
                {
                    colors[i] = Color.FromHex(hex[i].Trim());
                }
                return colors;
            }
        }

//since this is a property that I am not trying to modify, I will not provide the rest of its code to make this as simple as possible.
        public GradientColorStackMode Mode { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

仅此而已,如果您需要更多信息,我会在看到您的请求后立即提供,感谢大家的宝贵时间,希望您有美好的一天。

编辑:在这里,您在 myProject.ios 中有一个与背景渐变相关的类。

[assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))]

namespace MyProject.iOS.Renderers
{
    public class GradientLayoutRenderer : VisualElementRenderer<StackLayout>
    {
        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
            GradientLayout layout = (GradientLayout)Element;

            CGColor[] colors = new CGColor[layout.Colors.Length];

            for (int i = 0, l = colors.Length; i < l; i++)
            {
                colors[i] = layout.Colors[i].ToCGColor();
            }

            var gradientLayer = new CAGradientLayer();

            switch (layout.Mode)
            {
                default:
                case GradientColorStackMode.ToRight:
                    gradientLayer.StartPoint = new CGPoint(0, 0.5);
                    gradientLayer.EndPoint = new CGPoint(1, 0.5);
                    break;
                case GradientColorStackMode.ToLeft:
                    gradientLayer.StartPoint = new CGPoint(1, 0.5);
                    gradientLayer.EndPoint = new CGPoint(0, 0.5);
                    break;
                case GradientColorStackMode.ToTop:
                    gradientLayer.StartPoint = new CGPoint(0.5, 0);
                    gradientLayer.EndPoint = new CGPoint(0.5, 1);
                    break;
                case GradientColorStackMode.ToBottom:
                    gradientLayer.StartPoint = new CGPoint(0.5, 1);
                    gradientLayer.EndPoint = new CGPoint(0.5, 0);
                    break;
                case GradientColorStackMode.ToTopLeft:
                    gradientLayer.StartPoint = new CGPoint(1, 0);
                    gradientLayer.EndPoint = new CGPoint(0, 1);
                    break;
                case GradientColorStackMode.ToTopRight:
                    gradientLayer.StartPoint = new CGPoint(0, 1);
                    gradientLayer.EndPoint = new CGPoint(1, 0);
                    break;
                case GradientColorStackMode.ToBottomLeft:
                    gradientLayer.StartPoint = new CGPoint(1, 1);
                    gradientLayer.EndPoint = new CGPoint(0, 0);
                    break;
                case GradientColorStackMode.ToBottomRight:
                    gradientLayer.StartPoint = new CGPoint(0, 0);
                    gradientLayer.EndPoint = new CGPoint(1, 1);
                    break;
            }

            gradientLayer.Frame = rect;
            gradientLayer.Colors = colors;

            NativeView.Layer.InsertSublayer(gradientLayer, 0);
        }
    }
}

编辑:这里还有与渐变布局的声明和调用相关的 xaml 和 xaml.cs 代码。XAML.CS:

            page.ColorsList = Items[0].StartColor+","+Items[0].EndColor;
            page.Mode = MyProject.Renderers.GradientColorStackMode.ToBottomLeft;

XAML:

<renderers:GradientLayout
                x:Name="page"
                Opacity="0"
                Mode="ToBottomLeft">
</renders:GradientLayout>

编辑:我尝试实现Juniorjiang的,在我完成之后我注意到1个变化,IOS类在我的xaml.cs类的构造函数之前执行,给我string[] hex = ColorsList.Split(',');与我之前提到的相同的错误,因为ColorsList没有收到任何值,所以我为“colors”提供了 2 个默认值,并实现了一个条件来确保构造函数代码没有在 ColorsList 为空的情况下执行:

public GradientLayout()
    {
        if(colorsList != null)
        {
          //colorsList have the following format: "HexCode1,HexCode2"
            string[] hex = ColorsList.Split(',');
            colors = new Color[hex.Length];

            for (int i = 0; i < hex.Length; i++)
            {
                colors[i] = Color.FromHex(hex[i].Trim());
            }
         // BindingContext = this;
        }
        else
        {
            colors = new Color[2];
            colors[0] = Color.FromHex("#000000");
            colors[1] = Color.FromHex("#000000");
        }
    }

之后执行代码没有给出任何错误,但它没有按预期工作,因为 IOS 类只调用类 1 时间(在构造视图时)。我也没有绑定任何东西,因为我不明白我应该做什么//BindingContext = this

标签: c#visual-studioxamarinxamarin.formsinotifypropertychanged

解决方案


我认为您应该实现如下INotifyPropertyChanged功能:

public class GradientLayout : StackLayout, INotifyPropertyChanged
{
    private Color[] colors;
    public string ColorsList { get; set; }
    public Color[] Colors
    {
        set
        {
            if (colors != value)
            {
                colors = value;
                OnPropertyChanged("Colors");
            }
        }
        get
        {
            return colors;
        }
    }

    public GradientLayout()
    {
          //colorsList have the following format: "HexCode1,HexCode2"
            string[] hex = ColorsList.Split(',');
            colors = new Color[hex.Length];

            for (int i = 0; i < hex.Length; i++)
            {
                colors[i] = Color.FromHex(hex[i].Trim());
            }
         // BindingContext = this;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

并且更好地使用模型来绑定控件的属性。看看这个文档

===================================更新=============== =====================

您可以使用 Bindable 属性来实现它。

创建一个CustomStackLayout

public class CustomStackLayout : StackLayout
{
    public static readonly BindableProperty ColorsProperty = BindableProperty.Create("Colors", typeof(string), typeof(CustomStackLayout), "Default Value");

    public string Colors
    {
        get { return (string)GetValue(ColorsProperty); }
        set { SetValue(ColorsProperty, value); }
    }
}

Xaml中,添加 Tap 方法来更改属性CustomStackLayout

<local:CustomStackLayout HorizontalOptions="FillAndExpand"
                         VerticalOptions="FillAndExpand"
                         BackgroundColor="Gray">
    <local:CustomStackLayout.GestureRecognizers>
        <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
    </local:CustomStackLayout.GestureRecognizers>
</local:CustomStackLayout>

TapGestureRecognizer_Tapped方法:

private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
    Console.WriteLine("--TapGestureRecognizer_Tapped--");
    var customStacklayout = sender as CustomStackLayout;
    customStacklayout.Colors = "new value";
}

因此,在iOS Renderer文件中,有一个OnElementPropertyChanged方法可以监听哪些属性发生了变化。在这里您还可以更新 UI 。

[assembly: ExportRenderer(typeof(CustomStackLayout),typeof(CustomStackLayoutRenderer))]
namespace AppFrameRenderer.iOS
{
    public class CustomStackLayoutRenderer : ViewRenderer
    {

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

            var customStack = sender as CustomStackLayout;

            Console.WriteLine("----" + customStack.Colors);

            if (customStack.Colors == "Default Value")
            {
                //--
            }
            else
            {
                BackgroundColor = UIColor.Red;
            }
        }

    }
}

效果如下:

在此处输入图像描述


推荐阅读