第二十七章:自定义渲染器(五)

简介: 渲染器和事件(1)大多数Xamarin.Forms元素都是交互式的。他们通过触发事件来响应用户输入。如果在Xamarin.Forms自定义元素中实现事件,则可能还需要在呈现器中为本机控件触发的相应事件定义事件处理程序。

渲染器和事件(1)

大多数Xamarin.Forms元素都是交互式的。他们通过触发事件来响应用户输入。如果在Xamarin.Forms自定义元素中实现事件,则可能还需要在呈现器中为本机控件触发的相应事件定义事件处理程序。本节将向您展示如何。
StepSlider元素的灵感来自Windows Slider元素的Xamarin.Forms实现的问题。默认情况下,Xamarin.Forms Slider在Windows上运行时
平台从0到1只有10个步骤,因此它的值只能为0,0.1,0.2等,最高可达1.0。
与常规Xamarin.Forms Slider一样,StepSlider元素具有Minimum,Maximum和Value属性,但它还定义了Step属性以指定Minimum和Maximum之间的步数。例如,如果Minimum设置为5,Maximum设置为10,Step设置为20,则Value属性的可能值为5.00,5.25,5.50,5.75,6.00等,最多为10。可能的值数值等于步长值加1。
有趣的是,实现此Step属性在所有三个平台上都需要采用不同的方法,但本练习的主要目的是演示如何实现事件。
这是Xamarin.FormsBook.Platform库中的StepSlider类。 请注意顶部的ValueChanged事件的定义以及Value属性中的更改触发该事件。 大部分可绑定属性定义都用于validateValue方法,它们确保属性在允许的范围内,以及coerceValue方法,它们确保属性在它们之间是一致的:

namespace Xamarin.FormsBook.Platform
{
    public class StepSlider : View
    {
        public event EventHandler<ValueChangedEventArgs> ValueChanged;
        public static readonly BindableProperty MinimumProperty =
            BindableProperty.Create(
                "Minimum",
                typeof(double),
                typeof(StepSlider),
                0.0,
                validateValue: (obj, min) => (double)min < ((StepSlider)obj).Maximum,
                coerceValue: (obj, min) =>
                {
                    StepSlider stepSlider = (StepSlider)obj;
                    stepSlider.Value = stepSlider.Coerce(stepSlider.Value,
                                                         (double)min,
                                                         stepSlider.Maximum);
                    return min;
                });
        public static readonly BindableProperty MaximumProperty =
            BindableProperty.Create(
                "Maximum",
                typeof(double),
                typeof(StepSlider),
                100.0,
                validateValue: (obj, max) => (double)max > ((StepSlider)obj).Minimum,
                coerceValue: (obj, max) =>
                {
                    StepSlider stepSlider = (StepSlider)obj;
                    stepSlider.Value = stepSlider.Coerce(stepSlider.Value,
                                                         stepSlider.Minimum,
                                                         (double)max);
                    return max;
                });
        public static readonly BindableProperty StepsProperty =
            BindableProperty.Create(
                "Steps",
                typeof(int),
                typeof(StepSlider),
                100,
                validateValue: (obj, steps) => (int)steps > 1);
        public static readonly BindableProperty ValueProperty =
            BindableProperty.Create(
                "Value",
                typeof(double),
                typeof(StepSlider),
                0.0,
                BindingMode.TwoWay,
                coerceValue: (obj, value) =>
                {
                    StepSlider stepSlider = (StepSlider)obj;
                    return stepSlider.Coerce((double)value,
                                             stepSlider.Minimum,
                                             stepSlider.Maximum);
                },
                propertyChanged: (obj, oldValue, newValue) =>
                {
                    StepSlider stepSlider = (StepSlider)obj;
                    EventHandler<ValueChangedEventArgs> handler = stepSlider.ValueChanged;
                    if (handler != null)
                        handler(obj, new ValueChangedEventArgs((double)oldValue,
                                                               (double)newValue));
                });
        public double Minimum
        {
            set { SetValue(MinimumProperty, value); }
            get { return (double)GetValue(MinimumProperty); }
        }
        public double Maximum
        {
            set { SetValue(MaximumProperty, value); }
            get { return (double)GetValue(MaximumProperty); }
        }
        public int Steps
        {
            set { SetValue(StepsProperty, value); }
            get { return (int)GetValue(StepsProperty); }
        }
        public double Value
        {
            set { SetValue(ValueProperty, value); }
            get { return (double)GetValue(ValueProperty); }
        }
        double Coerce(double value, double min, double max)
        {
            return Math.Max(min, Math.Min(value, max));
        }
    }
}

当Value属性更改时,StepSlider类会触发ValueChanged属性,但是当用户操作StepSlider的平台渲染器时,此类中没有任何内容可以更改Value属性。 那是留给渲染器类的。
再一次,让我们首先看看Xamarin.FormsBook.Platform.WinRT共享项目中的StepSliderRenderer的Windows实现,因为它更直接一些。 渲染器使用Windows.UI.Xaml.Controls.Slider进行本机控制。 为了避免Windows Slider和Xamarin.Forms Slider之间的命名空间冲突,using指令定义了win前缀以引用Windows命名空间并使用它来引用Windows Slider:

