【问题标题】:How do I prevent the OnToggled Handler from firing in a view cell?如何防止 OnToggled 处理程序在视图单元格中触发?
【发布时间】:2020-03-26 02:15:19
【问题描述】:

我正在创建一个动态填充选项的过滤器菜单。菜单是一个分组的 ListView。每个选项都是一个具有OptionNameIsFiltered 属性的类,并且对应的Switch 绑定到IsFiltered 属性。我在此过滤器菜单上有三个按钮:一个用于清除所有过滤器,一个用于应用选定的过滤器,一个用于取消,用于丢弃自上次打开过滤器菜单以来发生的任何切换。请务必注意,过滤器菜单会“记住”您应用后切换的过滤器。

“清除”和“应用”按钮效果很好,但我无法实现“取消”按钮。本质上,它现在所做的是通过在触发切换时将该过滤器选项添加到内部集合来跟踪自打开菜单以来发生的所有切换。如果用户选择取消,它将遍历所有这些切换值并恢复它们。当我逐步完成时,逻辑效果很好。我遇到的问题是,如果之前已经应用了过滤器,那么当出现“过滤器菜单”页面时,会为每个“已选择”过滤器触发切换处理程序事件。这会导致将那些“已选择”过滤器添加到“新选择”列表中。在这种情况下,我想找到一种方法来抑制 Toggle 处理程序的触发。

我找到了几个解决方案,但没有一个能够工作。到目前为止最有希望的是option 3 in this answer,它设置了 Switch 的处理程序事件出现之后,但是由于 Switch 存在于 DataTemplate 中,我无法通过名称访问它,因为可能存在无限开关数量。

还有其他在线提供的解决方案似乎对其他人来说是成功的,但没有足够的上下文让我弄清楚如何在我的场景中实施它。作为参考,我已经包含了包含 ListView 的 xaml 代码、带有任何相关代码的 xaml 页面背后的代码,以及带有任何相关代码的 ViewModel。

相关代码如下:

ListView XAML

<ListView ItemsSource="{Binding FilterOptionList}"
                          IsGroupingEnabled="True"
                          GroupDisplayBinding="{Binding Name}"
                          HasUnevenRows="True">
                    <ListView.GroupHeaderTemplate>
                        <DataTemplate>
                            <ViewCell Height="25">
                                <StackLayout Padding="3, 20, 3, 2">
                                    <Label Text="{Binding Name}"
                                           FontSize="Small"></Label>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.GroupHeaderTemplate>
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <Grid Margin="15, 0, 40, 0">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="20"/>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <Label Text="{Binding OptionName}"
                                           FontSize="Micro"
                                           Grid.Column="1"/>
                                    <Switch x:Name="filterSwitch"
                                            IsToggled="{Binding IsFiltered, Mode=TwoWay}"
                                            Toggled="Switch_OnToggled"
                                            Grid.Column="2"/>
                                </Grid>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>

代码背后

namespace MyProject.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class FilterMenu : ContentPage
    {
        public FilterMenu(ObservableRangeCollection<MyObject> myObjects, ObservableCollection<FilterOptionGroup> filterOptionList)
        {
            InitializeComponent();
            BindingContext = new FilterMenuViewModel(Navigation, myObjects, filterOptionList);
        }

        private void CloseButton_OnClicked(object sender, EventArgs e)
        {
            (BindingContext as FilterMenuViewModel)?.Cancel();
        }

        private void ClearButton_OnClicked(object sender, EventArgs e)
        {
            (BindingContext as FilterMenuViewModel)?.ClearFilter();
        }

        private void ApplyFilterButton_OnClicked(object sender, EventArgs e)
        {
            (BindingContext as FilterMenuViewModel)?.ApplyFilter();
        }

        private void Switch_OnToggled(object sender, ToggledEventArgs e)
        {
            FilterOption filterOption = (sender as Switch).Parent.BindingContext as FilterOption;
            (BindingContext as FilterMenuViewModel)?.ToggleFilter(sender, e, filterOption);
        }
    }
}

筛选菜单视图模型

public class FilterMenuViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<MyObject> MyObjects { get; private set; }
        private readonly INavigation _navigation;
        public event PropertyChangedEventHandler PropertyChanged;
        List<FilterOption> newlyCheckedFilters = new List<FilterOption>();
        public ObservableCollection<FilterOptionGroup> FilterOptionList { get; set; } = new ObservableCollection<FilterOptionGroup>() ;

        public FilterMenuViewModel(INavigation navigation, ObservableCollection<MyObject> myObjects, ObservableCollection<FilterOptionGroup> filterOptionList )
        {
            _navigation = navigation;
            this.MyObjects = myObjects;
            FilterOptionList = filterOptionList;


            PopulateCategories();
        }

        private void PopulateFilter(string name, List<string> newOptions)
        {
            // Code to create the Filter Options list...
        }

        private void PopulateCategories()
        {
            // Code to create the Filter Options list...
        }

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        }

        public async void Cancel()
        {
            foreach (var filterOption in newlyCheckedFilters)
            {
                filterOption.IsFiltered = !filterOption.IsFiltered;
            }

            await _navigation.PopModalAsync();
        }

        public void ClearFilter()
        {
            foreach (var group in FilterOptionList)
            {
                foreach (var option in group)
                {
                    option.IsFiltered = false;
                }
            }

        }

        public async void ApplyFilter()
        {
            await _navigation.PopModalAsync();
        }

        public void ToggleFilter(object sender, ToggledEventArgs toggledEventArgs, FilterOption filterOption)
        {
            newlyCheckedFilters.Add(filterOption);
        }
    }
}

【问题讨论】:

  • 您能否与我们分享一个基本演示来重现此问题,因为通过您的描述我无法完全理解该问题?另外,我同意复制过滤器列表,然后您可以通过此列表重置它。

标签: xaml xamarin xamarin.forms event-handling toggle


【解决方案1】:

使用bool 来控制切换处理程序。完成任何初始化后将其设置为true(例如OnAppearing

    bool init = false;

    private void Switch_OnToggled(object sender, ToggledEventArgs e)
    {
        if (!bool) return;

        FilterOption filterOption = (sender as Switch).Parent.BindingContext as FilterOption;
        (BindingContext as FilterMenuViewModel)?.ToggleFilter(sender, e, filterOption);
    }

【讨论】:

  • 我在后面的代码中添加了private bool init;,在构造函数的开头将其设置为true,在设置绑定上下文后返回false。然后我将if(init) {return;} 添加到处理程序中。不幸的是,这并没有解决问题。
  • 听起来你只是在颠倒我建议的逻辑,这很好。你在哪里设置 BindingContext?
  • 在构造函数中为后面的代码设置绑定上下文:public FilterMenu(ObservableRangeCollection&lt;MyObject&gt; myObjects, ObservableCollection&lt;FilterOptionGroup&gt; filterOptionList) { InitializeComponent(); BindingContext = new FilterMenuViewModel(Navigation, myObjects, filterOptionList); }
  • 应该注意的是,在我的研究中,我发现 Handler 事件是在任何其他初始化过程之前设置的。不确定这是否有帮助。
  • 没有实际调试你的代码我不能说出了什么问题 - 但另一种方法是在构造函数中制作过滤器列表的完整副本,并在需要时将其用作参考重置
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多