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

简介: 渲染器和事件(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);
        }
    }
}
目录
相关文章
|
Oracle Java 关系型数据库
在macOS系统中 下载、安装、使用Java8
在macOS系统中 下载、安装、使用Java8
15724 0
在macOS系统中 下载、安装、使用Java8
|
9月前
|
Cloud Native 关系型数据库 分布式数据库
2025阿里云PolarDB开发者大会,他们都来了
2025阿里云PolarDB开发者大会,超全议程,重磅来袭
|
应用服务中间件 nginx Docker
docker swarm创建覆盖网络
【10月更文挑战第14天】
133 3
|
11月前
|
小程序 API 开发工具
支付宝支付对接案例详解
支付宝支付对接指南,涵盖电脑网站、当面付和手机网站支付方式。对接前需了解支付宝开放平台和商家平台的区别,完成注册、实名认证、创建应用及签约产品等步骤。配置支付环境包括API密钥、回调地址和开发环境搭建。提供三种SDK供选择,推荐使用alipay-easysdk。详细步骤和示例代码可参考官方文档。
1025 15
|
监控 Python
`pytest-qt` 是一个用于在 Qt 应用程序中进行 GUI 测试的 pytest 插件。
`pytest-qt` 是一个用于在 Qt 应用程序中进行 GUI 测试的 pytest 插件。
|
存储 持续交付 开发者
掌握Docker容器化:提升开发效率与应用部署
【10月更文挑战第4天】在现代软件开发中,Docker容器化技术因其轻量级、可移植和快速部署的特点,成为提升开发效率和简化部署流程的关键工具。本文介绍了Docker的基本概念、核心组件及其优势,并探讨了如何在开发环境中搭建、微服务架构及CI/CD流程中有效利用Docker,助力软件开发更加高效便捷。
|
机器学习/深度学习 自然语言处理 大数据
社交媒体的情感分析大数据模型
构建基于大数据的情感分析模型,利用Python和机器学习处理社交媒体数据。情感分析识别文本情感倾向,助力市场洞察和舆情监控。技术栈包括Python、NLP库(nltk, spaCy, TextBlob, VADER)、Scikit-learn、TensorFlow/PyTorch及大数据工具。数据收集(如Twitter API)、预处理(去除噪声、分词)、特征提取(TF-IDF、词嵌入)、模型训练(逻辑回归、BERT)是关键步骤。模型能捕捉文本情感,支持决策,随着技术进步,应用前景广阔。
1770 10
|
机器学习/深度学习 编解码 计算机视觉
【YOLOv8改进】 SPD-Conv空间深度转换卷积,处理低分辨率图像和小对象问题 (论文笔记+引入代码)
YOLO目标检测专栏探讨了CNN在低分辨率和小目标检测中的局限性,提出SPD-Conv新架构,替代步长卷积和池化层,通过空间到深度层和非步长卷积保持细粒度信息。创新点包括消除信息损失、通用设计和性能提升。YOLOv5和ResNet应用SPD-Conv后,在困难任务上表现优越。详情见YOLO有效改进系列及项目实战目录。
|
分布式计算 关系型数据库 MySQL
开源数据集成平台SeaTunnel:MySQL实时同步到es
免费支持 MySQL 实时同步到 ElasticSearch 的工具很少,Apache SeaTunnel 是一个高性能开源大数据集成工具,提供灵活易用、易扩展并支持千亿级数据集成的解决方案,已经在B站、腾讯云、字节等数百家公司使用。
1827 1
|
Ubuntu Unix 大数据
【Linux从入门到精通】Linux的简单介绍及环境搭建
【Linux从入门到精通】Linux的简单介绍及环境搭建
1183 0