【WP8】MultiBinding

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

 

【WP8】MultiBinding

 

五个类

 

【WP8】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

 

【WP8】MultiBinding
    /// <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
【WP8】MultiBinding
    /// <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
【WP8】MultiBinding
    /// <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
【WP8】MultiBinding
    /// <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
【WP8】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

【WP8】MultiBinding
    //用于多个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绑定

【WP8】MultiBinding
<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、后台绑定

【WP8】MultiBinding
    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

你可能感兴趣的:(bind)