【问题标题】:Xamarin Forms - CustomObject, XAML Initialization, Setter not called, DependencyProperty, I'm lost?Xamarin Forms - CustomObject、XAML 初始化、未调用 Setter、DependencyProperty,我迷路了吗?
【发布时间】:2017-01-19 05:00:02
【问题描述】:

我有一个问题,我搜索了一个解决方案。幸运的是,我发布了很多关于它的帖子,但我对找到的解释感到迷茫。 initale 问题来自personal project about the polyline of the Xamarin.Forms.Map,其中初始化是通过来自 XAML 部分的绑定实现的..

举个例子说明一下:

我有一个对象 CustomMap.cs,它继承自 Xamarin.Forms.Map(此文件位于 PCL 部分 -> CustomControl/CustomMap.cs)

public class CustomMap : Map, INotifyPropertyChanged
{
    public static readonly BindableProperty PolylineAddressPointsProperty =
        BindableProperty.Create(nameof(PolylineAddressPoints), typeof(List<string>), typeof(CustomMap), null);
    public List<string> PolylineAddressPoints
    {
        get { return (List<string>)GetValue(PolylineAddressPointsProperty); }
        set
        {
            SetValue(PolylineAddressPointsProperty, value);
            this.GeneratePolylineCoordinatesInner();
        }
    }   
    // ...
}

因此,页面的 Xaml 部分(调用该控件的位置)如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:control="clr-namespace:MapPolylineProject.CustomControl;assembly=MapPolylineProject"
         x:Class="MapPolylineProject.Page.MainPage">

  <ContentPage.Content>
    <control:CustomMap x:Name="MapTest" PolylineAddressPoints="{Binding AddressPointList}"
                                       VerticalOptions="Fill" HorizontalOptions="Fill"/>

  </ContentPage.Content>

</ContentPage>

Csharp部分:

public partial class MainPage : ContentPage
{
    public List<string> AddressPointList { get; set; }

    public MainPage()
    {
        base.BindingContext = this;

        AddressPointList = new List<string>()
        {
            "72230 Ruaudin, France",
            "72100 Le Mans, France",
            "77500 Chelles, France"
        };

        InitializeComponent();

        //MapTest.PolylineAddressPoints = AddressPointList;
    }
}

所以,如果我从对象实例编辑PolylineAddressPoints,一切都很好(如果注释部分没有被注释..),但是如果我从 XAML 中初始化值(从InitializeComponent();),它不起作用,SetValue,在Set {},没有被调用..

然后我在网上搜索了它并获得了一些关于 依赖属性? 或类似的东西。所以我尝试了一些解决方案,但是,来自 WPF,所以一些方法,比如DependencyProperty.Register();。所以是的,我找不到解决问题的方法..

我也想到了一些事情,如果DependencyProperty.Register(); 将存在于 Xamarin.Forms 中,那么这意味着我必须为每个值都这样做吗?因为,如果每个值都必须由 XAML 绑定逻辑 设置,那将无法正常工作,我必须注册每个值,不是吗?

如果我不清楚,我很抱歉,但我对这个问题很迷茫。请不要犹豫,询问更多细节,提前谢谢!

【问题讨论】:

    标签: dependency-properties


    【解决方案1】:

    我终于在这里找到了解决方案 => Ignore the Binding initialization

    从 Stackoverflow 复制粘贴。以下答案由 Stephane Delcroix 给出,感谢他!


    这里有多个问题:

    • 为什么在使用 Xaml 时从不调用属性设置器?
    • 我是否正确定义了我的 BindableProperty ?
    • 为什么我的绑定失败了?

    让我以不同的顺序回答它们。

    我是否正确定义了我的 BindableProperty ?

    BindableProperty 声明是正确的,但可以通过使用IList&lt;string&gt; 来改进:

    public static readonly BindableProperty PolylineAddressPointsProperty =
        BindableProperty.Create(nameof(PolylineAddressPoints), typeof(IList<string>), typeof(CustomMap), null);
    

    但是属性访问器是错误的,应该只包含这个:

    public IList<string> PolylineAddressPoints
    {
        get { return (IList<string>)GetValue(PolylineAddressPointsProperty); }
        set { SetValue(PolylineAddressPointsProperty, value); }
    }
    

    我会在回答下一个问题时告诉你原因。但是您想在属性更改时调用方法。为此,您必须将propertyChanged 委托引用到CreateBindableProperty,如下所示:

    public static readonly BindableProperty PolylineAddressPointsProperty =
        BindableProperty.Create(nameof(PolylineAddressPoints), typeof(IList<string>), typeof(CustomMap), null,
                                propertyChanged: OnPolyLineAddressPointsPropertyChanged);
    

    您也必须声明该方法:

    static void OnPolyLineAddressPointsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        ((CustomMap)bindable).OnPolyLineAddressPointsPropertyChanged((IList<string>)oldValue, (IList<string>)newValue);
    }
    
    void OnPolyLineAddressPointsPropertyChanged(IList<string> oldValue, IList<string> newValue)
    {
        GeneratePolylineCoordinatesInner();
    }
    

    为什么在使用 Xaml 时从不调用属性设置器?

    该属性和属性访问器仅在通过代码访问该属性时才被调用。 C# 代码。

    当使用 Xaml 中的 BindablePrperty 后备存储设置属性时,会绕过属性访问器并直接使用 SetValue()

    从代码或 Xaml 定义 Binding 时,再次绕过属性访问器,并在需要修改属性时使用 SetValue()。而当SetValue() 被调用时,propertyChanged 委托在属性改变之后被执行(这里要完整,propertyChanging 在属性改变之前被调用)。

    如果可绑定属性仅由 xaml 使用,或者在 Binding 上下文中使用,您可能想知道为什么还要费心定义该属性。好吧,我说属性访问器没有被调用,但是它们在 Xaml 和 XamlC 的上下文中使用:

    • 可以在属性上定义[TypeConverter] 属性,并将使用该属性
    • 启用XamlC,属性签名可用于在编译时推断BindablePropertyType

    因此,始终为公共 BindableProperties 声明属性访问器是一个好习惯。总是。

    为什么我的绑定失败了?

    当您使用 CustomMap 作为机器人 ViewViewModel(我不会告诉 Mvvm 警察)时,在您的构造函数中这样做就足够了:

    BindingContext = this; //no need to prefix it with base.
    

    正如你已经在做的那样,一旦你按照我之前解释的方式修改了 BindableProperty 声明,你的 Binding 应该可以工作了。

    【讨论】:

    • 这里的关键事实是 XAML“inflator”直接调用 SetValue() 并绕过 set()。我浏览了很多 Xamarin 文档,但从未见过这个。你知道它记录在哪里吗?谢谢。
    猜你喜欢
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    • 2012-02-25
    • 2017-05-29
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    相关资源
    最近更新 更多