• [Silverlight入门系列]多重绑定MultiBinding有必要吗?


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

    IMultiValueConverter:

       1: using System;
       2: using System.Net;
       3: using System.Windows;
       4: using System.Windows.Controls;
       5: using System.Windows.Documents;
       6: using System.Windows.Ink;
       7: using System.Windows.Input;
       8: using System.Windows.Media;
       9: using System.Windows.Media.Animation;
      10: using System.Windows.Shapes;
      11: using System.Globalization;
      12:  
      13: namespace SLMultiBinding
      14: {
      15:   /// <summary>
      16:   /// see: http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx
      17:   /// </summary>
      18:   public interface IMultiValueConverter
      19:   {   
      20:       object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
      21:  
      22:       object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture);
      23:     
      24:   }
      25: }

    示例TitleConverter:

       1: using System;
       2: using System.Net;
       3: using System.Windows;
       4: using System.Windows.Controls;
       5: using System.Windows.Documents;
       6: using System.Windows.Ink;
       7: using System.Windows.Input;
       8: using System.Windows.Media;
       9: using System.Windows.Media.Animation;
      10: using System.Windows.Shapes;
      11:  
      12: namespace SLMultiBinding
      13: {
      14:   public class TitleConverter : IMultiValueConverter
      15:   {
      16:     #region IMultiValueConverter Members
      17:  
      18:     public object Convert(object[] values, Type targetType,
      19:       object parameter, System.Globalization.CultureInfo culture)
      20:     {
      21:       string forename = values[0] as string;
      22:       string surname = values[1] as string;
      23:  
      24:       return string.Format("{0}, {1}", surname, forename);
      25:     }
      26:  
      27:     public object[] ConvertBack(object value, Type[] targetTypes,
      28:       object parameter, System.Globalization.CultureInfo culture)
      29:     {
      30:       throw new NotImplementedException();
      31:     }
      32:  
      33:     #endregion
      34:   }
      35: }

    BindingUtil.cs:

       1: using System;
       2: using System.Net;
       3: using System.Linq;
       4: using System.Windows;
       5: using System.Windows.Controls;
       6: using System.Windows.Documents;
       7: using System.Windows.Ink;
       8: using System.Windows.Input;
       9: using System.Windows.Media;
      10: using System.Windows.Media.Animation;
      11: using System.Windows.Shapes;
      12: using System.Windows.Data;
      13: using System.ComponentModel;
      14: using System.Reflection;
      15:  
      16: namespace SLMultiBinding
      17: {
      18:   /// <summary>
      19:   /// Provides a mechanism for attaching a MultiBinding to an element
      20:   /// </summary>
      21:   public class BindingUtil
      22:   {
      23:     #region DataContextPiggyBack attached property
      24:  
      25:     /// <summary>
      26:     /// DataContextPiggyBack Attached Dependency Property, used as a mechanism for exposing
      27:     /// DataContext changed events
      28:     /// </summary>
      29:     public static readonly DependencyProperty DataContextPiggyBackProperty =
      30:         DependencyProperty.RegisterAttached("DataContextPiggyBack", typeof(object), typeof(BindingUtil),
      31:             new PropertyMetadata(null, new PropertyChangedCallback(OnDataContextPiggyBackChanged)));
      32:  
      33:     public static object GetDataContextPiggyBack(DependencyObject d)
      34:     {
      35:       return (object)d.GetValue(DataContextPiggyBackProperty);
      36:     }
      37:  
      38:     public static void SetDataContextPiggyBack(DependencyObject d, object value)
      39:     {
      40:       d.SetValue(DataContextPiggyBackProperty, value);
      41:     }
      42:  
      43:     /// <summary>
      44:     /// Handles changes to the DataContextPiggyBack property.
      45:     /// </summary>
      46:     private static void OnDataContextPiggyBackChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      47:     {
      48:       FrameworkElement targetElement = d as FrameworkElement;
      49:  
      50:       // whenever the targeElement DataContext is changed, copy the updated property
      51:       // value to our MultiBinding.
      52:       MultiBinding relay = GetMultiBinding(targetElement);
      53:       relay.DataContext = targetElement.DataContext;
      54:     }
      55:  
      56:     #endregion
      57:  
      58:     #region MultiBinding attached property
      59:  
      60:     public static MultiBinding GetMultiBinding(DependencyObject obj)
      61:     {
      62:       return (MultiBinding)obj.GetValue(MultiBindingProperty);
      63:     }
      64:  
      65:     public static void SetMultiBinding(DependencyObject obj, MultiBinding value)
      66:     {
      67:       obj.SetValue(MultiBindingProperty, value);
      68:     }
      69:  
      70:     public static readonly DependencyProperty MultiBindingProperty =
      71:         DependencyProperty.RegisterAttached("MultiBinding",
      72:         typeof(MultiBinding), typeof(BindingUtil), new PropertyMetadata(null, OnMultiBindingChanged));
      73:  
      74:     private static readonly BindingFlags dpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
      75:  
      76:     /// <summary>
      77:     /// Invoked when the MultiBinding property is set on a framework element
      78:     /// </summary>
      79:     private static void OnMultiBindingChanged(DependencyObject depObj,
      80:       DependencyPropertyChangedEventArgs e)
      81:     {
      82:       FrameworkElement targetElement = depObj as FrameworkElement;
      83:  
      84:       // bind the target elements DataContext, to our DataContextPiggyBack property
      85:       // this allows us to get property changed events when the targetElement
      86:       // DataContext changes
      87:       targetElement.SetBinding(BindingUtil.DataContextPiggyBackProperty, new Binding());
      88:  
      89:       MultiBinding relay = GetMultiBinding(targetElement);
      90:       relay.Initialise();
      91:  
      92:       // find the target dependency property
      93:       FieldInfo[] sourceFields = targetElement.GetType().GetFields(dpFlags);
      94:       FieldInfo targetDependencyPropertyField =
      95:           sourceFields.First(i => i.Name == relay.TargetProperty + "Property");
      96:       DependencyProperty targetDependencyProperty =
      97:           targetDependencyPropertyField.GetValue(null) as DependencyProperty;
      98:  
      99:       // bind the ConvertedValue of our MultiBinding instance to the target property
     100:       // of our targetElement
     101:       Binding binding = new Binding("ConvertedValue");
     102:       binding.Source = relay;
     103:       targetElement.SetBinding(targetDependencyProperty, binding);
     104:     }
     105:  
     106:     #endregion
     107:  
     108:   }
     109: }

    MultiBinding.cs

       1: using System;
       2: using System.Net;
       3: using System.Windows;
       4: using System.Windows.Controls;
       5: using System.Windows.Documents;
       6: using System.Windows.Ink;
       7: using System.Windows.Input;
       8: using System.Windows.Media;
       9: using System.Windows.Media.Animation;
      10: using System.Windows.Shapes;
      11: using System.Windows.Data;
      12: using System.Collections.ObjectModel;
      13: using System.Windows.Markup;
      14: using System.ComponentModel;
      15: using System.Collections.Generic;
      16: using System.Globalization;
      17:  
      18: namespace SLMultiBinding
      19: {
      20:   /// <summary>
      21:   /// Allows multiple bindings to a single property.
      22:   /// </summary>
      23:   [ContentProperty("Bindings")]
      24:   public class MultiBinding : Panel, INotifyPropertyChanged
      25:   {
      26:  
      27:     #region ConvertedValue dependency property
      28:  
      29:     public static readonly DependencyProperty ConvertedValueProperty =
      30:         DependencyProperty.Register("ConvertedValue", typeof(object), typeof(MultiBinding),
      31:             new PropertyMetadata(null, OnConvertedValue));
      32:  
      33:     /// <summary>
      34:     /// This dependency property is set to the resulting output of the
      35:     /// associated Converter.
      36:     /// </summary>
      37:     public object ConvertedValue
      38:     {
      39:       get { return (object)GetValue(ConvertedValueProperty); }
      40:       set { SetValue(ConvertedValueProperty, value); }
      41:     }
      42:  
      43:     private static void OnConvertedValue(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
      44:     {
      45:       MultiBinding relay = depObj as MultiBinding;
      46:       relay.OnPropertyChanged("ConvertedValue");
      47:     }
      48:  
      49:     #endregion
      50:  
      51:     #region CLR properties
      52:  
      53:     /// <summary>
      54:     /// The target property on the element which this MultiBinding is assocaited with.
      55:     /// </summary>
      56:     public string TargetProperty { get; set; }
      57:  
      58:     /// <summary>
      59:     /// The Converter which is invoked to compute the result of the multiple bindings
      60:     /// </summary>
      61:     public IMultiValueConverter Converter { get; set; }
      62:  
      63:     /// <summary>
      64:     /// The configuration parameter supplied to the converter
      65:     /// </summary>
      66:     public object ConverterParameter { get; set; }
      67:  
      68:     /// <summary>
      69:     /// The bindings, the result of which are supplied to the converter.
      70:     /// </summary>
      71:     public ObservableCollection<Binding> Bindings { get; set; }
      72:  
      73:     #endregion
      74:  
      75:     public MultiBinding()
      76:     {
      77:       Bindings = new ObservableCollection<Binding>();
      78:     }
      79:  
      80:     /// <summary>
      81:     /// Invoked when any of the BindingSlave's Value property changes.
      82:     /// </summary>
      83:     private void Slave_PropertyChanged(object sender, PropertyChangedEventArgs e)
      84:     {
      85:       UpdateConvertedValue();
      86:     }
      87:  
      88:     /// <summary>
      89:     /// Uses the Converter to update the ConvertedValue in order to reflect
      90:     /// the current state of the bindings.
      91:     /// </summary>
      92:     private void UpdateConvertedValue()
      93:     {
      94:       List<object> values = new List<object>();
      95:       foreach (BindingSlave slave in Children)
      96:       {
      97:         values.Add(slave.Value);
      98:       }
      99:       ConvertedValue = Converter.Convert(values.ToArray(), typeof(object), ConverterParameter,
     100:         CultureInfo.CurrentCulture);
     101:     }
     102:  
     103:     /// <summary>
     104:     /// Creates a BindingSlave for each Binding and binds the Value
     105:     /// accordingly.
     106:     /// </summary>
     107:     internal void Initialise()
     108:     {
     109:       foreach (Binding binding in Bindings)
     110:       {
     111:         BindingSlave slave = new BindingSlave();
     112:         slave.SetBinding(BindingSlave.ValueProperty, binding);
     113:         slave.PropertyChanged += new PropertyChangedEventHandler(Slave_PropertyChanged);
     114:         Children.Add(slave);
     115:       }            
     116:     }
     117:     
     118:     #region INotifyPropertyChanged Members
     119:  
     120:     public event PropertyChangedEventHandler PropertyChanged;
     121:  
     122:     protected void OnPropertyChanged(string name)
     123:     {
     124:       if (PropertyChanged != null)
     125:       {
     126:         PropertyChanged(this, new PropertyChangedEventArgs(name));
     127:       }
     128:     }
     129:  
     130:     #endregion
     131:   }
     132:  
     133:   /// <summary>
     134:   /// A simple element with a single Value property, used as a 'slave'
     135:   /// for a Binding.
     136:   /// </summary>
     137:   public class BindingSlave : FrameworkElement, INotifyPropertyChanged
     138:   {
     139:     #region Value
     140:  
     141:     public static readonly DependencyProperty ValueProperty =
     142:         DependencyProperty.Register("Value", typeof(object), typeof(BindingSlave),
     143:             new PropertyMetadata(null, OnValueChanged));
     144:  
     145:     public object Value
     146:     {
     147:       get { return (object)GetValue(ValueProperty); }
     148:       set { SetValue(ValueProperty, value); }
     149:     }
     150:  
     151:     private static void OnValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
     152:     {
     153:       BindingSlave slave = depObj as BindingSlave;
     154:       slave.OnPropertyChanged("Value");
     155:     }
     156:  
     157:     #endregion
     158:  
     159:     #region INotifyPropertyChanged Members
     160:  
     161:     public event PropertyChangedEventHandler PropertyChanged;
     162:  
     163:     protected void OnPropertyChanged(string name)
     164:     {
     165:       if (PropertyChanged != null)
     166:       {
     167:         PropertyChanged(this, new PropertyChangedEventArgs(name));
     168:       }
     169:     }
     170:  
     171:     #endregion
     172:  
     173:   }
     174: }

    代码自己看,不解释。

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

       1: <TextBlock Foreground="White" FontSize="13">
       2:     <local:BindingUtil.MultiBinding>
       3:         <local:MultiBinding TargetProperty="Text" Converter="{StaticResource TitleConverter}">
       4:             <Binding Path="Surname"/>                            
       5:             <Binding Path="Forename"/>
       6:         </local:MultiBinding>
       7:     </local:BindingUtil.MultiBinding>
       8: </TextBlock>

    是否有必要用多重绑定?

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

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

    总结

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

  • 相关阅读:
    Ajax学习感悟
    C#自定义控件designmodel的判断
    Asp.net+Flash多文件上传
    .net下帮助文件(sandcastle)
    水晶报表小例用于学习
    WinAPI转C#利器
    利用HttpHandler和Cache统计点击量
    关于C#调用API的理解(汇多考勤机HD4K)
    ubuntu 12.04 配置PHP開發環境遇到的問題
    windows 7 系統在VMWear workstation 9上安裝Mac OS X 10.7
  • 原文地址:https://www.cnblogs.com/lovenan/p/2589704.html
Copyright © 2020-2023  润新知