using System.ComponentModel;
using Xamarin.Forms;
using Win = Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
#if WINDOWS_UWP
using Xamarin.Forms.Platform.UWP;
#else
using Xamarin.Forms.Platform.WinRT;
#endif
[assembly: ExportRenderer(typeof(Xamarin.FormsBook.Platform.StepSlider),
                          typeof(Xamarin.FormsBook.Platform.WinRT.StepSliderRenderer))]
namespace Xamarin.FormsBook.Platform.WinRT
{
    public class StepSliderRenderer : ViewRenderer<StepSlider, Win.Slider>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<StepSlider> args)
        {
            base.OnElementChanged(args);
            if (Control == null)
            {
                SetNativeControl(new Win.Slider());
            }
            if (args.NewElement != null)
            {
                SetMinimum();
                SetMaximum();
                SetSteps();
                SetValue();
                Control.ValueChanged += OnWinSliderValueChanged;
            }
            else
            {
                Control.ValueChanged -= OnWinSliderValueChanged;
            }
        }
        __
    }
}

此渲染器与您之前看到的渲染器之间的最大区别在于,此渲染器在本机Windows Slider的ValueChanged事件上设置了事件处理程序。 (您很快就会看到事件处理程序。)但是,如果args.NewElement变为null,则表示不再有Xamarin.Forms元素附加到渲染器,并且不再需要事件处理程序。 此外,您很快就会看到事件处理程序引用从ViewRenderer类继承的Element属性,并且如果args.NewElement为null,则该属性也将为null。
因此,当args.NewElement变为null时,OnElementChanged将分离事件处理程序。 同样,只要args.NewElement变为null,就应释放为渲染器分配的任何资源。
OnElementPropertyChanged方法的重写检查StepSlider定义的四个属性的更改:

namespace Xamarin.FormsBook.Platform.WinRT
{
    public class StepSliderRenderer : ViewRenderer<StepSlider, Win.Slider>
    {
        __
        protected override void OnElementPropertyChanged(object sender,
                                                         PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(sender, args);
            if (args.PropertyName == StepSlider.MinimumProperty.PropertyName)
            {
                SetMinimum();
            }
            else if (args.PropertyName == StepSlider.MaximumProperty.PropertyName)
            {
                SetMaximum();
            }
            else if (args.PropertyName == StepSlider.StepsProperty.PropertyName)
            {
                SetSteps();
            }
            else if (args.PropertyName == StepSlider.ValueProperty.PropertyName)
            {
                SetValue();
            }
        }
        __
    }
}

Windows Slider定义了Minimum,Maximum和Value属性,就像Xamarin.Forms Slider和新的StepSlider一样。 但它没有定义Steps属性。 相反,它定义了StepFrequency属性,它与Steps属性相反。 要重现前面的示例(最小设置为5,最大设置为10,步骤设置为20),您可以将StepFrequency设置为0.25。 转换非常简单:

namespace Xamarin.FormsBook.Platform.WinRT
{
    public class StepSliderRenderer : ViewRenderer<StepSlider, Win.Slider>
    {
        __
        void SetMinimum()
        {
            Control.Minimum = Element.Minimum;
        }
        void SetMaximum()
        {
            Control.Maximum = Element.Maximum;
        }
        void SetSteps()
        {
            Control.StepFrequency = (Element.Maximum - Element.Minimum) / Element.Steps;
        }
        void SetValue()
        {
            Control.Value = Element.Value;
        }
        __
     }
}

最后,这是Windows Slider的ValueChanged处理程序。 这有责任在StepSlider中设置Value属性,然后触发自己的ValueChanged事件。 但是,存在一种用于从渲染器设置值的特殊方法。 此方法称为SetValueFromRenderer,由IElementController接口定义,并由Xamarin.Forms Element类实现:

namespace Xamarin.FormsBook.Platform.WinRT
{
    public class StepSliderRenderer : ViewRenderer<StepSlider, Win.Slider>
    {
        __
        void OnControlValueChanged(object sender, RangeBaseValueChangedEventArgs args)
        {
            ((IElementController)Element).SetValueFromRenderer(StepSlider.ValueProperty,
                                                               args.NewValue);
        }
    }
}

iOS UISlider具有MinValue,MaxValue和Value属性,并定义了ValueChanged事件,但它没有像Steps或StepFrequency属性那样的东西。 相反,Xamarin.FormsBook.Platform.iOS中的iOS StepSliderRenderer类在从ValueChanged事件处理程序调用SetValueFromRenderer之前对Value属性进行手动调整:

using System;
using System.ComponentModel;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(Xamarin.FormsBook.Platform.StepSlider),
                          typeof(Xamarin.FormsBook.Platform.iOS.StepSliderRenderer))]
