【问题标题】:Xamarin IOS Custom Renderer overriden Draw method not called未调用 Xamarin IOS 自定义渲染器覆盖的 Draw 方法
【发布时间】:2020-01-24 07:32:11
【问题描述】:

我正在尝试在列表视图中加载自定义滑块控件(具有手风琴行为)。当视图加载时,所有列表视图元素都被折叠,因此滑块控件的可见性为假。我观察到,当控件不可见时,不会调用 ios 渲染器中覆盖的 Draw 方法,因此我最终在我的列表视图中拥有本机控件。

我已经在一个单独的项目中重现了这个问题:

我有 IOS 自定义渲染器:

public class CustomGradientSliderRenderer : SliderRenderer
{
    public CGColor StartColor { get; set; }
    public CGColor CenterColor { get; set; }
    public CGColor EndColor { get; set; }
    protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
    {

        if (Control == null)
        {
            var customSlider = e.NewElement as CustomGradientSlider;
            StartColor = customSlider.StartColor.ToCGColor();
            CenterColor = customSlider.CenterColor.ToCGColor();
            EndColor = customSlider.EndColor.ToCGColor();

            var slider = new SlideriOS
            {
                Continuous = true,

                Height = (nfloat)customSlider.HeightRequest
            };

            SetNativeControl(slider);
        }

        base.OnElementChanged(e);
    }

    public override void Draw(CGRect rect)
    {
        base.Draw(rect);

        if (Control != null)
        {
            Control.SetMinTrackImage(CreateGradientImage(rect.Size), UIControlState.Normal);
        }
    }

    void OnControlValueChanged(object sender, EventArgs eventArgs)
    {
        ((IElementController)Element).SetValueFromRenderer(Slider.ValueProperty, Control.Value);
    }

    public UIImage CreateGradientImage(CGSize rect)
    {
        var gradientLayer = new CAGradientLayer()
        {
            StartPoint = new CGPoint(0, 0.5),
            EndPoint = new CGPoint(1, 0.5),
            Colors = new CGColor[] { StartColor, CenterColor, EndColor },
            Frame = new CGRect(0, 0, rect.Width, rect.Height),
            CornerRadius = 5.0f
        };

        UIGraphics.BeginImageContext(gradientLayer.Frame.Size);
        gradientLayer.RenderInContext(UIGraphics.GetCurrentContext());
        var image = UIGraphics.GetImageFromCurrentImageContext();
        UIGraphics.EndImageContext();

        return image.CreateResizableImage(UIEdgeInsets.Zero);
    }
}

public class SlideriOS : UISlider
{
    public nfloat Height { get; set; }

    public override CGRect TrackRectForBounds(CGRect forBounds)
    {
        var rect = base.TrackRectForBounds(forBounds);
        return new CGRect(rect.X, rect.Y, rect.Width, Height);
    }
}

代码隐藏视图:

Main.xaml:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage
    x:Class="GradientSlider.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:GradientSlider">
    <ContentPage.Content>

    <Grid>
    <StackLayout  x:Name="SliderContainer">

        <local:CustomGradientSlider
            x:Name="mySlider"
            CenterColor="#feeb2f"
            CornerRadius="16"
            EndColor="#ba0f00"
            HeightRequest="20"
            HorizontalOptions="FillAndExpand"
            Maximum="10"

            Minimum="0"
            StartColor="#6bab29"
            VerticalOptions="CenterAndExpand"
            MaximumTrackColor="Transparent"

            ThumbColor="green"
            />
        <Label x:Name="lblText" Text="txt"
               VerticalOptions="Center" HorizontalOptions="Center"/>
    </StackLayout>

    <Button Text="Magic" Clicked="Button_Tapped" WidthRequest="100" HeightRequest="50" VerticalOptions="Center" HorizontalOptions="Center"/>

         </Grid>
        </ContentPage.Content>

</ContentPage>

Main.xaml.cs:

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace GradientSlider
{
    public partial class MainPage : ContentPage, INotifyPropertyChanged

    {

        public MainPage()
        {
            InitializeComponent();
            SliderContainer.IsVisible = false;
        }


        void Button_Tapped(object sender,ClickedEventArgs a)
        {
            SliderContainer.IsVisible = !SliderContainer.IsVisible;

        }


    }
}

所以在上面的场景中,您可以看到,当我加载 main.xaml 时,控件是不可见的 (SliderContainer.IsVisible = false;),在这种情况下,我得到了一个本机滑块控件,而不是我的自定义控件。如果我在构造函数中更改 SliderContainer.IsVisible = true;然后我得到我的自定义控件。

经过调查,我意识到如果在视图加载时控件不可见,则不会调用公共覆盖 void Draw(CGRect rect)。当控件不可见时,我找不到任何触发 Draw 方法的解决方案。

有人知道如何在控件不可见的情况下正确加载自定义渲染器吗?

谢谢!

【问题讨论】:

    标签: xamarin xamarin.forms xamarin.ios custom-renderer


    【解决方案1】:

    假设渲染器覆盖OnElementPropertyChanged

    protected override void OnElementChanged(ElementChangedEventArgs<MyFormsSlider> e)
    {
        if (e.NewElement != null)
        {
            if (Control == null)
            {
                // Instantiate the native control and assign it to the Control property with
                // the SetNativeControl method
                SetNativeControl(new MyNativeControl(...
        ...
    }
    
    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
    
        //assuming MyFormsSlider derives from View / VisualElement; the latter has IsVisibleProperty
        if (e.PropertyName == MyFormsSlider.IsVisibleProperty.PropertyName)
        {
            //Control is the control set with SetNativeControl
            Control. ...
        }
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 2015-06-14
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 2020-09-08
      • 1970-01-01
      相关资源
      最近更新 更多