日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
讀MAUI源代碼理解可綁定對(duì)象和可綁定屬性的存儲(chǔ)機(jī)制

和 UWP 與 WPF 不同的是在 MAUI 里面,使用可綁定對(duì)象 BindableObject 替換了依賴對(duì)象的概念,我閱讀了 MAUI 的源代碼發(fā)現(xiàn)其實(shí)只是命名變更了,里面的機(jī)制和設(shè)計(jì)思想都是差不多的。在 MAUI 里面提供 BindableObject 用來(lái)支持可綁定屬性機(jī)制和附加屬性機(jī)制,本文將告訴大家在 MAUI 里面是如何在可綁定對(duì)象里面提供可綁定屬性和附加屬性的存儲(chǔ)的機(jī)制。

在 WPF 里面,依賴屬性的提出的一部分原因是為了省內(nèi)存。在 MAUI 里面,我猜測(cè)省內(nèi)存是可綁定對(duì)象提出的一個(gè)原因。由于一個(gè)界面控件,例如按鈕等,有著非常龐大數(shù)量的屬性,假設(shè)每個(gè)控件里面的所有屬性都是需要獨(dú)立的對(duì)象不能共用,那么在復(fù)雜界面上,將會(huì)因?yàn)榇罅康目丶拇罅繉傩哉加么罅康膬?nèi)存??山壎▽?duì)象里面可以實(shí)現(xiàn)在屬性沒(méi)有被賦值時(shí),將可以使用默認(rèn)值,而對(duì)于大部分控件來(lái)說(shuō),很多不常用的屬性都是使用默認(rèn)值即可??山壎▽?duì)象需要解決的是讓可綁定屬性可以代替普通的 CLR 屬性,對(duì)可綁定屬性進(jìn)行賦值時(shí),可以值和可綁定對(duì)象關(guān)聯(lián),從而可以讀取出來(lái)。既然名字叫可綁定對(duì)象,那自然也要實(shí)現(xiàn)綁定的支持,綁定的支持的核心就是通知,需要支持在屬性值變更的時(shí)候進(jìn)行通知。接下來(lái)將通過(guò)閱讀源代碼了解在 MAUI 里是如何實(shí)現(xiàn)。

打開(kāi) MAUI 的 BindableObject 的源代碼,可以看到在 BindableObject 里有 _properties 字段,定義如下:

public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
{
readonly Dictionary _properties = new Dictionary(4);
}

沒(méi)錯(cuò),這就是在 MAUI 里面的可綁定對(duì)象的存儲(chǔ)核心實(shí)現(xiàn)。在 MAUI 的可綁定對(duì)象里面通過(guò) _properties? 字典存放可綁定屬性的值內(nèi)容,字典的 Key 是 BindableProperty 可綁定屬性,字典的 Value 是 BindablePropertyContext 可綁定屬性上下文,初始化字典默認(rèn)占用 4 個(gè)空間,默認(rèn)初始化空間是為了優(yōu)化而已,沒(méi)有什么特別用途。通過(guò)此字典定義可以了解到存儲(chǔ)的核心實(shí)現(xiàn)就是將可綁定屬性和對(duì)應(yīng)的值存入到對(duì)象的字典里,例如給某個(gè)可綁定對(duì)象的某個(gè)叫 Xxx 的可綁定屬性進(jìn)行賦值,那將會(huì)對(duì) _properties 字典更新 Xxx 屬性的值內(nèi)容。

在 MAUI 的實(shí)現(xiàn)是,在可綁定對(duì)象里面,使用 SetValueCore 方法進(jìn)行屬性更新賦值,我刪掉了不關(guān)鍵的邏輯的代碼如下:

internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
{
// 獲取或創(chuàng)建可綁定屬性上下文信息
BindablePropertyContext context = GetOrCreateContext(property);

SetValueActual(property, context, value, currentlyApplying, attributes, silent);
}

BindablePropertyContext GetOrCreateContext(BindableProperty property) => GetContext(property) ?? CreateAndAddContext(property);

internal BindablePropertyContext GetContext(BindableProperty property) => _properties.TryGetValue(property, out var result) ? result : null;

