WPF里面是有多重绑定的,Silverlight里面没有,而且Silverlight5也不会有(SL5功能列表),网上有手工实现Silverlight多重绑定(MultiBinding)的,基本上源头是这篇文章。里面实现Silverlight多重绑定(MultiBinding)的基本是这么几个类:

IMultiValueConverter:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Globalization;
  12:  
namespace SLMultiBinding
  14: {
/// <summary>
/// see: http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx
/// </summary>
interface IMultiValueConverter
  19:   {   
object parameter, CultureInfo culture);
  21:  
object parameter, CultureInfo culture);
  23:     
  24:   }
  25: }

示例TitleConverter:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
  11:  
namespace SLMultiBinding
  13: {
class TitleConverter : IMultiValueConverter
  15:   {
#region IMultiValueConverter Members
  17:  
object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
  20:     {
string;
string;
  23:  
, surname, forename);
  25:     }
  26:  
value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
  29:     {
new NotImplementedException();
  31:     }
  32:  
#endregion
  34:   }
  35: }

BindingUtil.cs:

using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.ComponentModel;
using System.Reflection;
  15:  
namespace SLMultiBinding
  17: {
/// <summary>
/// Provides a mechanism for attaching a MultiBinding to an element
/// </summary>
class BindingUtil
  22:   {
#region DataContextPiggyBack attached property
  24:  
/// <summary>
/// DataContextPiggyBack Attached Dependency Property, used as a mechanism for exposing
/// DataContext changed events
/// </summary>
readonly DependencyProperty DataContextPiggyBackProperty =
typeof(BindingUtil),
new PropertyChangedCallback(OnDataContextPiggyBackChanged)));
  32:  
object GetDataContextPiggyBack(DependencyObject d)
  34:     {
object)d.GetValue(DataContextPiggyBackProperty);
  36:     }
  37:  
value)
  39:     {
value);
  41:     }
  42:  
/// <summary>
/// Handles changes to the DataContextPiggyBack property.
/// </summary>
void OnDataContextPiggyBackChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  47:     {
as FrameworkElement;
  49:  
// whenever the targeElement DataContext is changed, copy the updated property
// value to our MultiBinding.
  52:       MultiBinding relay = GetMultiBinding(targetElement);
  53:       relay.DataContext = targetElement.DataContext;
  54:     }
  55:  
#endregion
  57:  
#region MultiBinding attached property
  59:  
static MultiBinding GetMultiBinding(DependencyObject obj)
  61:     {
return (MultiBinding)obj.GetValue(MultiBindingProperty);
  63:     }
  64:  
value)
  66:     {
value);
  68:     }
  69:  
readonly DependencyProperty MultiBindingProperty =
,
null, OnMultiBindingChanged));
  73:  
readonly BindingFlags dpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
  75:  
/// <summary>
/// Invoked when the MultiBinding property is set on a framework element
/// </summary>
void OnMultiBindingChanged(DependencyObject depObj,
  80:       DependencyPropertyChangedEventArgs e)
  81:     {
as FrameworkElement;
  83:  
// bind the target elements DataContext, to our DataContextPiggyBack property
// this allows us to get property changed events when the targetElement
// DataContext changes
new Binding());
  88:  
  89:       MultiBinding relay = GetMultiBinding(targetElement);
  90:       relay.Initialise();
  91:  
// find the target dependency property
  93:       FieldInfo[] sourceFields = targetElement.GetType().GetFields(dpFlags);
  94:       FieldInfo targetDependencyPropertyField =
);
  96:       DependencyProperty targetDependencyProperty =
as DependencyProperty;
  98:  
// bind the ConvertedValue of our MultiBinding instance to the target property
// of our targetElement
);
 102:       binding.Source = relay;
 103:       targetElement.SetBinding(targetDependencyProperty, binding);
 104:     }
 105:  
#endregion
 107:  
 108:   }
 109: }

MultiBinding.cs

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Windows.Markup;
using System.ComponentModel;
using System.Collections.Generic;
using System.Globalization;
  17:  
namespace SLMultiBinding
  19: {
/// <summary>
/// Allows multiple bindings to a single property.
/// </summary>
)]
class MultiBinding : Panel, INotifyPropertyChanged
  25:   {
  26:  
#region ConvertedValue dependency property
  28:  
readonly DependencyProperty ConvertedValueProperty =
typeof(MultiBinding),
null, OnConvertedValue));
  32:  
/// <summary>
/// This dependency property is set to the resulting output of the
/// associated Converter.
/// </summary>
object ConvertedValue
  38:     {
object)GetValue(ConvertedValueProperty); }
value); }
  41:     }
  42:  
void OnConvertedValue(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
  44:     {
as MultiBinding;
);
  47:     }
  48:  
