2016-10-10 72 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 
    } 
}