У меня действительно странная проблема с переменными областями. 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.