Xamarin Forms - CustomObject,XAML 初始化,Setter 未调用,DependencyProperty,我迷路了?

Xamarin Forms - CustomObject, XAML Initialization, Setter not called, DependencyProperty, I'm lost?

我有问题,我搜索了解决方案。幸运的是,我对它进行了很多 post 的讨论,但我对找到的解释感到困惑。初始问题来自 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 {} 中,不被调用..

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

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

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

我终于在这里找到了解决方案 =>

从 Whosebug 复制粘贴。以下答案由 Stephane Delcroix 提供,感谢他!


这里有多个问题:

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

让我换个顺序回答。

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

BindableProperty 声明是正确的,但可以通过使用 IList<string>:

进行改进
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 时从未调用 属性 setter?

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

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

当定义 Binding 时,无论是来自代码还是来自 Xaml,属性 访问器再次被绕过,当 属性 需要时使用 SetValue()待修改。当 SetValue() 被调用时,propertyChanged 委托在 属性 改变之后执行(这里要完整,propertyChanging 之前被调用 属性 变化)。

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

的上下文中使用
  • 可以在属性上定义一个[TypeConverter]属性,并将被使用
  • 启用 XamlC,属性 签名可用于在编译时推断 BindableProperty.
  • Type

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

为什么我的绑定失败了?

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

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

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