我有一个非常大的表单(主要适用于平板电脑),其TabbedPage
嵌套ScrollView
,垂直StackPanel
包含许多控件。
我很少出现ListView
包含几个单行项目的情况,我需要根据内容调整大小。
我想摆脱它的滚动条,但无论如何我不希望它占用的空间超过其物品所需的空间。
有没有办法(甚至是丑陋的)无需编写渲染器x3平台即可实现这一目标?
这是描述我的树的伪:
<ContentPage>
<MasterDetailPage>
<MasterDetailPage.Detail>
<TabbedPage>
<ContentPage>
<ScrollView>
<StackPanel>
<!-- many controls-->
<ListView>
渲染后,ListView
之后会出现巨大差距。我怎么能避免这种情况?
我尝试弄乱VerticalOptions
和HeightRequest
,其中没有效果。
我正在寻找一种动态方式(最好没有继承)来实现这一点,而不涉及自定义渲染器。
答案 0 :(得分:8)
基于Lutaaya的answer,我做了一个行为,自动执行此操作,确定并设置行高(Gist)。
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 />
答案 1 :(得分:4)
确定假设您的ListView填充了NewsFeeds,让我们使用ObservableCollection
来包含我们的数据以填充ListView,如下所示:
XAML代码:
<ListView x:Name="newslist"/>
C#代码
ObservableCollection <News> trends = new ObservableCollection<News>();
然后将趋势列表分配给ListView:
newslist.ItemSource = trends;
然后,我们在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;
希望它有所帮助。
答案 2 :(得分:1)
我可能在这里简化了事情,但是仅将HasUnevenRows="True"
添加到ListView对我来说是有效的。
答案 3 :(得分:0)
我可以使事件处理程序考虑到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">
可以通过Grid,StackLayout等更改框架。 xaml.cs:
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;
}
}
当显示框架(正在应用ListView.ItemTemplate)时,框架的大小会更改。 我们通过Measure()方法获取实际高度,然后将其放入Dictionary中,后者了解当前的ListView并保存Frame的高度。当显示最后一帧时,我们将所有高度相加。 如果没有项目,则ListView_OnSizeChanged()会将listView.HeightRequest设置为0。