Silverlight 多重绑定
在 Silverlight 中执行多重绑定。
引言
Multi-binding 是 WPF 中一个非常重要/强大的功能。当您需要一个依赖于多个输入的单个结果时,它会派上用场。该结果被绑定到一个特殊的逻辑,该逻辑通常在“MultiValueConverter”中实现。因此,任何输入的更改都应该体现在输出结果中。在 Silverlight 中,此功能缺失!
在本文中,我将展示我提出的三种解决方案,从“快速简单”到“类似 WPF”。
背景
在将 WPF 应用程序移植到 Silverlight 时,我遇到了这个“缺失的功能”问题(Silverlight 程序员都熟悉......),因为 Silverlight 只是 WPF 的一个子集。像任何其他软件开发人员一样,我做的第一件事就是谷歌搜索......
在研究了建议的方法后,我得出了这些结论
- 其中一些解决方案对我来说太聪明/太复杂了。
- 解决方案不如我希望的那样健壮。
- 不支持
DependencyObject
多重绑定(仅支持FrameworkElement
) Binding
中缺乏RelativeSource
。- 实现方式与基础 WPF 方式相去甚远(有时很笨拙),这使得移植到 Silverlight 非常混乱。
例如
所以,我想我应该试试,用自己的方式来解决它……
一些“提示”引导我找到了我提出的解决方案
- 微软在所有 Silverlight 版本中一直排除此功能,这可能有两个原因
- 微软对其开发者的需求漠不关心……
- 微软“认为”使用标准可用工具可以相对简单地实现此功能。
- 思考多重绑定的核心功能应该会“触发”某人的灵感
- Silverlight 5.0 中引入的新功能
RelativeSource
与AncestorType
MarkupExtension
(简而言之,XAML 中“自动”实例化的对象,它有一个返回值)。
我发现,对我来说,采用一种方法更有成效:对于我遇到的任何算法问题,存在一个简单、优雅且快速的解决方案,我只需要找到它……所以我认为第二个原因是“真正”的原因。
哪个标准 Silverlight 功能具有“监视和响应属性更改”的“能力”?是的,DependencyProperty
!以及它的变体 AttachedProperty
。
考虑到这些“提示”,我提出了三个解决方案
1. 快速简单
如果您只考虑多重绑定的**功能**,您可以使用此方法来实现。
- 构造一个“专用”类,其中包含与您的输入绑定匹配的附加属性(您甚至可以为这些属性赋予有意义的名称)。
- 将任何这些附加属性的更改中继到一个单一的“Any...Changed”子例程,您将在其中执行两项操作
- 执行您想要的多重绑定逻辑。
- 更新“结果属性”。
2. 部分优雅
作为最佳实践,最好将多重绑定分离到不同的任务中
- 多输入绑定通用“引擎”(适用于 XAML)
- 多值转一逻辑(通常作为
IMultiValueConverter
的实现)。
考虑到这一点,我们可以创建一个基于“AttachedProperty
”的类(称为 MultiBindingUtil
),它将拥有任意数量(为简单起见,示例只有三个预定义的)的附加属性用于输入绑定(称为 Binding1
到 Binding
(n))、用于Converter
的AttachedProperty
,最后,用于结果的AttachedProperty
(在示例中称为 MBResult
)。现在,我们在 XAML 中要做的就是将输入绑定的附加属性绑定到我们想要的任何内容,提供一个转换器,并将 MBResult
绑定回我们想要由多重绑定更改的属性。在多重绑定“引擎”内部,就像之前的示例一样,我们将把任何输入绑定的更改中继到一个单一的“Any...Changed”子例程;只有在那里,我们将使用该类的绑定作为输入,用提供的转换器的**转换后**结果更新 MBResult
属性。一切都很好,除了有一个有据可查/已报告的设计时错误发生在 Silverlight 中,当将元素的属性绑定到其自身的附加属性时。此错误以一种非常微妙且具有信息量的方式表现出来。
我找不到任何解决此“本地路径”问题的方法。如果您知道如何修复它,请分享。
3. 类似 WPF
此解决方案提供了与 WPF 的多重绑定非常相似的 XAML 外观,这在移植时可能很有帮助。此外,它还利用了新的 Silverlight 5.0 MarkupExtension
支持以及前一个解决方案的 MultiBindingUtil
类。在这里,我们使用一种新的 MarkupExtension
类型类,称为“MyMultiBinding(Extension)
”,它有一个 Content
属性 List<Binding>
,名为(奇怪地)“Bindings
”,以及一个名为(奇怪地)“Converter
”的 IMultiValueConverter
类型属性。
使用此属性作为内容是通过此属性完成的
[ContentProperty("Bindings")]
实际的技巧是在 MarkupExtension
最重要的重写方法中完成的
override object ProvideValue(IServiceProvider serviceProvider)
我们将执行以下操作
- 获取“目标”
FrameworkElement
- 将
MultiBindingUtil
的绑定属性“附加”到它,并将其值与“我们的”Bindings
列表匹配。 - 将
Converter
属性“附加”到它,值为“我们的”Converter
。 - 将返回值设置为
Binding
(而不是简单值)。此绑定将绑定到MBResult MultiBindingUtil
的附加属性(!)。
IProvideValueTarget pvt =
serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
DependencyObject TargetObject = pvt.TargetObject as DependencyObject ;
for (int i = 0; i < _Bindings.Count ; i++)
{
string sDpName="Binding"+(i+1).ToString()+"Property";
//using reflection to get the DependencyProperty from type/string
FieldInfo fi = typeof(MultiBindingUtil).GetField(sDpName);
if (fi==null)
{
throw new IndexOutOfRangeException(
"MultiBindingUtil Max number of binding(3) exceeded");
}
DependencyProperty dp = (DependencyProperty)fi.GetValue(null);
BindingExpressionBase beb =
BindingOperations.SetBinding(TargetObject, dp, _Bindings[i]);
}
TargetObject.SetValue(MultiBindingUtil.ConverterProperty, this.Converter);
Binding b = new Binding();
b.Source = TargetObject;
b.Path =new PropertyPath(MultiBindingUtil.MBResultProperty);
return b;
备注
- 解决方案 1 和 2 应与 Silverlight 4.0 兼容。
- 解决方案 3 对 Style Setter 多重绑定不起作用。
快速简单解决方案(#1)可以在那里提供帮助,就像这样
<Setter Property="mb:MultiBindingReplacement.EpisodeLength"
Value="{Binding Path=RangeMilSec}" />
<Setter Property="mb:MultiBindingReplacement.Scale"
Value="{Binding Path=VM.ScaleTransScaleX}" />
其中 MultiBindingReplacement
是专用类的名称,而 EpisodeLength
和 Scale
是绑定的专用 DependencyProperty
。
在类中,我执行“转换逻辑”并将结果设置到 Style 的目标对象的特殊属性中(如解决方案 3 中所述)。