BindablePropertyContext CreateAndAddContext(BindableProperty property)
{
var context = new BindablePropertyContext { ... };
_properties.Add(property, context);
return context;
}


void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
{
// 觸發(fā)對(duì)象變更前事件

context.Value = value;

// 觸發(fā)對(duì)象已變更事件
}

可以看到賦值的第一步就是調(diào)用 GetOrCreateContext 方法,嘗試去拿到上下文信息,如果拿不到就創(chuàng)建。這里的用到的 BindablePropertyContext 上下文信息是存儲(chǔ)可綁定屬性的關(guān)鍵,在 BindablePropertyContext 里面存放了很多字段,定義如下:

public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
{
internal class BindablePropertyContext
{
public BindableContextAttributes Attributes;
public BindingBase Binding;
public Queue DelayedSetters;
public BindableProperty Property;
public object Value;

public bool StyleValueSet;
public object StyleValue;
}
}

可以看到 BindablePropertyContext 是一個(gè)內(nèi)部類型,也不對(duì)外開(kāi)放。在 BindablePropertyContext 里面重要的就是 Value? 字段,表示存儲(chǔ)的實(shí)際值內(nèi)容。其次為了更好的支持綁定,也添加了 Binding 字段。

在獲取到 BindablePropertyContext 上下文之后,即可進(jìn)行賦值,賦值是調(diào)用 SetValueActual 方法進(jìn)行賦值,賦值前后分別觸發(fā)事件用來(lái)通知。觸發(fā)通知事件最重要的功能是讓綁定可以有刷新的時(shí)機(jī)。如此即可完成賦值過(guò)程。

通知事件是分別觸發(fā)可綁定的對(duì)象的通知事件和對(duì)應(yīng)的可綁定屬性的通知事件,如下面代碼:

void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
{
// 觸發(fā)對(duì)象變更前事件
property.PropertyChanging?.Invoke(this, original, value);
OnPropertyChanging(property.PropertyName);

context.Value = value;

// 觸發(fā)對(duì)象已變更事件
OnPropertyChanged(property.PropertyName);
property.PropertyChanged?.Invoke(this, original, value);
}

通過(guò)以上代碼可以看到,可綁定對(duì)象給可綁定屬性賦值的時(shí)候,就是先獲取或創(chuàng)建可綁定屬性上下文,將賦值的參數(shù)值給到 可綁定屬性上下文 的 Value 字段。如此完成賦值過(guò)程。

由于賦值的參數(shù)值被放入到 可綁定屬性上下文 的 Value 字段,而 可綁定屬性上下文 又放入到 _properties? 字典里,相當(dāng)于間接將 賦值的參數(shù)值 放入到 _properties 字典里。自然在獲取值過(guò)程里,也需要從字典里面讀取。在 MAUI 里面讀取可綁定屬性是通過(guò) GetValue 方法實(shí)現(xiàn),代碼如下:

public object GetValue(BindableProperty property)
{
if (property == null)
throw new ArgumentNullException(nameof(property));

var context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);

return context == null ? property.DefaultValue : context.Value;
}

以上代碼的判斷 BindableProperty 的 DefaultValueCreator 屬性邏輯是 MAUI 特有的邏輯,和 WPF 與 UWP 不相同,咱下文再聊?;氐将@取屬性的方法上,是通過(guò)先獲取對(duì)象的可綁定上下文信息,如果能獲取到可綁定上下文,證明此可綁定對(duì)象的這個(gè)可綁定屬性曾經(jīng)被賦值過(guò),需要用賦值更新的內(nèi)容。如果拿到的可綁定屬性上下文是空,那就使用可綁定屬性定義的默認(rèn)值即可。

在 MAUI 里面,通過(guò) BindableProperty 的 DefaultValueCreator 屬性簡(jiǎn)化了可綁定屬性的定義,和讓可綁定屬性更加強(qiáng)大。使用 MAUI 的可綁定屬性和可綁定對(duì)象對(duì)比 WPF 的依賴屬性和依賴對(duì)象的實(shí)現(xiàn),可以看到 MAUI 的實(shí)現(xiàn)實(shí)在簡(jiǎn)潔很多。在 MAUI 里的 BindableProperty 的 DefaultValueCreator 屬性是一個(gè)委托,定義如下:

