【WP8】MultiBinding
WP中系统没有像WPF中直接支持MultiBinding,可以通过以下代码实现
五个类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class BindingCollection : Collection<BindingBase> { // Fields private readonly BindingCollectionChangedCallback _collectionChangedCallback; // Methods //internal BindingCollection(BindingCollectionChangedCallback callback) //{ // _collectionChangedCallback = callback; //} protected override void ClearItems() { base.ClearItems(); OnBindingCollectionChanged(); } protected override void InsertItem(int index, BindingBase item) { if (item == null) { throw new ArgumentNullException("item"); } ValidateItem(item); base.InsertItem(index, item); OnBindingCollectionChanged(); } private void OnBindingCollectionChanged() { if (_collectionChangedCallback != null) { _collectionChangedCallback(); } } protected override void RemoveItem(int index) { base.RemoveItem(index); OnBindingCollectionChanged(); } protected override void SetItem(int index, BindingBase item) { if (item == null) { throw new ArgumentNullException("item"); } ValidateItem(item); base.SetItem(index, item); OnBindingCollectionChanged(); } private static void ValidateItem(BindingBase binding) { if (!(binding is Binding)) { throw new NotSupportedException("BindingCollectionContainsNonBinding"); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// A simple element with a single Value property, used as a 'slave' /// for a Binding. /// </summary> public class BindingSlave : FrameworkElement, INotifyPropertyChanged { #region Value public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(BindingSlave), new PropertyMetadata(null, OnValueChanged)); public object Value { get { return GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } private static void OnValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { BindingSlave slave = depObj as BindingSlave; Debug.Assert(slave != null); slave.OnPropertyChanged("Value"); } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Provides a mechanism for attaching a MultiBinding to an element /// </summary> public class BindingUtil { #region DataContextPiggyBack attached property /// <summary> /// DataContextPiggyBack Attached Dependency Property, used as a mechanism for exposing /// DataContext changed events /// </summary> public static readonly DependencyProperty DataContextPiggyBackProperty = DependencyProperty.RegisterAttached("DataContextPiggyBack", typeof(object), typeof(BindingUtil), new PropertyMetadata(null, new PropertyChangedCallback(OnDataContextPiggyBackChanged))); public static object GetDataContextPiggyBack(DependencyObject d) { return (object)d.GetValue(DataContextPiggyBackProperty); } public static void SetDataContextPiggyBack(DependencyObject d, object value) { d.SetValue(DataContextPiggyBackProperty, value); } /// <summary> /// Handles changes to the DataContextPiggyBack property. /// </summary> private static void OnDataContextPiggyBackChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { FrameworkElement targetElement = d as FrameworkElement; // whenever the targeElement DataContext is changed, copy the updated property // value to our MultiBinding. MultiBindings relay = GetMultiBindings(targetElement); relay.SetDataContext(targetElement.DataContext); } #endregion #region MultiBindings attached property public static MultiBindings GetMultiBindings(DependencyObject obj) { return (MultiBindings)obj.GetValue(MultiBindingsProperty); } public static void SetMultiBindings(DependencyObject obj, MultiBindings value) { obj.SetValue(MultiBindingsProperty, value); } public static readonly DependencyProperty MultiBindingsProperty = DependencyProperty.RegisterAttached("MultiBindings", typeof(MultiBindings), typeof(BindingUtil), new PropertyMetadata(null, OnMultiBindingsChanged)); /// <summary> /// Invoked when the MultiBinding property is set on a framework element /// </summary> private static void OnMultiBindingsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { FrameworkElement targetElement = depObj as FrameworkElement; // bind the target elements DataContext, to our DataContextPiggyBack property // this allows us to get property changed events when the targetElement // DataContext changes targetElement.SetBinding(DataContextPiggyBackProperty, new Binding()); MultiBindings bindings = GetMultiBindings(targetElement); bindings.Initialize(targetElement); } #endregion }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// see: http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx /// </summary> public interface IMultiValueConverter { object Convert(object[] values, Type targetType, object parameter, CultureInfo culture); object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture); }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Allows multiple bindings to a single property. /// </summary> [ContentProperty("Bindings")] public class MultiBinding : Panel, INotifyPropertyChanged { #region ConvertedValue dependency property public static readonly DependencyProperty ConvertedValueProperty = DependencyProperty.Register("ConvertedValue", typeof(object), typeof(MultiBinding), new PropertyMetadata(null, OnConvertedValue)); /// <summary> /// This dependency property is set to the resulting output of the /// associated Converter. /// </summary> public object ConvertedValue { get { return GetValue(ConvertedValueProperty); } set { SetValue(ConvertedValueProperty, value); } } private static void OnConvertedValue(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { MultiBinding relay = depObj as MultiBinding; Debug.Assert(relay != null); relay.OnPropertyChanged("ConvertedValue"); } #endregion #region CLR properties /// <summary> /// The target property on the element which this MultiBinding is assocaited with. /// </summary> public string TargetProperty { get; set; } /// <summary> /// The Converter which is invoked to compute the result of the multiple bindings /// </summary> public IMultiValueConverter Converter { get; set; } /// <summary> /// The configuration parameter supplied to the converter /// </summary> public object ConverterParameter { get; set; } /// <summary> /// The bindings, the result of which are supplied to the converter. /// </summary> public BindingCollection Bindings { get; set; } #endregion public MultiBinding() { Bindings = new BindingCollection(); } /// <summary> /// Invoked when any of the BindingSlave's Value property changes. /// </summary> private void SlavePropertyChanged(object sender, PropertyChangedEventArgs e) { UpdateConvertedValue(); } /// <summary> /// Uses the Converter to update the ConvertedValue in order to reflect /// the current state of the bindings. /// </summary> private void UpdateConvertedValue() { List<object> values = new List<object>(); foreach (BindingSlave slave in Children) { values.Add(slave.Value); } ConvertedValue = Converter.Convert(values.ToArray(), typeof(object), ConverterParameter, CultureInfo.CurrentCulture); } /// <summary> /// Creates a BindingSlave for each Binding and binds the Value /// accordingly. /// </summary> internal void Initialise() { Children.Clear(); foreach (Binding binding in Bindings) { BindingSlave slave = new BindingSlave(); slave.SetBinding(BindingSlave.ValueProperty, binding); slave.PropertyChanged += SlavePropertyChanged; Children.Add(slave); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[ContentProperty("Bindings")] public class MultiBindings : FrameworkElement { private FrameworkElement _targetElement; public ObservableCollection<MultiBinding> Bindings { get; set; } public MultiBindings() { Bindings = new ObservableCollection<MultiBinding>(); } #if !SILVERLIGHT void Loaded(object sender, RoutedEventArgs e) { _targetElement.Loaded -= Loaded; foreach (MultiBinding binding in Bindings) { FieldInfo field = _targetElement.GetType().GetField(binding.TargetProperty + "Property", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (field == null) continue; System.Windows.Data.MultiBinding newBinding = new System.Windows.Data.MultiBinding { Converter = binding.Converter, ConverterParameter = binding.ConverterParameter }; foreach (BindingBase bindingBase in binding.Bindings) { newBinding.Bindings.Add(bindingBase); } DependencyProperty dp = (DependencyProperty)field.GetValue(_targetElement); BindingOperations.SetBinding(_targetElement, dp, newBinding); } } #endif public void SetDataContext(object dataContext) { foreach (MultiBinding relay in Bindings) { relay.DataContext = dataContext; } } public void Initialize(FrameworkElement targetElement) { _targetElement = targetElement; #if !SILVERLIGHT _targetElement.Loaded += Loaded; #else const BindingFlags DpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy; foreach (MultiBinding relay in Bindings) { relay.Initialise(); // find the target dependency property Type targetType = null; string targetProperty = null; // assume it is an attached property if the dot syntax is used. if (relay.TargetProperty.Contains(".")) { // split to find the type and property name string[] parts = relay.TargetProperty.Split('.'); targetType = Type.GetType("System.Windows.Controls." + parts[0] + ", System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"); targetProperty = parts[1]; } else { targetType = targetElement.GetType(); targetProperty = relay.TargetProperty; } FieldInfo[] sourceFields = targetType.GetFields(DpFlags); FieldInfo targetDependencyPropertyField = sourceFields.First(i => i.Name == targetProperty + "Property"); DependencyProperty targetDependencyProperty = targetDependencyPropertyField.GetValue(null) as DependencyProperty; // bind the ConvertedValue of our MultiBinding instance to the target property // of our targetElement Binding binding = new Binding("ConvertedValue") { Source = relay }; targetElement.SetBinding(targetDependencyProperty, binding); } #endif } }
1、定义两个Converter
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//用于多个bool转化为Visibility
public class VisibilityConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (values.Any(value => !(bool) value))
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
//多个文本转换
public class TextConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return string.Join(", ", values);
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
2、在Xaml绑定
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<phone:PhoneApplicationPage
x:Class="Bomo.Test.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:multiBindingExtend="clr-namespace:Bomo.Test.MultiBindingExtend"
xmlns:test="clr-namespace:Bomo.Test"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<phone:PhoneApplicationPage.Resources>
<test:VisibilityConverter x:Key="VisibilityConverter"></test:VisibilityConverter>
<test:TextConverter x:Key="TextConverter"></test:TextConverter>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot 是包含所有页面内容的根网格-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="多路绑定" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="MultiBinding" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<TextBlock >如果两个开关都打开,方块变红色</TextBlock>
<Grid>
<Rectangle Width="300" Height="30" Fill="#eee" Margin="10"></Rectangle>
<Rectangle Width="300" Height="30" Fill="Red" Margin="10" >
<multiBindingExtend:BindingUtil.MultiBindings>
<multiBindingExtend:MultiBindings>
<multiBindingExtend:MultiBinding TargetProperty="Visibility" Converter="{StaticResource VisibilityConverter}">
<multiBindingExtend:MultiBinding.Bindings>
<multiBindingExtend:BindingCollection>
<Binding Path="Toggle1IsChecked"/>
<Binding Path="Toggle2IsChecked"/>
</multiBindingExtend:BindingCollection>
</multiBindingExtend:MultiBinding.Bindings>
</multiBindingExtend:MultiBinding>
</multiBindingExtend:MultiBindings>
</multiBindingExtend:BindingUtil.MultiBindings>
</Rectangle>
</Grid>
</StackPanel>
<ToggleButton Grid.Row="2" Grid.Column="0" IsChecked="{Binding Toggle1IsChecked, Mode=TwoWay}">开关1</ToggleButton>
<ToggleButton Grid.Row="2" Grid.Column="1" IsChecked="{Binding Toggle2IsChecked, Mode=TwoWay}">开关2</ToggleButton>
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">下面文本绑定了两个文本框的内容</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Foreground="{StaticResource PhoneAccentBrush}">
<multiBindingExtend:BindingUtil.MultiBindings>
<multiBindingExtend:MultiBindings>
<multiBindingExtend:MultiBinding TargetProperty="Text" Converter="{StaticResource TextConverter}">
<multiBindingExtend:MultiBinding.Bindings>
<multiBindingExtend:BindingCollection>
<Binding Path="Text1"/>
<Binding Path="Text2"/>
</multiBindingExtend:BindingCollection>
</multiBindingExtend:MultiBinding.Bindings>
</multiBindingExtend:MultiBinding>
</multiBindingExtend:MultiBindings>
</multiBindingExtend:BindingUtil.MultiBindings>
</TextBlock>
<TextBox Grid.Row="2" Grid.Column="0" Text="{Binding Text1, Mode=TwoWay}"></TextBox>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Text2, Mode=TwoWay}"></TextBox>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
3、后台绑定
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public partial class MainPage : INotifyPropertyChanged
{
// 构造函数
public MainPage()
{
InitializeComponent();
}
#region Toggle1IsChecked
/// <summary>
/// The <see cref="Toggle1IsChecked" /> property's name.
/// </summary>
public const string Toggle1IsCheckedPropertyName = "Toggle1IsChecked";
private bool _toggle1IsChecked = false;
/// <summary>
/// Sets and gets the Toggle1IsChecked property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public bool Toggle1IsChecked
{
get
{
return _toggle1IsChecked;
}
set
{
if (_toggle1IsChecked == value)
{
return;
}
_toggle1IsChecked = value;
RaisePropertyChanged(Toggle1IsCheckedPropertyName);
}
}
#endregion
#region Toggle2IsChecked
/// <summary>
/// The <see cref="Toggle2IsChecked" /> property's name.
/// </summary>
public const string Toggle2IsCheckedPropertyName = "Toggle2IsChecked";
private bool _toggle2IsChecked = false;
/// <summary>
/// Sets and gets the Toggle2IsChecked property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public bool Toggle2IsChecked
{
get
{
return _toggle2IsChecked;
}
set
{
if (_toggle2IsChecked == value)
{
return;
}
_toggle2IsChecked = value;
RaisePropertyChanged(Toggle2IsCheckedPropertyName);
}
}
#endregion
#region Text1
/// <summary>
/// The <see cref="Text1" /> property's name.
/// </summary>
public const string Text1PropertyName = "Text1";
private string _text1 = string.Empty;
/// <summary>
/// Sets and gets the Text1 property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string Text1
{
get
{
return _text1;
}
set
{
if (_text1 == value)
{
return;
}
_text1 = value;
RaisePropertyChanged(Text1PropertyName);
}
}
#endregion
#region Text2
/// <summary>
/// The <see cref="Text2" /> property's name.
/// </summary>
public const string Text2PropertyName = "Text2";
private string _text2 = string.Empty;
/// <summary>
/// Sets and gets the Text2 property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string Text2
{
get
{
return _text2;
}
set
{
if (_text2 == value)
{
return;
}
_text2 = value;
RaisePropertyChanged(Text2PropertyName);
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
这里只支持到属性的绑定,不支持到UIElement的绑定
本文引用自:http://blog.csdn.net/huangliangjie/article/details/6734099