Listview 表单列表视图大小与内容?

Listview 表单列表视图大小与内容?,listview,xamarin.forms,height,sizetocontent,Listview,Xamarin.forms,Height,Sizetocontent,我有一个相当大的表单(主要适用于平板电脑),它有一个TabbedPage嵌套ScrollView和一个垂直StackPanel,其中包含许多控件 我有一个列表视图,其中包含一些单行项目,我需要它根据内容调整大小。 我想去掉它的滚动条,但无论如何,我不希望它占用的空间超过其项目所需的空间。 有没有一种方法(甚至是丑陋的方法)可以实现这一点,而不必在x3平台上编写渲染器 下面是一个描述我的树的伪代码: <ContentPage> <MasterDetailPage>

我有一个相当大的表单(主要适用于平板电脑),它有一个
TabbedPage
嵌套
ScrollView
和一个垂直
StackPanel
,其中包含许多控件

我有一个
列表视图
,其中包含一些单行项目,我需要它根据内容调整大小。
我想去掉它的滚动条,但无论如何,我不希望它占用的空间超过其项目所需的空间。
有没有一种方法(甚至是丑陋的方法)可以实现这一点,而不必在x3平台上编写渲染器

下面是一个描述我的树的伪代码:

<ContentPage>
  <MasterDetailPage>
    <MasterDetailPage.Detail>
      <TabbedPage>
        <ContentPage>
          <ScrollView>
            <StackPanel>
              <!-- many controls-->
              <ListView>

渲染时,
列表视图
后会出现巨大的间隙。我怎样才能避免呢

我试着摆弄
垂直选项
高度请求
,但没有一个有效


我正在寻找一种动态的方法(最好是没有继承)来实现这一点,而不涉及自定义渲染器。

好吧,假设您的ListView中填充了新闻提要,让我们使用
ObservableCollection
来包含数据,以填充ListView,如下所示:

XAML代码:

<ListView x:Name="newslist"/>
然后,我们对ListView和数据进行了一些逻辑处理,以便ListView包装数据,随着数据的增加,ListView也会增加,反之亦然:

int i = trends.Count;
int heightRowList = 90;
i = (i * heightRowList);
newslist.HeightRequest = i;
因此,完整的代码是:

ObservableCollection <News> trends = new ObservableCollection<News>();
newslist.ItemSource = trends;
int i = trends.Count;
int heightRowList = 90;
i = (i * heightRowList);
newslist.HeightRequest = i;
ObservableCollection trends=新的ObservableCollection();
newslist.ItemSource=趋势;
int i=趋势。计数;
整数高度行列表=90;
i=(i*高度行列表);
newslist.HeightRequest=i;
希望能有帮助

基于Lutaaya的行为,我做了一个自动化的行为,确定并设置行高()

行为:
名称空间Xamarin.Forms
{
使用制度;
使用System.Linq;
公共类AutoSizeBehavior:行为
{
ListView _ListView;
ItemPlatedItemView单元格=>\u ListView;
受保护的覆盖无效数据到(ListView可绑定)
{
bindable.itemseapping+=外观已更改;
bindable.item+=外观已更改;
_ListView=可绑定;
}
受保护的覆盖无效OnDetachingFrom(ListView可绑定)
{
bindable.itemseapping-=外观已更改;
bindable.items-=外观已更改;
_ListView=null;
}
无效外观已更改(对象发送者,ItemVisibilityEventArgs e)=>
更新重量(如项目);
无效更新高度(对象项)
{
if(_ListView.hasRows)
{
双高;
如果((高度=_ListView.HeightRequest)=
(双精度)VisualElement.HeightRequestProperty.DefaultValue)
高度=0;
高度+=测量高度(项目);
设置高度(高度);
}
else if(_ListView.RowHeight==(int)ListView.RowHeightProperty.DefaultValue)
{
var高度=测量高度(项目);
_ListView.RowHeight=高度;
设置高度(高度);
}
}
int MEASUREROWEIGHT(对象项)
{
var template=\u ListView.itemtplate;
var cell=(cell)template.CreateContent();
cell.BindingContext=项;
变量高度=cell.RenderHeight;
var mod=高度%1;
如果(mod>0)
高度=高度-mod+1;
返回(int)高度;
}
空隙设置高度(双倍高度)
{
//如果页眉或页脚是字符串等,则执行TODO。
如果(_ListView.Header是VisualElement头)
高度+=收割台高度;
如果(_ListView.Footer是VisualElement Footer)
高度+=页脚高度;
_ListView.HeightRequest=高度;
}
}
}
用法:


我可能过分简化了这里的内容,但只需将
添加到ListView中就行了。

我可以制作一个事件处理程序,考虑ListView单元格大小变化的影响。给你:

    <ListView ItemsSource="{Binding Items}"
         VerticalOptions="Start"
         HasUnevenRows="true"
         CachingStrategy="RecycleElement"
         SelectionMode="None" 
         SizeChanged="ListView_OnSizeChanged">
               <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell >
                           <Frame Padding="10,0" SizeChanged="VisualElement_OnSizeChanged">

框架可以通过网格、堆栈布局等进行更改。 xaml.cs:

