zoukankan      html  css  js  c++  java
  • 【WP8】MultiBinding

    WP中系统没有像WPF中直接支持MultiBinding,可以通过以下代码实现

     

    五个类

        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");
                }
            }
        }
    BindingCollection
        /// <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
    
        }
    BindingSlave
        /// <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
    
        }
    BindingUtil
        /// <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);
        }
    IMultiValueConverter
        /// <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
        }
    MultiBinding
        [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
            }
        }
    MultiBindings

    1、定义两个Converter

        //用于多个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
        }
    Converter

     

    2、在Xaml绑定

    <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>
    MainPage.xaml

     

    3、后台绑定

        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
        }
    MainPage.xaml.cs

     

    这里只支持到属性的绑定,不支持到UIElement的绑定

     

    本文引用自:http://blog.csdn.net/huangliangjie/article/details/6734099

  • 相关阅读:
    每日一题:用一个SQL语句交换两条数据某一列的值
    String value(); String[] value(); 比较
    java.sql.SQLException: Unknown system variable 'tx_isolation'
    return; 用法
    Linux和UNIX的关系及区别(详解版)
    404
    Centos 7 修改YUM镜像源地址为阿里云镜像地址
    JVM面试必备
    秒杀优化迭代
    电商秒杀基础构建项目笔记2(优化效验准则和商品模型创建)
  • 原文地址:https://www.cnblogs.com/bomo/p/3616451.html
Copyright © 2011-2022 走看看