#endregion
  50:  
#region CLR properties
  52:  
/// <summary>
/// The target property on the element which this MultiBinding is assocaited with.
/// </summary>
string TargetProperty { get; set; }
  57:  
/// <summary>
/// The Converter which is invoked to compute the result of the multiple bindings
/// </summary>
public IMultiValueConverter Converter { get; set; }
  62:  
/// <summary>
/// The configuration parameter supplied to the converter
/// </summary>
object ConverterParameter { get; set; }
  67:  
/// <summary>
/// The bindings, the result of which are supplied to the converter.
/// </summary>
public ObservableCollection<Binding> Bindings { get; set; }
  72:  
#endregion
  74:  
public MultiBinding()
  76:     {
new ObservableCollection<Binding>();
  78:     }
  79:  
/// <summary>
/// Invoked when any of the BindingSlave's Value property changes.
/// </summary>
object sender, PropertyChangedEventArgs e)
  84:     {
  85:       UpdateConvertedValue();
  86:     }
  87:  
/// <summary>
/// Uses the Converter to update the ConvertedValue in order to reflect
/// the current state of the bindings.
/// </summary>
void UpdateConvertedValue()
  93:     {
object>();
in Children)
  96:       {
  97:         values.Add(slave.Value);
  98:       }
object), ConverterParameter,
 100:         CultureInfo.CurrentCulture);
 101:     }
 102:  
/// <summary>
/// Creates a BindingSlave for each Binding and binds the Value
/// accordingly.
/// </summary>
void Initialise()
 108:     {
in Bindings)
 110:       {
new BindingSlave();
 112:         slave.SetBinding(BindingSlave.ValueProperty, binding);
new PropertyChangedEventHandler(Slave_PropertyChanged);
 114:         Children.Add(slave);
 115:       }            
 116:     }
 117:     
#region INotifyPropertyChanged Members
 119:  
event PropertyChangedEventHandler PropertyChanged;
 121:  
string name)
 123:     {
null)
 125:       {
new PropertyChangedEventArgs(name));
 127:       }
 128:     }
 129:  
#endregion
 131:   }
 132:  
/// <summary>
/// A simple element with a single Value property, used as a 'slave'
/// for a Binding.
/// </summary>
class BindingSlave : FrameworkElement, INotifyPropertyChanged
 138:   {
#region Value
 140:  
readonly DependencyProperty ValueProperty =
typeof(BindingSlave),
null, OnValueChanged));
 144:  
object Value
 146:     {
object)GetValue(ValueProperty); }
value); }
 149:     }
 150:  
void OnValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
 152:     {
as BindingSlave;
);
 155:     }
 156:  
#endregion
 158:  
#region INotifyPropertyChanged Members
 160:  
event PropertyChangedEventHandler PropertyChanged;
 162:  
string name)
 164:     {
null)
 166:       {
new PropertyChangedEventArgs(name));
 168:       }
 169:     }
 170:  
#endregion
 172:  
 173:   }
 174: }

代码自己看,不解释。

最后使用起来就是这样,可以绑定N个域,逻辑在Converter里面实现:

>
>
>
/>                            
/>
>
>
>

 

是否有必要用多重绑定?

一般你要用到多重绑定的情况是:这个域需要根据多个域的值来计算(包含一定的业务逻辑),也就是多重绑定吗?就需要把业务逻辑放到ValueConverter里面吗?不一定。ValueConverter也不太合适。实现方法很多。如果我们用MVVM模式来开发Silverlight的话,那M-V-VM里面就有个M:Model,可以把这个需要根据多个域的值来计算(包含一定的业务逻辑)的域放到呈现模型里面,这样就能很方便的绑定到这个新的Field了。如果你的MVVM模式没有M,只有VM,这就是设计的问题了。一般来说,有很多业务逻辑需要放到这个M里面,包括本文说的复合域的情形,还有数据验证(Validation)的情形,都可能需要用到M。这个M设计的好,绑定才实现的完美。

反过来,本文所实现的方法是用多重绑定加ValueConverter,而把业务逻辑放到ValueConverter里面不太合适。Converter应该是薄薄的,也就是数据转换为适合显示的格式,比如bool转换为Visibility…..

 

总结

遇到问题,多思考,不要打破系统架构和层次设计,不要胡乱添加业务逻辑,这很容易导致系统的“破窗效应”。动手写代码之前写思考,思考的代价远比写代码、测试、重构的代价小!

WPF里面是有多重绑定的,Silverlight里面没有,而且Silverlight5也不会有(SL5功能列表),网上有手工实现Silverlight多重绑定(MultiBinding)的,基本上源头是这篇文章。里面实现Silverlight多重绑定(MultiBinding)的基本是这么几个类:

