点击新项目时折叠 Xamarin ListView 中的所有项目
Posted
技术标签:
【中文标题】点击新项目时折叠 Xamarin ListView 中的所有项目【英文标题】:Collapse all items in Xamarin ListView when a new item is tapped 【发布时间】:2021-07-09 23:50:58 【问题描述】:我有一个看似相对简单的问题,但我想不通。
我有一个 Xamarin ListView,它使用 ViewCells 的项目模板,如下所示:
<ListView HasUnevenRows="True"
ItemsSource="Binding Devices"
ItemTemplate="StaticResource DeviceDataTemplateSelector"
SelectionMode="None"
ItemTapped="NewItemTapped"
/>
目前,“DeviceDataTemplateSelector”始终返回以下 ViewCell(稍后将添加更多 ViewCell)。
ViewCell 使用 Xamarin 社区工具包中的 Expander 控件
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:ViewModel="clr-namespace:App.ViewModels"
x:Class="App.UI.ViewCellStandardTemplate">
<ViewCell.View>
<!-- Calls an expander for each ViewCell. -->
<xct:Expander HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Margin="StaticResource UniversalMargin"
>
<xct:Expander.Header>
<Grid HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Margin="StaticResource UniversalMargin">
<Grid.RowDefinitions>
<RowDefinition Height = "*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width = "*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Header Text" />
</Grid>
</xct:Expander.Header>
<xct:Expander.Content>
<Grid HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Margin="StaticResource UniversalMargin">
<Grid.RowDefinitions>
<RowDefinition Height = "*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width = "*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0"
Grid.Column="0"
Text="Click Me"
Command="Binding Source=RelativeSource AncestorType=x:Type ViewModel:PageViewModel, Mode=FindAncestorBindingContext, Path=ClickCommand"
CommandParameter="Binding ."
/>
</Grid>
</xct:Expander.Content>
</xct:Expander>
</ViewCell.View>
</ViewCell>
到目前为止,所有这些都正常工作 - 问题是当我点击 ListView 中的一个项目时,我希望任何其他展开的项目都折叠起来。
换句话说,当我点击一个 ViewCell 来展开它时,我希望 ListView 中的所有其他 ViewCell 将它们各自的 Expander 控件的“IsExpanded”属性设置为 false。
我已尝试在 ListView 中调用“ItemTapped”事件,然后循环浏览 ListView 中的元素 - 我不确定这是否是正确的方法?
在 ViewCell 中访问 Expander 控件属性的最佳方式是什么?
我不知道该怎么做 - 任何帮助将不胜感激。
谢谢。
【问题讨论】:
当您“循环遍历 ListView 中的元素”时会发生什么?您是否成功地将“IsExpanded”属性设置为 false?但它实际上并没有折叠它们? 【参考方案1】:更新
编辑提供纯UI解决方案,灵感来自get cells from ListView(或者可以试试这个highlight the selected item)
两步:
-
在 XAML 中定义
ViewCell
和 ListView
。
<ListView x:Name="MyList" ...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<xct:Expander Tapped="Expander_Tapped" ...>
-
在 XAML.cs 中定义方法:
private void Expander_Tapped(object sender, EventArgs e)
var expander = sender as Expander;
var state = expander.IsExpanded;
var cells = MyList.GetType().GetRuntimeProperties()
.FirstOrDefault(info => info.Name == "TemplatedItems")?.GetValue(MyList);
if (cells != null)
foreach (ViewCell cell in cells as ITemplatedItemsList<Cell>)
if (cell.BindingContext != null)
var child = cell.View as Expander;
child.IsExpanded = false;
expander.IsExpanded = state;
上一个答案:
与其操作视图属性,我更愿意在模型类中进行操作。
这里的三个步骤:
-
在模型类中添加布尔属性。
public class YourDeviceModel : INotifyPropertyChanged
private bool flag;
public bool Flag
get => flag;
set
flag = value;
OnPropertyChanged("Flag");
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
-
单击时在 ViewModel 中定义
TapCommand
(如果使用 MVVM)
public ICommand TapCommand => new Command<YourDeviceModel>(OnItemTapped);
private void OnItemTapped(YourDeviceModel currentSelection)
//if your Devices is a ObservableCollection, convert it and reset all flags
Devices.ToList().ForEach(x => x.Flag = false);
//set the current one to true
currentSelection.Flag = true;
-
在
Expander
中添加属性和命令绑定(在ListView 中删除ItemTapped
)
<xct:Expander
IsExpanded="Binding Flag, Mode=TwoWay"
Command="Binding Source=RelativeSource AncestorType=x:Type ViewModel:PageViewModel, Mode=FindAncestorBindingContext, Path=TapCommand"
CommandParameter="Binding .">
【讨论】:
这可能不是一个好主意。 IsExpanded 在 Model 域中可能没有任何意义;它可能纯粹是视图状态的一部分。 (仅供参考:这是数据绑定更大问题的一部分;它擅长从模型进行简单映射,但一旦视图需求更复杂,就不是很好了。所以任何一个都做你做的事情,这与分离视图的目标背道而驰来自模型,或者需要每个项目的“视图模型”,这很笨拙/容易出错,因为现在有两个层次结构要保持同步。我怀疑数据绑定最终会被放弃。也许是 MVU,但我没有t 研究过。) 我同意仅在大多数情况下保留逻辑(例外情况,例如列表需要根据数据扩展某些项目)并在不触及 ViewModel 的情况下更新解决方案。 很遗憾,由于各种原因,我无法使用此解决方案。我需要数据模板选择器来选择不同的视图单元格。更重要的是,我还想尽可能地保留它 MVVM。在模型中具有扩展状态会打破这一点。我已经尝试了第一种方法,但无法使其正常工作,因为将 ViewCell.View 转换为堆栈面板总是会引发空异常 - 不知道为什么。我原以为会有一种相对简单的方法来做到这一点,它遵守 MVVM,但显然不是:/ 但是我很欣赏答案的努力! 我的错,我还在 ViewCell 上测试了 Tapped 事件,并在那里添加了一个 StackLayout 并对逻辑进行了编码。只要var child = cell.View as Expander
就可以了,还更新了答案。
嗨@DR_Bart,您尝试过更新的代码吗(更改了一行)。以上是关于点击新项目时折叠 Xamarin ListView 中的所有项目的主要内容,如果未能解决你的问题,请参考以下文章
Xamarin.Forms 不可点击的 ListView(移除选择涟漪效应)
如何在 Xamarin Forms 中滚动时折叠(隐藏或向上滑动)导航栏(标题栏)?
Xamarin Listview 仅添加 1 项我的列表中有 2 项
Xamarin表单ListView ItemTapped / ItemSelected在XAML上绑定命令