如何在Xamarin.forms xaml中水平显示ListView中的项目?

时间:2017-12-14 19:07:50

标签: c# xaml listview xamarin xamarin.forms

我在ListView中显示了一个SQLite对象列表,但我想让它们水平显示。所以不要这样:

| longitem        |
| item            |
| evenlongeritem  |
| item            |
| longeritem      |

我想要这个:

| longitem item   |
| evenlongeritem  |
| item longeritem |

重要的是,这些项目的宽度可能不同,因此只需将列表分成一定数量的列就可以得到改进,但并不理想。我也不知道物品的数量。

这是我目前的代码:

<ListView x:Name="inactiveList" VerticalOptions="Start" ItemTapped="PutBack">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Name}" TextColor="Black">
                  <TextCell.ContextActions>
                         <MenuItem Command="{Binding Source={x:Reference ListPage}, Path=DeleteListItem}" CommandParameter="{Binding .}" Text="delete" />
                  </TextCell.ContextActions>
              </TextCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

代码背后:

public ListPage()
{
    InitializeComponent();

    ObservableCollection<ListItem> activeItems =
        new ObservableCollection<ListItem>(
            App.ListItemRepo.GetActiveListItems());
    activeList.ItemsSource = activeItems;
    ...

我尝试将ViewCell包裹在水平StackLayout中,但我收到了此错误:

  

未处理的异常:System.InvalidCastException:指定的强制转换是   无效。

我不确定该错误是什么意思,但我认为不可能在StackLayout内添加DataTemplate。我也无法使ListView水平。

-

更新4:

我终于可以将简单的标签水平列出,但现在我无法重新创建垂直ListView中内置的水龙头和长按动作。这可能吗?

ListView.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns:local="clr-namespace:Myapp">
        <!-- ... -->
        <local:WrapLayout x:Name="inactiveList" ItemsSource="{Binding .}" Spacing="5" />

ListView.xaml.cs

using Myapp.Models;
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using SQLite;
using System.Threading.Tasks;
using System.IO;
using Xamarin.Forms;
using System.Diagnostics;
using DLToolkit.Forms.Controls;

namespace Myapp
{
    public partial class ListPage
    {
        ...
        public ListPage()
        {
            InitializeComponent();

            ObservableCollection<ListItem> inactiveItems =
                new ObservableCollection<ListItem>(
                    App.ListItemRepo.GetInactiveListItems());
            inactiveList.ItemsSource = inactiveItems;
            inactiveList.HeightRequest = 50 * inactiveItems.Count;


        }
        ...
    }

    public class WrapLayout : Layout<View>
    {

        public ObservableCollection<ListItem> ItemsSource
        {
            get { return (ObservableCollection<ListItem>)GetValue(ItemSourceProperty); }
            set { SetValue(ItemSourceProperty, value); }
        }

        public static readonly BindableProperty ItemSourceProperty =
            BindableProperty.Create
            (
                "ItemsSource",
                typeof(ObservableCollection<ListItem>),
                typeof(WrapLayout),
                propertyChanged: (bindable, oldvalue, newvalue) => ((WrapLayout)bindable).AddViews()
            );


        void AddViews()
        {
            Children.Clear();
            foreach (ListItem s in ItemsSource)
            {
                Button button = new Button();
                button.BackgroundColor = Color.Red;
                button.Text = s.Name;
                button.TextColor = Color.Black;
                button.Clicked = "{Binding Source={x:Reference ListPage}, Path=PutBack}";
                Children.Add(button);
            }
        }

        public static readonly BindableProperty SpacingProperty =
            BindableProperty.Create
            (
                "Spacing",
                typeof(double),
                typeof(WrapLayout),
                10.0,
                propertyChanged: (bindable, oldvalue, newvalue) => ((WrapLayout)bindable).OnSizeChanged()
            );

        public double Spacing
        {
            get { return (double)GetValue(SpacingProperty); }
            set { SetValue(SpacingProperty, value); }
        }

        private void OnSizeChanged()
        {
            this.ForceLayout();
        }

        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
        {
            if (WidthRequest > 0)
                widthConstraint = Math.Min(widthConstraint, WidthRequest);
            if (HeightRequest > 0)
                heightConstraint = Math.Min(heightConstraint, HeightRequest);

            double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
            double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);

            return DoHorizontalMeasure(internalWidth, internalHeight);
        }

        private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
        {
            int rowCount = 1;

            double width = 0;
            double height = 0;
            double minWidth = 0;
            double minHeight = 0;
            double widthUsed = 0;

            foreach (var item in Children)
            {
                var size = item.Measure(widthConstraint, heightConstraint);

                height = Math.Max(height, size.Request.Height);

                var newWidth = width + size.Request.Width + Spacing;
                if (newWidth > widthConstraint)
                {
                    rowCount++;
                    widthUsed = Math.Max(width, widthUsed);
                    width = size.Request.Width;
                }
                else
                    width = newWidth;

                minHeight = Math.Max(minHeight, size.Minimum.Height);
                minWidth = Math.Max(minWidth, size.Minimum.Width);
            }

            if (rowCount > 1)
            {
                width = Math.Max(width, widthUsed);
                height = (height + Spacing) * rowCount - Spacing; // via MitchMilam 
            }

            return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
        }

        protected override void LayoutChildren(double x, double y, double width, double height)
        {
            double rowHeight = 0;
            double yPos = y, xPos = x;

            foreach (var child in Children.Where(c => c.IsVisible))
            {
                var request = child.Measure(width, height);

                double childWidth = request.Request.Width;
                double childHeight = request.Request.Height;
                rowHeight = Math.Max(rowHeight, childHeight);

                if (xPos + childWidth > width)
                {
                    xPos = x;
                    yPos += rowHeight + Spacing;
                    rowHeight = 0;
                }

                var region = new Rectangle(xPos, yPos, childWidth, childHeight);
                LayoutChildIntoBoundingRegion(child, region);
                xPos += region.Width + Spacing;
            }
        }
    }
}