IMultiValueConverter:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Globalization;
  12:  
namespace SLMultiBinding
  14: {
/// <summary>
/// see: http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx
/// </summary>
interface IMultiValueConverter
  19:   {   
object parameter, CultureInfo culture);
  21:  
object parameter, CultureInfo culture);
  23:     
  24:   }
  25: }

示例TitleConverter:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
  11:  
namespace SLMultiBinding
  13: {
class TitleConverter : IMultiValueConverter
  15:   {
#region IMultiValueConverter Members
  17:  
object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
  20:     {
string;
string;
  23:  
, surname, forename);
  25:     }
  26:  
value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
  29:     {
new NotImplementedException();
  31:     }
  32:  
#endregion
  34:   }
  35: }

BindingUtil.cs:

using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.ComponentModel;
using System.Reflection;
  15:  
namespace SLMultiBinding
  17: {
/// <summary>
/// Provides a mechanism for attaching a MultiBinding to an element
/// </summary>
class BindingUtil
  22:   {
#region DataContextPiggyBack attached property
  24:  
/// <summary>
/// DataContextPiggyBack Attached Dependency Property, used as a mechanism for exposing
/// DataContext changed events
/// </summary>
readonly DependencyProperty DataContextPiggyBackProperty =
typeof(BindingUtil),
new PropertyChangedCallback(OnDataContextPiggyBackChanged)));
  32:  
object GetDataContextPiggyBack(DependencyObject d)
  34:     {
object)d.GetValue(DataContextPiggyBackProperty);
  36:     }
  37:  
value)
  39:     {
value);
  41:     }
  42:  
/// <summary>
/// Handles changes to the DataContextPiggyBack property.
/// </summary>
void OnDataContextPiggyBackChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  47:     {
as FrameworkElement;
  49:  
// whenever the targeElement DataContext is changed, copy the updated property
// value to our MultiBinding.
  52:       MultiBinding relay = GetMultiBinding(targetElement);
  53:       relay.DataContext = targetElement.DataContext;
  54:     }
  55:  
#endregion
  57:  
#region MultiBinding attached property
  59:  
static MultiBinding GetMultiBinding(DependencyObject obj)
  61:     {
return (MultiBinding)obj.GetValue(MultiBindingProperty);
  63:     }
  64:  
value)
  66:     {
value);
  68:     }
  69:  
readonly DependencyProperty MultiBindingProperty =
,
null, OnMultiBindingChanged));
  73:  
readonly BindingFlags dpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
  75:  
/// <summary>
/// Invoked when the MultiBinding property is set on a framework element
/// </summary>
void OnMultiBindingChanged(DependencyObject depObj,
  80:       DependencyPropertyChangedEventArgs e)
  81:     {
as FrameworkElement;
  83:  
// bind the target elements DataContext, to our DataContextPiggyBack property
// this allows us to get property changed events when the targetElement
// DataContext changes
new Binding());
  88:  
  89:       MultiBinding relay = GetMultiBinding(targetElement);
  90:       relay.Initialise();
  91:  
// find the target dependency property
  93:       FieldInfo[] sourceFields = targetElement.GetType().GetFields(dpFlags);
  94:       FieldInfo targetDependencyPropertyField =
);
  96:       DependencyProperty targetDependencyProperty =
as DependencyProperty;
  98:  
// bind the ConvertedValue of our MultiBinding instance to the target property
// of our targetElement
);
 102:       binding.Source = relay;
 103:       targetElement.SetBinding(targetDependencyProperty, binding);
 104:     }
 105:  
#endregion
 107:  
 108:   }
 109: }

MultiBinding.cs

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Windows.Markup;
using System.ComponentModel;
using System.Collections.Generic;
using System.Globalization;
  17:  
namespace SLMultiBinding
  19: {
/// <summary>
/// Allows multiple bindings to a single property.
/// </summary>
)]
class MultiBinding : Panel, INotifyPropertyChanged
  25:   {
  26:  
#region ConvertedValue dependency property
  28:  
readonly DependencyProperty ConvertedValueProperty =
typeof(MultiBinding),
null, OnConvertedValue));
  32:  
/// <summary>
/// This dependency property is set to the resulting output of the
/// associated Converter.
/// </summary>
object ConvertedValue
  38:     {
object)GetValue(ConvertedValueProperty); }
value); }
  41:     }
  42:  
void OnConvertedValue(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
  44:     {
as MultiBinding;
);
  47:     }
  48:  
#endregion
  50:  
#region CLR properties
  52:  
/// <summary>
/// The target property on the element which this MultiBinding is assocaited with.
/// </summary>
string TargetProperty { get; set; }
  57:  
