2016-10-10 32 views
1

我目前正在重新设计一个显示Xamarin.Forms应用程序中的联系人信息的页面。该页面将显示部分列表(地址,电话号码,电子邮件地址等),每个部分都有一个图标和相关信息。这些部分应该用一条线分隔,但不应该在第一部分和最后一部分之上。此外,空白部分根本不显示。Xamarin.Forms:相当于CSS:最后一种类型的选择器

的标记基本上是这样的:

<ScrollView> 
    <StackLayout> 
    <Label Text="{Binding Contact.Name}" /> 
    <controls:ContactSection Icon="address.png"> 
     <!-- address-type stuff --> 
    </controls:ContactSection> 
    <controls:ContactSection Icon="phone.png"> 
     <!-- phone numbers --> 
    </controls:ContactSection> 
    <!-- further sections --> 
    </StackLayout> 
</ScrollView> 

我找到了工作,除了线的大部分。 (我只是使用BoxViewHeightRequest为1.)为了使它们正常工作,我需要告诉渲染器在除最后一个之外的每个可见部分下方绘制一条线。实质上,我需要一个CSS3风格的:not(:last-of-type)选择器(或者一个:not(:first-of-type)与上面的行)。

在XAML中这样做的最好方法是什么? (或者在必要的情况下在代码后面?)

+0

这是一个痛点。我曾经写过一个ListBox子类,它在它拥有的项目容器上设置布尔FirstItem/LastItem附加属性。我想你可以写一个与StackPanel的子项相同的行为。一旦你有了,你可以在LastItem属性中轻松地使用触发器编写一个Style。 –

回答

1

你刚刚提醒我,我一直想要这一段时间,所以我写了一个(带片断,这是一个十分钟的工作)。让我知道如果这与Xamarin合作,我无法用这个测试。

更新:今天我必须半睡半醒。我将“StackLayout”读为“StackPanel”。 OP将其改编为Xamarin,并将该工作代码公布为另一个答案。

using System; 
using System.Windows; 
using System.Windows.Controls; 

namespace HollowEarth.AttachedProperties 
{ 
    public static class PanelBehaviors 
    { 
     public static void UpdateChildFirstLastProperties(Panel panel) 
     { 
      for (int i = 0; i < panel.Children.Count; ++i) 
      { 
       var child = panel.Children[i]; 

       SetIsFirstChild(child, i == 0); 
       SetIsLastChild(child, i == panel.Children.Count - 1); 
      } 
     } 

     #region PanelExtensions.IdentifyFirstAndLastChild Attached Property 
     public static bool GetIdentifyFirstAndLastChild(Panel panel) 
     { 
      return (bool)panel.GetValue(IdentifyFirstAndLastChildProperty); 
     } 

     public static void SetIdentifyFirstAndLastChild(Panel panel, bool value) 
     { 
      panel.SetValue(IdentifyFirstAndLastChildProperty, value); 
     } 

     /// <summary> 
     /// Behavior which causes the Panel to identify its first and last children with attached properties. 
     /// </summary> 
     public static readonly DependencyProperty IdentifyFirstAndLastChildProperty = 
      DependencyProperty.RegisterAttached("IdentifyFirstAndLastChild", typeof(bool), typeof(PanelBehaviors), 
       // Default MUST be false, or else True won't be a change in 
       // the property value, so PropertyChanged callback won't be 
       // called, and nothing will happen. 
       new PropertyMetadata(false, IdentifyFirstAndLastChild_PropertyChanged)); 

     private static void IdentifyFirstAndLastChild_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      Panel panel = (Panel)d; 

      ((Panel)d).LayoutUpdated += (s, e2) => UpdateChildFirstLastProperties(panel); 
     } 

     #endregion PanelExtensions.IdentifyFirstAndLastChild Attached Property 

     #region PanelExtensions.IsFirstChild Attached Property 
     public static bool GetIsFirstChild(UIElement obj) 
     { 
      return (bool)obj.GetValue(IsFirstChildProperty); 
     } 

     public static void SetIsFirstChild(UIElement obj, bool value) 
     { 
      obj.SetValue(IsFirstChildProperty, value); 
     } 