3 个答案:

答案 0 :(得分:6)

请参阅My Post。它与你的情况类似。

只需定制Layout并管理其大小及其子项的安排。

更新

  

我收到了“在xmlns clr-namespace:Myapp中找不到本地类型:WrapLayout”错误。

公开课程WrapLayout,将其与ListPage分开。

  

我对如何在这里应用数据绑定感到有点困惑

我们需要在wraplayout中添加名为BindableProperty的{​​{1}},并在属性更改时添加子视图。

XmaI位

ItemSource

背后的代码

 <ContentPage.Content>
    <local:WrapLayout x:Name="wrap" ItemSource="{Binding .}" Spacing="5" />
</ContentPage.Content>

更新:按钮事件

我们可以在List<string> list = new List<string> { "11111111111111111111111", "22222", "333333333333333", "4", "55555555", "6666666666666666666666", "77777", "8888888888", "99999999999999999999999999999999" }; this.BindingContext = list; 内定义event,当我们点击或长按按钮时,触发事件。关于长按,我们应该创建自定义渲染器来实现它。

WrapLayout

WrapLayout

LongPress的自定义渲染器

namespace ImageWrapLayout
{
public class ButtonWithLongPressGesture : Button
{
    public EventHandler LongPressHandle;
    public EventHandler TapHandle;

    public void HandleLongPress(object sender, EventArgs e)
    {
        //Handle LongPressActivated Event
        LongPressHandle(sender, e);
    }

    public void HandleTap(object sender, EventArgs e)
    {
        //Handle Tap Event
        TapHandle(sender, e);
    }
}


public class WrapLayout : Layout<View>
{

    public List<string> ItemSource
    {
        get { return (List<string>)GetValue(ItemSourceProperty); }
        set { SetValue(ItemSourceProperty, value); }
    }

