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 View
和 ViewModel
(我不会告诉 Mvvm 警察),在你的构造函数中这样做就足够了:
BindingContext = this; //no need to prefix it with base.
正如您已经在做的那样,一旦您按照我之前解释的方式修改了 BindableProperty
声明,您的 Binding
就应该可以工作了。
我有问题,我搜索了解决方案。幸运的是,我对它进行了很多 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 View
和 ViewModel
(我不会告诉 Mvvm 警察),在你的构造函数中这样做就足够了:
BindingContext = this; //no need to prefix it with base.
正如您已经在做的那样,一旦您按照我之前解释的方式修改了 BindableProperty
声明,您的 Binding
就应该可以工作了。