public sealed class BindableProperty
{
public delegate object CreateDefaultValueDelegate(BindableObject bindable);

internal CreateDefaultValueDelegate DefaultValueCreator { get; }
}

可以看到 BindableProperty 的 DefaultValueCreator 屬性的委托是支持給傳入的可綁定對(duì)象進(jìn)行處理,對(duì)可綁定對(duì)象返回特定的默認(rèn)值。這里值得說(shuō)明的是,通過(guò)委托是可以特例給可綁定對(duì)象不同的默認(rèn)值的,但不代表著一定是不同的可綁定對(duì)象都一定需要不同的默認(rèn)值對(duì)象。這里只是一個(gè)委托,讓委托返回相同的對(duì)象是完全可以的。這個(gè)委托更多的是使用在判斷可綁定對(duì)象類型,根據(jù)可綁定類型對(duì)象或者狀態(tài),返回不同的默認(rèn)值?;蛘呤欠祷匾粋€(gè)需要運(yùn)行時(shí)動(dòng)態(tài)計(jì)算值,而不是一個(gè)可以寫(xiě)固定在代碼里面的參數(shù)。

例如對(duì)于 FontSize 的可綁定屬性的定義里,就采用讓不同的控件返回不同的默認(rèn)字體大小,定義如下:

public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create("FontSize", typeof(double), typeof(IFontElement), 0d,
propertyChanged: OnFontSizeChanged,
defaultValueCreator: FontSizeDefaultValueCreator);

static object FontSizeDefaultValueCreator(BindableObject bindable)
=> ((IFontElement)bindable).FontSizeDefaultValueCreator();

也就是說(shuō)對(duì)于不同的可綁定對(duì)象,獲取到的默認(rèn)的字體大小是根據(jù)對(duì)應(yīng)的可綁定對(duì)象的 FontSizeDefaultValueCreator 方法實(shí)現(xiàn)決定,不同的可綁定對(duì)象可以有不同的實(shí)現(xiàn),從而實(shí)現(xiàn)了讓默認(rèn)值關(guān)聯(lián)上具體的可綁定對(duì)象類型。這個(gè)創(chuàng)新的設(shè)計(jì),可以省掉在 WPF 里面的大量默認(rèn)依賴屬性值重寫(xiě)的邏輯代碼,省掉了這部分代碼,也可以大量減少的機(jī)制,從而減少更多的代碼。

例如 Span 和 Editor 控件對(duì)字體大小默認(rèn)值有不同的實(shí)現(xiàn)。

public class Span : GestureElement, IFontElement
{
double IFontElement.FontSizeDefaultValueCreator() =>
double.NaN;
}

public partial class Button : View, IFontElement
{
double IFontElement.FontSizeDefaultValueCreator() =>
this.GetDefaultFontSize();
}

同樣,對(duì)于某些可綁定屬性來(lái)說(shuō),需要給每個(gè)可綁定對(duì)象的對(duì)象不同的默認(rèn)值對(duì)象,例如 Grid 里面的 RowDefinitions 屬性。大家都知道,在 Grid 里面的 RowDefinitions 是一個(gè)集合,如果集合也是一個(gè)共享的默認(rèn)值,那自然會(huì)存在默認(rèn)值污染。如果默認(rèn)值是一個(gè)空值,那么將會(huì)讓 Grid 邏輯里面存在大量的判斷空邏輯,或者需要其他額外的初始化邏輯。在 MAUI 里面,通過(guò) DefaultValueCreator 委托,實(shí)現(xiàn)了每個(gè) Grid 對(duì)象使用獨(dú)立的默認(rèn)值對(duì)象,代碼如下:

public class Grid : Layout, IGridLayout
{
public static readonly BindableProperty RowDefinitionsProperty = BindableProperty.Create("RowDefinitions",
typeof(RowDefinitionCollection), typeof(Grid), null, validateValue: (bindable, value) => value != null,
propertyChanged: UpdateSizeChangedHandlers, defaultValueCreator: bindable =>
{
// 每個(gè) Grid 對(duì)象使用獨(dú)立的,新創(chuàng)建的默認(rèn)值對(duì)象
var rowDef = new RowDefinitionCollection();
rowDef.ItemSizeChanged += ((Grid)bindable).DefinitionsChanged;
return rowDef;
});
}