     /// <summary> 
     /// True if UIElement is first child of a Panel 
     /// </summary> 
     public static readonly DependencyProperty IsFirstChildProperty = 
      DependencyProperty.RegisterAttached("IsFirstChild", typeof(bool), typeof(PanelBehaviors), 
       new PropertyMetadata(false)); 
     #endregion PanelExtensions.IsFirstChild Attached Property 

     #region PanelExtensions.IsLastChild Attached Property 
     public static bool GetIsLastChild(UIElement obj) 
     { 
      return (bool)obj.GetValue(IsLastChildProperty); 
     } 

     public static void SetIsLastChild(UIElement obj, bool value) 
     { 
      obj.SetValue(IsLastChildProperty, value); 
     } 

     /// <summary> 
     /// True if UIElement is last child of a Panel 
     /// </summary> 
     public static readonly DependencyProperty IsLastChildProperty = 
      DependencyProperty.RegisterAttached("IsLastChild", typeof(bool), typeof(PanelBehaviors), 
       new PropertyMetadata(false)); 
     #endregion PanelExtensions.IsLastChild Attached Property 
    } 
} 

用例:

<StackPanel 
    xmlns:heap="clr-namespace:HollowEarth.AttachedProperties" 
    heap:PanelBehaviors.IdentifyFirstAndLastChild="True" 
    HorizontalAlignment="Left" 
    Orientation="Vertical" 
    > 
    <StackPanel.Resources> 
     <Style TargetType="Label"> 
      <Setter Property="Content" Value="Blah blah" /> 
      <Setter Property="Background" Value="SlateGray" /> 
      <Setter Property="Margin" Value="4" /> 

      <Style.Triggers> 
       <Trigger Property="heap:PanelBehaviors.IsFirstChild" Value="True"> 
        <Setter Property="Background" Value="DeepSkyBlue" /> 
        <Setter Property="Content" Value="First Child" /> 
       </Trigger> 
       <Trigger Property="heap:PanelBehaviors.IsLastChild" Value="True"> 
        <Setter Property="Background" Value="SeaGreen" /> 
        <Setter Property="Content" Value="Last Child" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </StackPanel.Resources> 
    <Label /> 
    <Label /> 
    <Label /> 
    <Label /> 
</StackPanel> 
+0

谢谢,这工作!我将添加Xamarin版本的代码作为我自己的完整答案,但你的确是“正确的”。 –

+0

@TimOkrongli太棒了。感谢您发布Xamarin版本。 –

1

后埃德宾吉提供了WPF的解决方案,我决定发布Xamarin.Forms相当于我从他的代码构建。

namespace Foo.Behaviors 
{ 
    using System.Linq; 

    using Xamarin.Forms; 

    /// <summary> 
    ///  Identifies the first and last child of a <see cref="Layout{View}"/>. 
    /// </summary> 
    public class FirstAndLastChildBehavior 
    { 
     /// <summary> 
     ///  Identifies the first and last child of the given <see cref="Layout{View}"/>. 
     /// </summary> 
     /// <param name="layout">The <see cref="Layout{View}"/>.</param> 
     public static void UpdateChildFirstLastProperties(Layout<View> layout) 
     { 
      // This is just here to provide a convenient place to do filtering, e.g. .Where(v => v.IsVisible). 
      var children = layout.Children; 

      for (var i = 0; i < children.Length; ++i) 
      { 
       var child = children[i]; 

       SetIsFirstChild(child, i == 0); 
       SetIsLastChild(child, i == children.Length - 1); 
      } 
     } 

     #region PanelExtensions.IdentifyFirstAndLastChild Attached Property 
     /// <summary> 
     ///  Gets a value that controls whether the child-identifying functionality is enabled for the given <see cref="Layout{View}"/>. 
     /// </summary> 
     /// <param name="layout">The <see cref="Layout{View}"/>.</param> 
     /// <returns><c>True</c> if functionality has been enabled, <c>false</c> otherwise.</returns> 
     public static bool GetIdentifyFirstAndLastChild(Layout<View> layout) 
     { 
      return (bool)layout.GetValue(IdentifyFirstAndLastChildProperty); 
     } 