/// <summary>
/// The Converter which is invoked to compute the result of the multiple bindings
/// </summary>
public IMultiValueConverter Converter { get; set; }
  62:  
/// <summary>
/// The configuration parameter supplied to the converter
/// </summary>
object ConverterParameter { get; set; }
  67:  
/// <summary>
/// The bindings, the result of which are supplied to the converter.
/// </summary>
public ObservableCollection<Binding> Bindings { get; set; }
  72:  
#endregion
  74:  
public MultiBinding()
  76:     {
new ObservableCollection<Binding>();
  78:     }
  79:  
/// <summary>
/// Invoked when any of the BindingSlave's Value property changes.
/// </summary>
object sender, PropertyChangedEventArgs e)
  84:     {
  85:       UpdateConvertedValue();
  86:     }
  87:  
/// <summary>
/// Uses the Converter to update the ConvertedValue in order to reflect
/// the current state of the bindings.
/// </summary>
void UpdateConvertedValue()
  93:     {
object>();
in Children)
  96:       {
  97:         values.Add(slave.Value);
  98:       }
object), ConverterParameter,
 100:         CultureInfo.CurrentCulture);
 101:     }
 102:  
/// <summary>
/// Creates a BindingSlave for each Binding and binds the Value
/// accordingly.
/// </summary>
void Initialise()
 108:     {
in Bindings)
 110:       {
new BindingSlave();
 112:         slave.SetBinding(BindingSlave.ValueProperty, binding);
new PropertyChangedEventHandler(Slave_PropertyChanged);
 114:         Children.Add(slave);
 115:       }            
 116:     }
 117:     
#region INotifyPropertyChanged Members
 119:  
event PropertyChangedEventHandler PropertyChanged;
 121:  
string name)
 123:     {
null)
 125:       {
new PropertyChangedEventArgs(name));
 127:       }
 128:     }
 129:  
#endregion
 131:   }
 132:  
/// <summary>
/// A simple element with a single Value property, used as a 'slave'
/// for a Binding.
/// </summary>
class BindingSlave : FrameworkElement, INotifyPropertyChanged
 138:   {
#region Value
 140:  
readonly DependencyProperty ValueProperty =
typeof(BindingSlave),
null, OnValueChanged));
 144:  
object Value
 146:     {
object)GetValue(ValueProperty); }
value); }
 149:     }
 150:  
void OnValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
 152:     {
as BindingSlave;
);
 155:     }
 156:  
#endregion
 158:  
#region INotifyPropertyChanged Members
 160:  
event PropertyChangedEventHandler PropertyChanged;
 162:  
string name)
 164:     {
null)
 166:       {
new PropertyChangedEventArgs(name));
 168:       }
 169:     }
 170:  
#endregion
 172:  
 173:   }
 174: }

代码自己看,不解释。

最后使用起来就是这样,可以绑定N个域,逻辑在Converter里面实现:

>
>
>
/>                            
/>
>
>
>

 

是否有必要用多重绑定?

一般你要用到多重绑定的情况是:这个域需要根据多个域的值来计算(包含一定的业务逻辑),也就是多重绑定吗?就需要把业务逻辑放到ValueConverter里面吗?不一定。ValueConverter也不太合适。实现方法很多。如果我们用MVVM模式来开发Silverlight的话,那M-V-VM里面就有个M:Model,可以把这个需要根据多个域的值来计算(包含一定的业务逻辑)的域放到呈现模型里面,这样就能很方便的绑定到这个新的Field了。如果你的MVVM模式没有M,只有VM,这就是设计的问题了。一般来说,有很多业务逻辑需要放到这个M里面,包括本文说的复合域的情形,还有数据验证(Validation)的情形,都可能需要用到M。这个M设计的好,绑定才实现的完美。

反过来,本文所实现的方法是用多重绑定加ValueConverter,而把业务逻辑放到ValueConverter里面不太合适。Converter应该是薄薄的,也就是数据转换为适合显示的格式,比如bool转换为Visibility…..

 

总结

遇到问题,多思考,不要打破系统架构和层次设计,不要胡乱添加业务逻辑,这很容易导致系统的“破窗效应”。动手写代码之前写思考,思考的代价远比写代码、测试、重构的代价小!

相关文章:

  • 2021-11-15
  • 2021-08-29
  • 2021-05-29
  • 2021-06-12
  • 2021-08-17
  • 2022-01-12
  • 2022-12-23
  • 2021-12-01
猜你喜欢
  • 2022-12-23
  • 2021-09-16
  • 2022-01-06
  • 2021-07-18
  • 2021-07-17
  • 2021-12-29
  • 2021-10-24
相关资源
相似解决方案