在 MAUI 里面除了可綁定屬性之外,還有一個(gè)特殊的屬性類型,附加屬性。附加屬性可以定義在任意的類型里面,通過(guò)附加屬性,給某個(gè)現(xiàn)有的類型附加上屬性。功能上和 WPF 或 UWP 的附加屬性功能是相同的??山壎▽傩院透郊訉傩远际窍嗤?BindableProperty 類型,只是在創(chuàng)建的時(shí)候,調(diào)用的靜態(tài)創(chuàng)建方法不同而已。對(duì)于可綁定屬性來(lái)說(shuō),調(diào)用的是 BindableProperty.Create? 方法創(chuàng)建。對(duì)于附加屬性來(lái)說(shuō),調(diào)用 BindableProperty.CreateAttached 創(chuàng)建。在 MAUI 里面,通過(guò)閱讀代碼,我認(rèn)為分開(kāi)兩個(gè)方法更多的是為了兼容 WPF 或 UWP 的寫(xiě)法,沒(méi)有非常本質(zhì)的差別,參數(shù)也差不多,如下面代碼:

internal static BindableProperty Create(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
CreateDefaultValueDelegate defaultValueCreator = null)
{
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging,
defaultValueCreator: defaultValueCreator);
}

internal static BindableProperty CreateAttached(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
bool isReadOnly, CreateDefaultValueDelegate defaultValueCreator = null)
{
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, isReadOnly,
defaultValueCreator);
}

如此可以看到可綁定屬性和附加屬性從參數(shù)上是似乎相同的。由于附加屬性也是一個(gè)可綁定屬性類型,同理可以了解到附加屬性的存儲(chǔ)也和可綁定對(duì)象的可綁定屬性的存儲(chǔ)是相同的。如此也能解答一個(gè)問(wèn)題,在 MAUI 的附加屬性,附加到對(duì)象上,附加屬性的參數(shù)值是如何跟隨對(duì)象的生命周期的問(wèn)題。由于附加屬性也是一個(gè)可綁定屬性,同樣將參數(shù)值存在可綁定對(duì)象的 _properties? 字典里面,在對(duì)象會(huì) GC 回收時(shí),自然 _properties 字段也被回收,那放在字典里面的參數(shù)值也自然被減去引用,當(dāng)參數(shù)值的沒(méi)有被引用時(shí),也就自然被回收。

在 MAUI 里面,可綁定對(duì)象基類型的意義就是提供了可綁定屬性的機(jī)制,存儲(chǔ)可綁定屬性的方式就是通過(guò) _properties 字典存放。通過(guò)字典存放的內(nèi)容是被賦值更改的屬性,沒(méi)有賦值更改的屬性是沒(méi)有被放入到字典里面,獲取在字典里面沒(méi)有存放的屬性時(shí),將會(huì)通過(guò)對(duì)應(yīng)的可綁定屬性獲取到默認(rèn)值。默認(rèn)值的獲取有兩個(gè)方式,一個(gè)是可綁定屬性的固定的默認(rèn)值屬性,另一個(gè)是通過(guò)可綁定屬性的默認(rèn)值創(chuàng)建委托創(chuàng)建默認(rèn)值。在 MAUI 里的可綁定屬性的默認(rèn)值創(chuàng)建委托是一個(gè)創(chuàng)新,可以寫(xiě)出讓不同的可綁定對(duì)象使用不同的默認(rèn)值的功能,也可以寫(xiě)出根據(jù)不同的可綁定對(duì)象類型返回不同的默認(rèn)值,通過(guò)委托的方式靈活實(shí)現(xiàn)復(fù)雜的功能。


網(wǎng)站欄目:讀MAUI源代碼理解可綁定對(duì)象和可綁定屬性的存儲(chǔ)機(jī)制
文章路徑:http://www.5511xx.com/article/cciscgs.html