你的位置:首页 > ASP.net教程

[ASP.net教程]WinRT ListView间隔变色(二)

上文说到,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;      }    }  }}

View Code


在这里我们要注册一点:

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>