Search code examples
c#listviewxamarinmvvmbinding-context

Xamarin ListView ItemsSource cannot bind data inside PopupPage


I'm stuck with a strange issue. (In this code, I'm using a InputKit.Checkbox)

Followed by this tutorial, I've created view, model, and viewmodel like so:

view.xaml:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:Plugin.InputKit.Shared.Controls;assembly=Plugin.InputKit"
             x:Class="ISSO_I.PopupTypes.MultiselectListView">
    <ContentView.Content>
        <StackLayout>
            <ListView RowHeight="70" Margin="5" ItemsSource="{Binding Items}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Orientation="Horizontal">
                                <controls:CheckBox IsChecked="{Binding IsChecked}" LabelPosition="After" Margin="10"
                                                   Type="Material" Text="{Binding Body}" TextColor="Black" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <BoxView Color="Accent" HeightRequest="1" />
            <controls:CheckBox IsChecked="{Binding Path=AllChecked}" LabelPosition="After" Margin="10" Type="Material"
                               Text="(Выбрать все)" TextColor="Black" />
            <Button Style="{StaticResource ButtonStandard}" Margin="10" Text="Применить" Clicked="ButtonConfirmClicked"/>
        </StackLayout>
    </ContentView.Content>
</ContentView>

view.xaml.cs:

public partial class MultiselectListView
    {

        private readonly MultiselectListViewModel _vm;

        public event EventHandler ApplyConstrs;

        /// <summary>
        /// Для заголовка окна
        /// </summary>
        public const string Header = "Выбранные номера конструкций";

        public MultiselectListView (ObservableCollection<MultiselectItem> items)
        {
            InitializeComponent();
            //MultiListView.ItemsSource = _vm.Items;
            BindingContext = _vm = new MultiselectListViewModel(items);
        }

        protected virtual void OnApplyConstrs()
        {
            ApplyConstrs?.Invoke(_vm.Items.Where(item => item.IsChecked).ToList(), EventArgs.Empty);
        }

        private async void ButtonConfirmClicked(object sender, EventArgs e)
        {
            OnApplyConstrs();
            await Navigation.PopAsync();
        }
    }

view_model.cs:

public class MultiselectListViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// Номера конструкций
        /// </summary>
        private ObservableCollection<MultiselectItem> _items;
        public ObservableCollection<MultiselectItem> Items
        {
            get => _items;
            set
            {
                if (_items == value) return;
                _items = value;
                OnPropertyChanged(nameof(Items));
            }
        }

        private bool _allChecked;
        public bool AllChecked
        {
            get => _allChecked;
            set
            {
                if (_allChecked == value) return;
                _allChecked = value;
                OnPropertyChanged(nameof(AllChecked));
                // Меняем все галочки
                foreach (var item in Items)
                {
                    item.IsChecked = _allChecked;
                }
            }
        }

        public MultiselectListViewModel(ObservableCollection<MultiselectItem> items)
        {
            Items = items;
        }

        #region INotify Staff

        public event PropertyChangedEventHandler PropertyChanged;

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

        #endregion

    }

and finally model.cs:

public class MultiselectItem
    {

        /// <summary>
        /// Признак выбранного элемента
        /// </summary>
        public bool IsChecked { get; set; }

        /// <summary>
        /// Текст элемента
        /// </summary>
        public string Body { get; set; }
    }

When I use this code, the content in listView not showing at all. But when I aplly ItemsSource in c# code like: MultiListView.ItemsSource = _vm.Items; (commited in code), the data appears. I want to do that correctly, by using xaml.

Also, it's strange, but field AllChecked never firing in model.

So what is wrong with my code? Could anyone explain me that? Thanks in advance.


Solution

  • After some time I've found very strange result:

    I use a common PopupPage with empty ContentView in it for initializing many popup pages in two lines of code and not to copy/paste xaml code for initializing it. When I use this class with ListView and my MultiselectListView as popup content, the binding doesn't work.

    But when I created PopupPage 'from zero', binding worked, ItemsSource has appeared. It's really strange, but it worked.