Silverlight 数据绑定 (2):Source to Target
2008-10-24 11:47:07 来源:WEB开发网俗话说,要知其然,知其所以然。上面的代码说明了在数据源对象中可以设计一个事件通知属性值的变化,并在适当的时候触发之。但是我们并不知道有谁监听了这个事件,并且把这个通知传达到绑定目标对象 (binding target),也就是 UI.
我们用 Reflector 看看 Silverlight 2 beta 2 的源代码,会发现下列两个关键的类:
internal class BindingExpression : BindingExpressionBase
{
// ...
// 连接到数据源
private void ConnectToSource(int index)
{
this._binding._isSealed = true;
if (this._binding.Path.PathParts == null)
{
this._cachedValue = this._source;
}
else if ((this._sourceListeners == null) || (index != this._sourceListeners.Length))
{
bool flag = false;
try
{
object source;
if ((this._sourceListeners == null) && (this._binding.Mode != BindingMode.OneTime))
{
this._sourceListeners = new WeakPropertyChangedListener[this._binding.Path.PathParts.Length];
}
if (index == 0)
{
source = this._source;
}
else
{
source = this._sourceListeners[--index].Source;
}
for (int i = index; i < this._binding.Path.PathParts.Length; i++)
{
if (source == null)
{
flag = true;
return;
}
if ((this._binding.Mode != BindingMode.OneTime) && (this._sourceListeners[i] == null))
{
// 这里尝试创建源对象的属性变更监听器 (A)
this._sourceListeners[i] = WeakPropertyChangedListener.CreateIfNecessary(source, this);
}
this._sourcePropertyInfo = source.GetType().GetProperty(this._binding.Path.PathParts[i]);
this._leafSourceObject = source;
if (this._sourcePropertyInfo == null)
{
TraceBindingError("The path '" + this._binding._path.Path + "' is invalid");
this._leafSourceObject = null;
flag = true;
return;
}
try
{
source = this._sourcePropertyInfo.GetValue(source, null);
}
catch (TargetInvocationException)
{
TraceBindingError("Could not connect to '" + this._binding._path.Path + "'");
this._leafSourceObject = null;
return;
}
}
if ((this._binding.Mode == BindingMode.OneTime) || (this._sourceListeners[this._sourceListeners.Length - 1] == null))
{
this._cachedValue = source;
}
flag = true;
}
finally
{
if (!flag)
{
this.DisconnectFromSource(index);
this._sourcePropertyInfo = null;
this._leafSourceObject = null;
}
}
}
}
}
internal class WeakPropertyChangedListener
{
// ...
// 被 (A) 处代码调用 (B)
internal static WeakPropertyChangedListener CreateIfNecessary(object source, BindingExpression bindingExpression)
{
// 查看数据源是否实现了 INotifyPropertyChanged 接口
INotifyPropertyChanged notify = source as INotifyPropertyChanged;
if (notify != null)
{
// 如果有,创建一个监听器,调用 (C)
return new WeakPropertyChangedListener(notify, bindingExpression);
}
return null;
}
// 构造函数 (C)
private WeakPropertyChangedListener(INotifyPropertyChanged notify, BindingExpression bindingExpression)
{
this._notifyPropertyChanged = notify;
// 这里注册事件的回调函数,以便在属性变化时获得通知(调用 D)
notify.PropertyChanged += new PropertyChangedEventHandler(this.PropertyChangedCallback);
this._weakBindingExpression = new WeakReference(bindingExpression);
}
// 回调函数 (D)
private void PropertyChangedCallback(object sender, PropertyChangedEventArgs args)
{
BindingExpression target = this._weakBindingExpression.Target as BindingExpression;
// 这里触发绑定目标对象的更新 (E)
if (target != null)
{
target.SourcePropertyChanged(sender, args);
}
else
{
this.Disconnect();
}
}
}
由上述代码跟踪可以看到整个调用流程(A -> B -> C -> D):
BindingExpression.ConnectToSource()
-> WeakPropertyChangedListener.ctor()
-> WeakPropertyChangedListener.PropertyChangedCallback()
-> BindingExpression.SourcePropertyChanged()
在 BindingExpression 中连接数据源时,就判断其是否实现了 INotifyPropertyChanged 接口,如果实现了,则注册一个回调函数。
在数据源发生变化时,将触发这个回调函数,在这个函数中调用到 BindingExpression 的 SourcePropertyChanged() 函数去更新目标对象。
这样就实现了一个 source -> target 绑定的数据更新触发机制。
Tags:Silverlight 数据 绑定
编辑录入:爽爽 [复制链接] [打 印]更多精彩
赞助商链接