点击新项目时折叠 Xamarin ListView 中的所有项目

问题描述

我遇到了一个看似相对简单的问题,但我无法弄清楚。

我有一个 Xamarin ListView,它使用 ViewCell 的项目模板,如下所示:

<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 控件属性的最佳方法是什么?

我不知道该怎么做 - 任何帮助将不胜感激。

谢谢。

解决方法

更新

经过编辑以提供纯 UI 解决方案,灵感来自 get cells from ListView(或可以尝试这个 highlight the selected item

两步:

  1. 在 XAML 中将 ViewCellListView 一起定义。
<ListView x:Name="MyList" ...>
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <xct:Expander Tapped="Expander_Tapped" ...>
  1. 在 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;
        }

上一个答案:

我宁愿在模型类中操作,而不是操作视图属性。

这里的三个步骤:

  1. 在您的模型类中添加一个布尔属性。
 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));
        }
    }
  1. 点击时在您的 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;
    }
  1. 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 .}">
,

我通过在扩展器的堆栈布局上使用命令在 ViewModel 中解决了这个问题:

在 Xaml 中 - 以两个扩展器为例:

xct:Expander.Header ...

xct:Expander.Header> ...

在视图模型中:

// 变量声明

私有扩展器_openExpander = new Expander();

公共命令 OnExpanderTappedCommand { get;放; }

// 在构造函数中

OnExpanderTappedCommand = new Command((o) => OnExpanderTappedCommandExecute(o));

//命令执行方法

private void OnExpanderTappedCommandExecute(object o)
    {
        Expander currentExpanderClicked = (Expander)o;

        if (currentExpanderClicked.IsExpanded)
        {
            _openExpander.IsExpanded = false;
            _openExpander = currentExpanderClicked;
            _openExpander.IsExpanded = true;
        }
    }

也许对研究这个的人有用。我的灵感来自原始帖子的广泛逻辑。