Область переменных Xamarin XAML не работает должным образом

У меня действительно странная проблема с переменными областями. Listview с именем «TodoListView» определяется через xaml, а его ItemSource заполняется из базы данных SQListe. Работает. Внутри ListView у меня есть ViewCell для отображения данных по строкам.

<ContentPage ... x:Class="JanitorPro.MainPage" ...>
  <StackLayout>
        <ListView x:Name="TodoListView" Margin="20" ItemSelected="OnListItemSelected">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
                            <Label Text="{Binding name}" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" />
                            <Switch HorizontalOptions="End" IsToggled="{Binding done}" Toggled="DoneSwitchToggled"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>

The codebehind looks like this (some irrelevant portions removed):

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();

        // load Database
        TodoListView.ItemsSource = await App.TodoDatabase.GetItemsAsync("SELECT * FROM [TodoItem]");
    }

    async void OnReloadButtonClicked(object sender, EventArgs e)
    {
        Debug.WriteLine("Reload Button Click");
        TodoListView.ItemsSource = await App.TodoDatabase.GetItemsAsync("SELECT * FROM [TodoItem]");
            Debug.WriteLine("Reload done.");
    }


    async void OnListItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        if (e.SelectedItem != null)
        {
            await Navigation.PushAsync(new TodoItemPage
            {
                BindingContext = e.SelectedItem as TodoItem
            });
        }
    }

    private void DoneSwitchToggled(object sender, ToggledEventArgs e)
    {
        // TodoItem i = null; 
        TodoItem i = TodoListView.SelectedItem;
        if (i != null)
        {
            Debug.WriteLine("Toggle: {0}", i.id);
        }
    }
}

}

Странность имеет две стадии. Прежде чем я вставил обработчик событий DoneSwitchToggled, каждое вхождение TodoListView.ItemsSource подчеркивалось красным под TodoListView и подсказкой, что «имя TodoListView не существует в текущем контексте». Хорошо, я подумал, что VS недостаточно умен, чтобы найти определение в файле xaml, потому что, несмотря на предупреждение, программа скомпилировалась и заработала нормально. TodoListView инициализируется и правильно отображает строки базовой базы данных, поэтому он явно существует во время выполнения.

Когда я добавил обработчик событий DoneSwitchToggled как в XAML, так и в программный код, все пошло наперекосяк. Внезапно программа больше не будет компилироваться, но выйдет из строя с ошибкой CS0103 «Имя« TodoListView »не существует в текущем контексте». Ошибка появляется три раза, причем номера строк указывают на другие вхождения TodoListView в onAppearing() и OnReloadButtonClicked(). Хм? Как, черт возьми, добавление ссылки на переменную в обработчике событий может сделать эту переменную недействительной в совершенно разных методах? Хорошо, раньше с переменной было что-то подозрительное (до сих пор не знаю, что...), но это сработало. Теперь этого больше нет, что не имеет для меня никакого смысла. Кроме того, если я закомментирую оскорбительную строку в обработчике события DoneSwitchToggled и вставлю фиктивное определение для i, например:

        TodoItem i = null; 
        // TodoItem i = TodoListView.SelectedItem;

все как раньше, VS по-прежнему подчеркивает другие появления TodoListView, но теперь программа снова собирается и работает нормально.

Кто-нибудь, кто может объяснить этот эффект и показать мне, как правильно мой код? Я думаю, что цель ясна: DoneSwitchToggled должен записать значение переключателя обратно в базу данных (и выполнить некоторые другие действия, не показанные в моем урезанном образце), и хотя объект «отправитель» правильно установлен для ссылки на мою кнопку, я не нашел способа получить доступ к базовой привязке данных, поскольку ToggledEventArgs, к сожалению, похоже, передает только положение переключателя «true» или «false», но, в отличие от обработчика событий OnListItemSelected, не передает никакой ссылки на связанную строку через второй аргумент. Поэтому моя идея состояла в том, чтобы использовать для этой цели ListView.SelectedItem.


person Nimral    schedule 28.02.2018    source источник
comment
После некоторого тестирования это должно действительно работать, попробуйте очистить проект от всего, что не является вашим кодом, и пересобрать его.   -  person Ivan Ičin    schedule 28.02.2018


Ответы (1)


Наконец-то я сам это понял. Это похоже на сбой в VS 2017. В TodoListView нет ничего плохого, поэтому ошибка CS0103 является вводящей в заблуждение чушью.

Что на самом деле означает VS, так это ошибка CS0266. TodoListView определяется общим списком

List<TodoItem>

чтобы получить доступ к SelectedItem, мне нужно привести его к типу:

TodoItem i = (TodoItem)TodoListView.SelectedItem;

Это добавило, все ошибки исчезли, код приложения строится нормально.

Кстати, к сожалению, этот подход для получения элемента, в котором был перевернут переключатель, оказался неработающим, TodoListView всегда возвращает значение null для SelectedItem, кажется, что элемент управления ListView не видит нажатия кнопки. Нужно найти другой способ найти элемент списка под переключателем, чтобы получить идентификатор связанной строки.

person Nimral    schedule 28.02.2018