    public static readonly BindableProperty ItemSourceProperty =
        BindableProperty.Create
        (
            "ItemSource",
            typeof(List<string>),
            typeof(WrapLayout),
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapLayout)bindable).AddViews()
        );


    void AddViews()
    {
        Children.Clear();
        foreach (string s in ItemSource)
        {
            ButtonWithLongPressGesture button = new ButtonWithLongPressGesture();
            button.BackgroundColor = Color.Red;
            button.Text = s;
            button.TextColor = Color.Black;
            Children.Add(button);

            button.TapHandle += WrapLayoutTapHandle;
            button.LongPressHandle = WrapLayoutLongPressHandle;
        }
    }


    public EventHandler WrapLayoutLongPressHandle;
    public EventHandler WrapLayoutTapHandle;




    public static readonly BindableProperty SpacingProperty =
        BindableProperty.Create
        (
            "Spacing",
            typeof(double),
            typeof(WrapLayout),
            10.0,
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapLayout)bindable).OnSizeChanged()
        );

    public double Spacing
    {
        get { return (double)GetValue(SpacingProperty); }
        set { SetValue(SpacingProperty, value); }
    }

    private void OnSizeChanged()
    {
        this.ForceLayout();
    }

    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
        if (WidthRequest > 0)
            widthConstraint = Math.Min(widthConstraint, WidthRequest);
        if (HeightRequest > 0)
            heightConstraint = Math.Min(heightConstraint, HeightRequest);

        double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
        double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);

        return DoHorizontalMeasure(internalWidth, internalHeight);
    }

    private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
    {
        int rowCount = 1;

        double width = 0;
        double height = 0;
        double minWidth = 0;
        double minHeight = 0;
        double widthUsed = 0;

        foreach (var item in Children)
        {
            var size = item.Measure(widthConstraint, heightConstraint);

            height = Math.Max(height, size.Request.Height);

            var newWidth = width + size.Request.Width + Spacing;
            if (newWidth > widthConstraint)
            {
                rowCount++;
                widthUsed = Math.Max(width, widthUsed);
                width = size.Request.Width;
            }
            else
                width = newWidth;

            minHeight = Math.Max(minHeight, size.Minimum.Height);
            minWidth = Math.Max(minWidth, size.Minimum.Width);
        }

        if (rowCount > 1)
        {
            width = Math.Max(width, widthUsed);
            height = (height + Spacing) * rowCount - Spacing; // via MitchMilam 
        }

        return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
    }

    protected override void LayoutChildren(double x, double y, double width, double height)
    {
        double rowHeight = 0;
        double yPos = y, xPos = x;

        foreach (var child in Children.Where(c => c.IsVisible))
        {
            var request = child.Measure(width, height);

            double childWidth = request.Request.Width;
            double childHeight = request.Request.Height;
            rowHeight = Math.Max(rowHeight, childHeight);

            if (xPos + childWidth > width)
            {
                xPos = x;
                yPos += rowHeight + Spacing;
                rowHeight = 0;
            }

            var region = new Rectangle(xPos, yPos, childWidth, childHeight);
            LayoutChildIntoBoundingRegion(child, region);
            xPos += region.Width + Spacing;
        }
    }
}
}

用法(在Page.cs中)

[assembly: ExportRenderer(typeof(ButtonWithLongPressGesture), typeof(LongPressGestureRecognizerButtonRenderer))]
namespace ImageWrapLayout.iOS
{
class LongPressGestureRecognizerButtonRenderer : ButtonRenderer
{
    ButtonWithLongPressGesture view;

    public LongPressGestureRecognizerButtonRenderer()
    {
        this.AddGestureRecognizer(new UILongPressGestureRecognizer((longPress) => {
            if (longPress.State == UIGestureRecognizerState.Began)
            {
                view.HandleLongPress(view, new EventArgs());
            }
        }));
    }

    protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
    {
        base.OnElementChanged(e);

        if (e.NewElement != null)
            view = e.NewElement as ButtonWithLongPressGesture;

        //if(Control == null)
        //{
        UIButton but = Control as UIButton;
            but.TouchUpInside += (sender, e1) => {
                view.HandleTap(view, new EventArgs());
            };
        //}
    }
}
}

答案 1 :(得分:2)

您可以使用此:https://github.com/daniel-luberda/DLToolkit.Forms.Controls/tree/master/FlowListView

它与ListView使用相同但具有列支持

enter image description here

答案 2 :(得分:1)

技术上你可以做到。所有VisualElements都有一个Rotation BindableProperty,因此将旋转设置为270.

public static readonly BindableProperty RotationProperty;
public static readonly BindableProperty RotationXProperty;
public static readonly BindableProperty RotationYProperty;

此代码来自Visual Element Class。另请参阅下面的示例。

<ListView x:Name="MessagesListView" Rotation="270" ItemsSource="{Binding Items}" RowHeight="40">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <ViewCell.View>
          <StackLayout>
            <!--mylayouthere-->
          </StackLayout>
        </ViewCell.View>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>