     /// <summary> 
     ///  Sets a value that controls whether the child-identifying functionality is enabled for the given <see cref="Layout{View}"/>. 
     /// </summary> 
     /// <param name="layout">The <see cref="Layout{View}"/>.</param> 
     /// <param name="value">The value.</param> 
     public static void SetIdentifyFirstAndLastChild(Layout<View> layout, bool value) 
     { 
      layout.SetValue(IdentifyFirstAndLastChildProperty, value); 
     } 

     /// <summary> 
     ///  Identifies the <see cref="IdentifyFirstAndLastChild"/> property. 
     /// </summary> 
     /// <remarks> 
     ///  The behavior can't be turned off; once the value is set to <c>true</c> the behavior will stick even if it's set back to 
     ///  <c>false</c> later. 
     /// </remarks> 
     public static readonly BindableProperty IdentifyFirstAndLastChildProperty = BindableProperty.CreateAttached(
      "IdentifyFirstAndLastChild", 
      typeof(bool), 
      typeof(FirstAndLastChildBehavior), 
      false, 
      BindingMode.OneWay, 
      null, 
      IdentifyFirstAndLastChildPropertyChanged); 

     /// <summary> 
     ///  Gets called when IdentifyFirstAndLastChildProperty changes. 
     /// </summary> 
     /// <param name="bindable">The object we're bound to.</param> 
     /// <param name="oldValue">This parameter is not used.</param> 
     /// <param name="newValue">This parameter is not used.</param> 
     private static void IdentifyFirstAndLastChildPropertyChanged(BindableObject bindable, object oldValue, object newValue) 
     { 
      var layout = (Layout<View>)bindable; 

      ((Layout<View>)bindable).LayoutChanged += (a, b) => UpdateChildFirstLastProperties(layout); 
     } 
     #endregion PanelExtensions.IdentifyFirstAndLastChild Attached Property 

     #region PanelExtensions.IsFirstChild Attached Property 
     /// <summary> 
     ///  Gets a value that determines whether the given <see cref="View"/> is the first child of its parent. 
     /// </summary> 
     /// <param name="obj">The <see cref="View"/>.</param> 
     /// <returns><c>True</c> if the <see cref="View"/> is the first child, <c>false</c> otherwise.</returns> 
     public static bool GetIsFirstChild(View obj) 
     { 
      return (bool)obj.GetValue(IsFirstChildProperty); 
     } 

     /// <summary> 
     ///  Sets a value that determines whether the given <see cref="View"/> is the first child of its parent. 
     /// </summary> 
     /// <param name="obj">The <see cref="View"/>.</param> 
     /// <param name="value">The value.</param> 
     public static void SetIsFirstChild(View obj, bool value) 
     { 
      obj.SetValue(IsFirstChildProperty, value); 
     } 

     /// <summary> 
     ///  Identifies the <see cref="IsFirstChild"/> property. 
     /// </summary> 
     public static readonly BindableProperty IsFirstChildProperty = BindableProperty.CreateAttached(
      "IsFirstChild", 
      typeof(bool), 
      typeof(FirstAndLastChildBehavior), 
      false); 
     #endregion PanelExtensions.IsFirstChild Attached Property 

     #region PanelExtensions.IsLastChild Attached Property 
     /// <summary> 
     ///  Gets a value that determines whether the given <see cref="View"/> is the last child of its parent. 
     /// </summary> 
     /// <param name="obj">The <see cref="View"/>.</param> 
     /// <returns><c>True</c> if the <see cref="View"/> is the last child, <c>false</c> otherwise.</returns> 
     public static bool GetIsLastChild(View obj) 
     { 
      return (bool)obj.GetValue(IsLastChildProperty); 
     } 

     /// <summary> 
     ///  Sets a value that determines whether the given <see cref="View"/> is the last child of its parent. 
     /// </summary> 
     /// <param name="obj">The <see cref="View"/>.</param> 
     /// <param name="value">The value.</param> 
     public static void SetIsLastChild(View obj, bool value) 
     { 
      obj.SetValue(IsLastChildProperty, value); 
     } 

     /// <summary> 
     ///  Identifies the <see cref="IsLastChild"/> property. 
     /// </summary> 
     public static readonly BindableProperty IsLastChildProperty = BindableProperty.CreateAttached(
      "IsLastChild", 
      typeof(bool), 
      typeof(FirstAndLastChildBehavior), 
      false); 
     #endregion PanelExtensions.IsLastChild Attached Property 
    } 
} 
相关问题