static readonly Dictionary\u listViewHeightDictionary=new Dictionary();
私有void VisualElement_OnSizeChanged(对象发送方,事件参数e)
{
变量帧=(VisualElement)发送方;
var listView=(listView)frame.Parent.Parent;
变量高度=(int)frame.Measure(10001000,MeasureFlags.IncludeMargins)。最小高度;
if(!\u listViewHeightDictionary.ContainsKey(listView))
{
_listViewHeightDictionary[listView]=新字典();
}
if(!\u listViewHeightDictionary[listView].TryGetValue(帧,out var oldHeight)| | oldHeight!=高度)
{
_listViewHeightDictionary[listView][frame]=高度;
var fullHeight=_listViewHeightDictionary[listView].Values.Sum();
如果((int)listView.HeightRequest!=全高
&&listView.ItemsSource.Cast().Count()==\u listViewHeightDictionary[listView].Count
)
{
listView.HeightRequest=全高;
布局(新矩形(listView.X、listView.Y、listView.Width、fullHeight));
}
}
}
private void ListView_OnSizeChanged(对象发送方,事件参数e)
{
var listView=(listView)发送方;
如果(listView.ItemsSource==null | | listView.ItemsSource.Cast().Count()==0)
{
listView.HeightRequest=0;
}
}
显示帧时(应用ListView.ItemTemplate),帧的大小会发生变化。 我们通过Measure()方法获取它的实际高度,并将其放入字典中,字典知道当前ListView并保存帧的高度。当显示最后一帧时,我们计算所有高度的总和。 如果没有项目,ListView_OnSizeChanged()将ListView.HeightRequest设置为0。

换句话说
ObservableCollection <News> trends = new ObservableCollection<News>();
newslist.ItemSource = trends;
int i = trends.Count;
int heightRowList = 90;
i = (i * heightRowList);
newslist.HeightRequest = i;
namespace Xamarin.Forms
{
  using System;
  using System.Linq;
  public class AutoSizeBehavior : Behavior<ListView>
  {
    ListView _ListView;
    ITemplatedItemsView<Cell> Cells => _ListView;

    protected override void OnAttachedTo(ListView bindable)
    {
      bindable.ItemAppearing += AppearanceChanged;
      bindable.ItemDisappearing += AppearanceChanged;
      _ListView = bindable;
    }

    protected override void OnDetachingFrom(ListView bindable)
    {
      bindable.ItemAppearing -= AppearanceChanged;
      bindable.ItemDisappearing -= AppearanceChanged;
      _ListView = null;
    }

    void AppearanceChanged(object sender, ItemVisibilityEventArgs e) =>
      UpdateHeight(e.Item);

    void UpdateHeight(object item)
    {
      if (_ListView.HasUnevenRows)
      {
        double height;
        if ((height = _ListView.HeightRequest) == 
            (double)VisualElement.HeightRequestProperty.DefaultValue)
          height = 0;

        height += MeasureRowHeight(item);
        SetHeight(height);
      }
      else if (_ListView.RowHeight == (int)ListView.RowHeightProperty.DefaultValue)
      {
        var height = MeasureRowHeight(item);
        _ListView.RowHeight = height;
        SetHeight(height);
      }
    }

    int MeasureRowHeight(object item)
    {
      var template = _ListView.ItemTemplate;
      var cell = (Cell)template.CreateContent();
      cell.BindingContext = item;
      var height = cell.RenderHeight;
      var mod = height % 1;
      if (mod > 0)
        height = height - mod + 1;
      return (int)height;
    }

    void SetHeight(double height)
    {
      //TODO if header or footer is string etc.
      if (_ListView.Header is VisualElement header)
        height += header.Height;
      if (_ListView.Footer is VisualElement footer)
        height += footer.Height;
      _ListView.HeightRequest = height;
    }
  }
}
<ContentPage xmlns:xf="clr-namespace:Xamarin.Forms">
  <ListView>
    <ListView.Behaviors>
      <xf:AutoSizeBehavior />
    <ListView ItemsSource="{Binding Items}"
         VerticalOptions="Start"
         HasUnevenRows="true"
         CachingStrategy="RecycleElement"
         SelectionMode="None" 
         SizeChanged="ListView_OnSizeChanged">
               <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell >
                           <Frame Padding="10,0" SizeChanged="VisualElement_OnSizeChanged">
        static readonly Dictionary<ListView, Dictionary<VisualElement, int>> _listViewHeightDictionary = new Dictionary<ListView, Dictionary<VisualElement, int>>();

    private void VisualElement_OnSizeChanged(object sender, EventArgs e)
    {
        var frame = (VisualElement) sender;
        var listView = (ListView)frame.Parent.Parent;
        var height = (int) frame.Measure(1000, 1000, MeasureFlags.IncludeMargins).Minimum.Height;
        if (!_listViewHeightDictionary.ContainsKey(listView))
        {
            _listViewHeightDictionary[listView] = new Dictionary<VisualElement, int>();
        }
        if (!_listViewHeightDictionary[listView].TryGetValue(frame, out var oldHeight) || oldHeight != height)
        {
            _listViewHeightDictionary[listView][frame] = height;
            var fullHeight = _listViewHeightDictionary[listView].Values.Sum();
            if ((int) listView.HeightRequest != fullHeight 
                && listView.ItemsSource.Cast<object>().Count() == _listViewHeightDictionary[listView].Count
                )
            {
                listView.HeightRequest = fullHeight;
                listView.Layout(new Rectangle(listView.X, listView.Y, listView.Width, fullHeight));
            }
        }
    }

    private void ListView_OnSizeChanged(object sender, EventArgs e)
    {
        var listView = (ListView)sender;
        if (listView.ItemsSource == null || listView.ItemsSource.Cast<object>().Count() == 0)
        {
            listView.HeightRequest = 0;
        }
    }