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

简介: 渲染器和事件(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);
        }
    }
}
目录
相关文章
|
1月前
|
Swift iOS开发 UED
实现一个自定义的iOS动画效果
本文介绍如何使用Swift和UIKit在iOS应用中实现一个自定义按钮动画,当按钮被点击时,其颜色从蓝色渐变为绿色,形状从圆形变为椭圆形,释放后恢复原状。通过UIView动画方法实现这一效果,代码示例展示了动画的平滑过渡和状态切换,有助于提升应用的视觉体验和用户交互。
49 1
|
JavaScript 定位技术
WebGis——Pixi开发vue项目之使用遮罩实现图形缓慢填充颜色(三)
WebGis——Pixi开发vue项目之使用遮罩实现图形缓慢填充颜色(三)
|
物联网
ThingJS有4种技术实现对象拾取方式
对象的拾取,这是3D技术的一个专业术语。
ThingJS有4种技术实现对象拾取方式
第二十七章:自定义渲染器(六)
有趣的是,Android SeekBar小部件具有与Steps属性等效的功能,但不等同于Minimum和Maximum属性! 这怎么可能? SeekBar实际上定义了一个名为Max的整数属性,SeekBar的Progress属性始终是一个从0到Max的整数。
762 0
|
前端开发 Android开发 iOS开发
第二十七章:自定义渲染器(四)
渲染器和属性(2) 现在,对于iOS,EllipseUIView类是存在的,可以使用EllipseUIView作为本机控件来编写EllipseViewRenderer。 从结构上讲,这个类几乎与Windows渲染器相同: using System.
575 0
|
Android开发 iOS开发 Windows
第二十七章:自定义渲染器(三)
渲染器和属性(1) Xamarin.Forms包含一个BoxView元素,用于显示矩形颜色块。 你有没有希望你有类似的东西画一个圆圈,或使它更通用,椭圆?这就是EllipseView的目的。 但是,因为您可能希望在多个应用程序中使用EllipseView,所以它在第20章“异步和文件I / O”中介绍的Xamarin.FormsBook.Platform库中实现。
786 0
|
Android开发 iOS开发
第二十七章:自定义渲染器(二)
您好,自定义渲染器! HelloRenderers程序主要演示编写简单渲染器所需的开销。 该程序定义了一个名为HelloView的新View衍生,旨在显示一个简单的固定文本字符串。 这是HelloRenderers可移植类库项目中的完整HelloView.cs文件: using Xamarin.Forms; namespace HelloRenderers { public class HelloView : View { } } 而已! 但请注意,该类被定义为public。
4595 0
|
Android开发 iOS开发 Windows
第二十七章:自定义渲染器(一)
Xamarin.Forms的核心可能看起来很神奇:像Button这样的单个元素在iOS,Android和Windows操作系统下显示为本机按钮的能力。在本章中,您将看到如何在所有三个平台上的Xamarin.Forms中的每个元素都由称为渲染器的特殊类支持。
739 0
|
JavaScript Android开发
第二十二章:动画(十)
你自己的缓和功能您可以轻松制作自己的缓动功能。所需要的只是一个类型为Func 的方法,它是一个带有double参数和double返回值的函数。这是一个传递函数:它应该为0的参数返回0,并且对于1的参数应该返回1.但是在这两个值之间,任何事情都会发生。
599 0
|
JavaScript Android开发
第二十二章:动画(九)
缓解功能你已经看过以下关键帧动画,它以一种方式摆动Button,然后是其他: async void OnButtonClicked(object sender, EventArgs args) { await button.RotateTo(90, 250); await button.RotateTo(-90, 500); await button.RotateTo(0, 250); } 但动画看起来并不合适。
980 0