namespace Xamarin.FormsBook.Platform.iOS
{
    public class StepSliderRenderer : ViewRenderer<StepSlider, UISlider>
    {
        int steps;
        protected override void OnElementChanged(ElementChangedEventArgs<StepSlider> args)
        {
            base.OnElementChanged(args);
            if (Control == null)
            {
                SetNativeControl(new UISlider());
            }
            if (args.NewElement != null)
            {
                SetMinimum();
                SetMaximum();
                SetSteps();
                SetValue();
                Control.ValueChanged += OnUISliderValueChanged;
            }
            else
            {
                Control.ValueChanged -= OnUISliderValueChanged;
            }
        }
        protected override void OnElementPropertyChanged(object sender,
                                                         PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(sender, args);
            if (args.PropertyName == StepSlider.MinimumProperty.PropertyName)
            {
                SetMinimum();
            }
            else if (args.PropertyName == StepSlider.MaximumProperty.PropertyName)
            {
                SetMaximum();
            }
            else if (args.PropertyName == StepSlider.StepsProperty.PropertyName)
            {
                SetSteps();
            }
            else if (args.PropertyName == StepSlider.ValueProperty.PropertyName)
            {
                SetValue();
            }
        }
        void SetMinimum()
        {
            Control.MinValue = (float)Element.Minimum;
        }
        void SetMaximum()
        {
            Control.MaxValue = (float)Element.Maximum;
        }
        void SetSteps()
        {
            steps = Element.Steps;
        }
        void SetValue()
        {
            Control.Value = (float)Element.Value;
        }
        void OnUISliderValueChanged(object sender, EventArgs args)
        {
            double increment = (Element.Maximum - Element.Minimum) / Element.Steps;
            double value = increment * Math.Round(Control.Value / increment);
            ((IElementController)Element).SetValueFromRenderer(StepSlider.ValueProperty, value);
        }
    }
}
目录
相关文章
|
6月前
|
存储
CocosCreator3.8研究笔记(二十二)CocosCreator 动画系统-动画剪辑和动画组件介绍
CocosCreator3.8研究笔记(二十二)CocosCreator 动画系统-动画剪辑和动画组件介绍
117 0
|
15天前
|
iOS开发 UED
实现一个自定义的iOS动画效果
【4月更文挑战第9天】本文将详细介绍如何在iOS平台上实现一个自定义的动画效果。我们将通过使用Core Animation框架来实现这个动画效果,并展示如何在不同的场景中使用它。文章的目标是帮助读者理解如何使用Core Animation框架来创建自定义动画,并提供一个简单的示例代码。
14 1
|
6月前
|
数据安全/隐私保护 iOS开发 MacOS
CocosCreator3.8研究笔记(二十四)CocosCreator 动画系统-动画编辑器实操-关键帧实现动态水印动画效果(2)
CocosCreator3.8研究笔记(二十四)CocosCreator 动画系统-动画编辑器实操-关键帧实现动态水印动画效果
|
6月前
|
数据安全/隐私保护
CocosCreator3.8研究笔记(二十四)CocosCreator 动画系统-动画编辑器实操-关键帧实现动态水印动画效果(1)
CocosCreator3.8研究笔记(二十四)CocosCreator 动画系统-动画编辑器实操-关键帧实现动态水印动画效果
|
6月前
CocosCreator3.8研究笔记(二十三)CocosCreator 动画系统-动画编辑器相关功能面板说明
CocosCreator3.8研究笔记(二十三)CocosCreator 动画系统-动画编辑器相关功能面板说明
|
9月前
|
JavaScript 定位技术
WebGis——Pixi开发vue项目之使用遮罩实现图形缓慢填充颜色(三)
WebGis——Pixi开发vue项目之使用遮罩实现图形缓慢填充颜色(三)
|
数据安全/隐私保护 iOS开发 开发者
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建(二)
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建
283 0
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建(二)
第二十七章:自定义渲染器(六)
有趣的是,Android SeekBar小部件具有与Steps属性等效的功能,但不等同于Minimum和Maximum属性! 这怎么可能? SeekBar实际上定义了一个名为Max的整数属性,SeekBar的Progress属性始终是一个从0到Max的整数。
734 0
|
前端开发 Android开发 iOS开发
第二十七章:自定义渲染器(四)
渲染器和属性(2) 现在,对于iOS,EllipseUIView类是存在的,可以使用EllipseUIView作为本机控件来编写EllipseViewRenderer。 从结构上讲,这个类几乎与Windows渲染器相同: using System.
549 0
|
Android开发 iOS开发 Windows
第二十七章:自定义渲染器(三)
渲染器和属性(1) Xamarin.Forms包含一个BoxView元素,用于显示矩形颜色块。 你有没有希望你有类似的东西画一个圆圈,或使它更通用,椭圆?这就是EllipseView的目的。 但是,因为您可能希望在多个应用程序中使用EllipseView,所以它在第20章“异步和文件I / O”中介绍的Xamarin.FormsBook.Platform库中实现。
757 0

热门文章

最新文章