簡體   English   中英

Xamarin.Forms:等效於CSS:last-of-type選擇器

[英]Xamarin.Forms: Equivalent to CSS :last-of-type selector

我目前正在重新設計一個頁面,該頁面在Xamarin.Forms應用程序中顯示聯系信息。 該頁面將顯示一個部分列表(地址,電話號碼,電子郵件地址等),每個部分都有一個圖標和相關信息。 這些部分應該用一條線隔開,但是第一部分的上方和最后部分的下方都不應有任何行。 另外,空白部分根本不會顯示。

標記基本上如下所示:

<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>

除了線條外,我大部分時間都可以使用它。 (我只是使用BoxView ,將HeightRequest為1。)為了使它們正常工作,我需要告訴渲染器在除最后一個部分之外的每個可見部分下方繪制一條線。 本質上,我需要一個CSS3樣式的:not(:last-of-type)選擇器(或上面一行帶有:not(:first-of-type)選擇器)。

在XAML中做到這一點的最佳方法是什么? (如果需要,還是在代碼后面?)

您只是提醒我,我已經想要了一段時間,所以我寫了一個(帶有摘要,這是一份十分鍾的工作)。 讓我知道這與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>

在Ed Plunkett為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
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM