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

C# 点击新项目时折叠Xamarin ListView中的所有项目,c#,xaml,listview,xamarin,xamarin.forms,C#,Xaml,Listview,Xamarin,Xamarin.forms,我有一个看似相对简单的问题,但我无法解决 我有一个Xamarin ListView,它使用ViewCells的项目模板,如下所示: <ListView HasUnevenRows="True" ItemsSource="{Binding Devices}" ItemTemplate="{StaticResource DeviceDataTemplateSelector}"

我有一个看似相对简单的问题,但我无法解决

我有一个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控件属性的最佳方式是什么

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


谢谢。

已更新

编辑以提供纯UI解决方案,灵感来自(或可以尝试此)

两个步骤:

  • 在XAML中定义
    ViewCell
    ListView
  • 单击(如果使用MVVM)时,在ViewModel中定义一个
    TapCommand

  • 我在ViewModel中通过在Expander的stacklayout上使用命令解决了这个问题:

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

    xct:Expander.Header

    xct:Expander.Header>

    在ViewModel中:

    //变量声明

    专用扩展器_openExpander=新扩展器()

    公共命令onexpandertapedcommand{get;set;}

    //在构造函数中

    OnExpanderTappedCommand=新命令((o)=>OnExpanderTappedCommandExecute(o))

    //命令执行方法

    private void OnExpanderTappedCommandExecute(object o)
        {
            Expander currentExpanderClicked = (Expander)o;
    
            if (currentExpanderClicked.IsExpanded)
            {
                _openExpander.IsExpanded = false;
                _openExpander = currentExpanderClicked;
                _openExpander.IsExpanded = true;
            }
        }
    

    也许对调查此事的人有用。我的灵感来自于原始帖子的广泛逻辑。

    当你“循环浏览列表视图中的元素”时会发生什么?您是否成功地将“IsExpanded”属性设置为false?但它实际上并没有使它们崩溃?这可能是个好主意,也可能不是个好主意。IsExpanded在模型域中可能没有任何意义;它可能只是视图状态的一部分。(仅供参考:这是数据绑定的一个更大问题的一部分;它在从模型进行简单映射方面很好,但在视图需要更复杂时就不太好了。因此,要么执行与将视图与模型分离的目标背道而驰的操作,要么需要“视图模型”每一个项目,这是笨拙的/容易出错的,因为现在有两个继承人需要保持同步。我怀疑数据绑定最终会被放弃。可能是MVU,但我还没有研究。)我同意只在大多数情况下保留逻辑(例外情况如列表需要根据数据扩展某些项目)并在未接触ViewModel的情况下更新了解决方案。不幸的是,由于各种原因,我无法使用此解决方案。我需要数据模板选择器来选择不同的视图单元格。更重要的是,我也希望尽可能多地保留MVVM。在模型中有一个扩展状态将打破这一局面。我尝试了第一种方法,但无法使其正常工作,因为将ViewCell.View强制转换为stackpanel总是会引发空异常-不确定原因。我本以为会有一个相对简单的方法来实现这一点,它遵循MVVM,但显然不是:/但是我非常感谢答案的努力!糟糕的是,我还在ViewCell上测试Tapped事件,并在那里添加了StackLayout并对逻辑进行编码。只需
    var child=cell。将视图视为扩展器就可以了,还更新了答案。嗨@DR_Bart,您是否尝试过更新的代码(更改了一行)。
    
            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));
            }
        }
    
        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;
        }
    
        <xct:Expander
            IsExpanded="{Binding Flag, Mode=TwoWay}"
            Command="{Binding Source={RelativeSource AncestorType={x:Type ViewModel:PageViewModel}, Mode=FindAncestorBindingContext}, Path=TapCommand}"
            CommandParameter="{Binding .}">
    
    private void OnExpanderTappedCommandExecute(object o)
        {
            Expander currentExpanderClicked = (Expander)o;
    
            if (currentExpanderClicked.IsExpanded)
            {
                _openExpander.IsExpanded = false;
                _openExpander = currentExpanderClicked;
                _openExpander.IsExpanded = true;
            }
        }