上文说到,WinRt中,我们不能在Style的Setter使用Binding.这个问题其实从SL5之前,一直都不可以。但是,为了使用强大的Binding,人们也一直想使用各种方法来达到Binding
从茫茫的Web里找一个有用的东西,本来是很简单的一件事,但是,MS最近几年,经常自我革新,革自己的命,经常今天可以用的技术,明天换个名字,少点东西,就出来让万千小白来试了。从WPF/SL/WinRT一样的Xaml却不一样的精彩。我们常常想,要是WinRT的Xmal也如WPF的那么强大就好了。但这也是只是想想而已。
言归正传
在SL时代,我们参看的一般是这一文章
The taming of the phone [New SetterValueBindingHelper sample demonstrates its usefulness on Windows Phone 7 (and Silverlight 4)]
在WinRT时代,我们借助于SO,找到这个
WinRT : Simple ScheduleControl
另外还一个C++/DX用的
WinRT and C++/CX compatible way to set Bindings via Styles
在这里我们找到一直以来想要的一个帮助类SetterValueBindingHelper
.
如下:

using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Markup; namespace SLWeek.Utils { [ContentProperty(Name = "Values")] public class SetterValueBindingHelper { /// <summary> /// Optional type parameter used to specify the type of an attached /// DependencyProperty as an assembly-qualified name, full name, or /// short name. /// </summary> [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Unambiguous in XAML.")] public string Type { get; set; } /// <summary> /// Property name for the normal/attached DependencyProperty on which /// to set the Binding. /// </summary> public string Property { get; set; } /// <summary> /// Binding to set on the specified property. /// </summary> public Binding Binding { get; set; } /// <summary> /// Collection of SetterValueBindingHelper instances to apply to the /// target element. /// </summary> /// <remarks> /// Used when multiple Bindings need to be applied to the same element. /// </remarks> public Collection<SetterValueBindingHelper> Values { get { // Defer creating collection until needed if (null == _values) { _values = new Collection<SetterValueBindingHelper>(); } return _values; } } private Collection<SetterValueBindingHelper> _values; /// <summary> /// Gets the value of the PropertyBinding attached DependencyProperty. /// </summary> /// <param name="element">Element for which to get the property.</param> /// <returns>Value of PropertyBinding attached DependencyProperty.</returns> [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "SetBinding is only available on FrameworkElement.")] public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element) { if (null == element) { throw new ArgumentNullException("element"); } return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty); } /// <summary> /// Sets the value of the PropertyBinding attached DependencyProperty. /// </summary> /// <param name="element">Element on which to set the property.</param> /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param> [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "SetBinding is only available on FrameworkElement.")] public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value) { if (null == element) { throw new ArgumentNullException("element"); } element.SetValue(PropertyBindingProperty, value); } /// <summary> /// PropertyBinding attached DependencyProperty. /// </summary> public static readonly DependencyProperty PropertyBindingProperty = DependencyProperty.RegisterAttached( "PropertyBinding", typeof(SetterValueBindingHelper), typeof(SetterValueBindingHelper), new PropertyMetadata(null, OnPropertyBindingPropertyChanged)); /// <summary> /// Change handler for the PropertyBinding attached DependencyProperty. /// </summary> /// <param name="d">Object on which the property was changed.</param> /// <param name="e">Property change arguments.</param> private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Get/validate parameters var element = (FrameworkElement)d; var item = (SetterValueBindingHelper)(e.NewValue); if ((null == item.Values) || (0 == item.Values.Count)) { // No children; apply the relevant binding ApplyBinding(element, item); } else { // Apply the bindings of each child foreach (var child in item.Values) { if ((null != item.Property) || (null != item.Binding)) { throw new ArgumentException( "A SetterValueBindingHelper with Values may not have its Property or Binding set."); } if (0 != child.Values.Count) { throw new ArgumentException( "Values of a SetterValueBindingHelper may not have Values themselves."); } ApplyBinding(element, child); } } } /// <summary> /// Applies the Binding represented by the SetterValueBindingHelper. /// </summary> /// <param name="element">Element to apply the Binding to.</param> /// <param name="item">SetterValueBindingHelper representing the Binding.</param> private static void ApplyBinding(FrameworkElement element, SetterValueBindingHelper item) { if ((null == item.Property) || (null == item.Binding)) { throw new ArgumentException( "SetterValueBindingHelper's Property and Binding must both be set to non-null values."); } // Get the type on which to set the Binding Type type = null; TypeInfo typeInfo = null; if (null == item.Type) { // No type specified; setting for the specified element type = element.GetType(); typeInfo = type.GetTypeInfo(); } else { // Try to get the type from the type system type = System.Type.GetType(item.Type); if (null == type) { // Search for the type in the list of assemblies foreach (var assembly in AssembliesToSearch) { // Match on short or full name typeInfo = assembly.DefinedTypes .Where(t => (t.FullName == item.Type) || (t.Name == item.Type)) .FirstOrDefault(); if (null != typeInfo) { // Found; done searching break; } } if (null == typeInfo) { // Unable to find the requested type anywhere throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unable to access type \"{0}\". Try using an assembly qualified type name.", item.Type)); } } else { typeInfo = type.GetTypeInfo(); } } // Get the DependencyProperty for which to set the Binding DependencyProperty property = null; var field = typeInfo.GetDeclaredProperty(item.Property + "Property"); // type.GetRuntimeField(item.Property + "Property"); if (null != field) { property = field.GetValue(null) as DependencyProperty; } if (null == property) { // Unable to find the requsted property throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unable to access DependencyProperty \"{0}\" on type \"{1}\".", item.Property, type.Name)); } // Set the specified Binding on the specified property element.SetBinding(property, item.Binding); } /// <summary> /// Returns a stream of assemblies to search for the provided type name. /// </summary> private static IEnumerable<Assembly> AssembliesToSearch { get { // Start with the System.Windows assembly (home of all core controls) yield return typeof(Windows.UI.Xaml.Controls.Control).GetTypeInfo().Assembly; } } } }
在这里我们要注册一点:
1 var field = typeInfo.GetDeclaredProperty(item.Property + "Property");
我们要清楚,依赖属性的问题,我们当前的typeInfo,他可能有的DeclaredProperty,很多是从基类继承过来的。所以,我们在使用的时候,在Xmal中设定SetterValueBindingHelper
的Type是,一定要设定为基类。要不,就不能正常获得依赖属性
我们解决了Setter不能Binding的问题。
接下来,就完成间隔变色的问题
我们先要实现一个Conveter
1 public sealed class ListItemBackgroudConverter:IValueConverter 2 { 3 public SolidColorBrush OddColorBrush { get; set; } 4 public SolidColorBrush EvenColorBrush { get; set; } 5 public object Convert(object value, Type targetType, object parameter, string language) 6 { 7 var item = value as ListViewItem; 8 if (item != null) 9 { 10 var listView = ItemsControl.ItemsControlFromItemContainer(item); 11 if (listView != null) 12 { 13 var index = listView.IndexFromContainer(item); 14 return index % 2 == 0 ? EvenColorBrush : OddColorBrush; 15 } 16 } 17 18 return null; 19 } 20 21 public object ConvertBack(object value, Type targetType, object parameter, string language) 22 { 23 throw new NotImplementedException(); 24 } 25 }
这个是我们的老套路了,为了方便在XMAL设定间隔色,我们定义了两种Brush
接下来,我们来定义Convetert和ListItem的ItemContainerStyle
<converter:ListItemBackgroudConverter x:Key="ListItemBackgroudConverter" OddColorBrush="{StaticResource SilverColorBrush}" EvenColorBrush="{StaticResource AsbestosColorBrush}"/>
<Style x:Key="ListItemBackgroud" TargetType="ListViewItem"> <Setter Property="utils:SetterValueBindingHelper.PropertyBinding"> <Setter.Value> <utils:SetterValueBindingHelper Type="Control" Property="Background" Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ListItemBackgroudConverter}}"/> </Setter.Value> </Setter> </Style>
再一次提醒注意,这里的Type是Control,而不是ListViewItem,因为只有Control的DeclaredProperty才有Backgroud.
我们在页面应用下这个Style
<ListView ItemsSource="{Binding ListPostTypes,Mode=TwoWay}" ItemContainerStyle="{StaticResource ListItemBackgroud}" Style="{StaticResource ListViewWrapItemsPanel}" ItemTemplate="{StaticResource SelectChannelTypeDataTemplate}"/>
最后,我们看下效果:
由于我ListView的Style对ListView的容器进行了更改,换成了ItemsWrapGrid,所以,它会自动根据宽度进行排列
其Style为
<Style x:Key="ListViewWrapItemsPanel" TargetType="ListView"> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <ItemsWrapGrid Orientation="Horizontal"/> </ItemsPanelTemplate> </Setter.Value> </Setter> </Style>