Xamarin的.表单:相当于CSS:last-of-type选择器

本文关键字:last-of-type 选择器 CSS 相当于 表单 Xamarin | 更新日期: 2023-09-27 18:12:25

我目前正在重新设计一个在Xamarin中显示联系人信息的页面。该页面将显示部分列表(地址,电话号码,电子邮件地址等),每个部分都有一个图标和相关信息。各部分之间应该用一条线隔开,但第一部分的上方和最后一部分的下方不能有线。此外,空部分根本不显示。

标记基本上是这样的:

<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的.表单:相当于CSS:last-of-type选择器

你刚刚提醒我,我想要这个已经有一段时间了,所以我写了一个(带有片段,这是一个十分钟的工作)。让我知道如何/如果这与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。我用他的代码构建了一个等价的表单。

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