В нашем приложении Xamarin у нас есть ListView, который показывает список Surveys.If пользователь выбирает опрос, они будут отправлены на эту страницу данных опросов. Для этого мы используем привязку TwoWay
к свойству SelectedItem
и добавим установщик в свойство SelectedItem в контроллере.Xamarin ListView привязка TwoWay не работает на iOS
Xaml-
<ListView x:Name= "surveyList" ItemsSource= "{Binding Surveys}" SelectedItem= "{Binding SelectedSurvey, Mode=TwoWay}" BackgroundColor= "White" HorizontalOptions= "Fill" SeparatorColor= "Gray" RowHeight= "50" >
C# -
private SurveyListItem _selectedSurvey;
public SurveyListItem SelectedSurvey
{
get { return _selectedSurvey; }
set
{
_selectedSurvey = value;
if (_selectedSurvey == null)
{
NotifyPropertyChanged();
return;
}
OnSurveySelected(_selectedSurvey);
_selectedSurvey = null;
NotifyPropertyChanged();
}
}
Это работает найти на Android
, но не работает на iOS
. При нажатии на элемент в списке не устанавливается _selectedSurvey
или вызывается установщиком SelectedSurvey
.
Я мог бы быстро исправить ситуацию и изменить ее на какой-то жест нажатия, но мы используем ListView в других местах приложения, где требуется многосеансовый выбор, и изменение всего этого на жест нажатия будет больно.
Любые идеи, почему это будет работать для Android
, но не iOS
?
Edit-
Полный Xaml-
<?xml version = "1.0" encoding= "utf-8" ?>
< TabbedPage xmlns = "http://xamarin.com/schemas/2014/forms"
xmlns:x= "http://schemas.microsoft.com/winfx/2009/xaml"
x:Class= "MyApp.View.ModuleContentPage"
xmlns:vm= "clr-namespace:MyApp.ViewModel;assembly=MyApp"
xmlns:local= "clr-namespace:MyApp.View;assembly=MyApp"
Title= "Module Content Render" >
<TabbedPage.Children>
< ContentPage Title= "Summary" IsEnabled= "False" >
<ContentPage.Content>
< StackLayout Padding= "20, 20, 20, 0" >
< Label Text= "To Do: Render Module Content Here." ></ Label >
</ StackLayout >
</ ContentPage.Content >
</ ContentPage >
< ContentPage Title= "Related" >
<ContentPage.Content>
< StackLayout Padding= "20, 20, 20, 0" >
< Label Text= "To Do: Render Related Content Here." ></ Label >
</ StackLayout >
</ ContentPage.Content >
</ ContentPage >
< ContentPage Title= "Surveys" IsEnabled= "False" >
<ContentPage.Content>
< StackLayout Padding= "20" >
< ListView x:Name= "surveyList" ItemsSource= "{Binding Surveys}" SelectedItem= "{Binding SelectedSurvey, Mode=TwoWay}" BackgroundColor= "White" HorizontalOptions= "Fill" SeparatorColor= "Gray" RowHeight= "50" >
<ListView.Header>
< StackLayout Padding= "0, 0, 0, 10" VerticalOptions= "Center" >
< Label Text= "Surveys" FontSize= "20" TextColor= "Gray" LineBreakMode= "NoWrap" />
</ StackLayout >
</ ListView.Header >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
< StackLayout VerticalOptions= "Center" >
< Grid ColumnSpacing= "20" >
<Grid.RowDefinitions>
< RowDefinition Height= "*" />
</ Grid.RowDefinitions >
<Grid.ColumnDefinitions>
< ColumnDefinition Width= "*" />
< ColumnDefinition Width= "50" />
</ Grid.ColumnDefinitions >
< Label Text= "{Binding HydratedSurvey.Name}" FontSize= "12" TextColor= "Black" FontAttributes= "Bold" Grid.Row= "0" Grid.Column= "0" />
< Label Text= "{Binding SurveyInstanceCount}" FontSize= "12" TextColor= "Green" FontAttributes= "Bold" Grid.Row= "0" Grid.Column= "1" />
</ Grid >
</ StackLayout >
</ ViewCell.View >
</ ViewCell >
</ DataTemplate >
</ ListView.ItemTemplate >
</ ListView >
</ StackLayout >
</ ContentPage.Content >
</ ContentPage >
</ TabbedPage.Children >
</ TabbedPage >
Код Behind-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace MyApp.View
{
public partial class ModuleContentPage : TabbedPage
{
private Page PreviousPage { get; set; }
public ModuleContentPage()
{
InitializeComponent();
// TODO hard coded to disabled the first two tabs and select Surveys. Remove when other tabs are finished
DisableTab(Children[0]);
DisableTab(Children[1]);
//Children[0].IsEnabled = false;
//Children[1].IsEnabled = false;
//PreviousPage = Children[2];
//CurrentPage = PreviousPage;
CurrentPage = Children[2];
CurrentPageChanged += ModuleContentPage_CurrentPageChanged;
PagesChanged += ModuleContentPage_PagesChanged;
}
private void DisableTab(Page page)
{
page.IsEnabled = false;
//page.Unfocus();
page.Opacity = 50.0;
}
private void ModuleContentPage_PagesChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CurrentPage = Children[2];
}
private void ModuleContentPage_CurrentPageChanged(object sender, EventArgs e)
{
CurrentPage = Children[2];
}
}
}
** Просмотр модели - **
using Newtonsoft.Json;
using MyApp.DataModel.TransferObjects;
using MyApp.DataAccess.UoW;
using MyApp.Services;
using MyApp.SQLiteAccess.Repository;
using MyApp.SQLiteAccess.Tables;
using MyApp.ViewModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace MyApp.ViewModel
{
public class SurveyListItem : ViewModelBase
{
public SurveyDTO HydratedSurvey { get; set; }
private int _surveyInstanceCount;
public int SurveyInstanceCount { get { return _surveyInstanceCount; } set { _surveyInstanceCount = value; NotifyPropertyChanged(); } }
}
public class ModuleContentPageViewModel : ViewModelBase
{
private ModuleHydratedDTO _module;
public ModuleHydratedDTO Module { get { return _module; } set { _module = value; NotifyPropertyChanged(); } }
private ModuleContentHydratedDTO _moduleContent;
public ModuleContentHydratedDTO ModuleContent { get { return _moduleContent; } set { _moduleContent = value; NotifyPropertyChanged(); } }
SqlSurveyInstanceRepository _sqlSurveyInstanceRepository;
SqlSurveyInstanceRepository SqlSurveyInstanceRepository { get { return _sqlSurveyInstanceRepository ?? (_sqlSurveyInstanceRepository = new SqlSurveyInstanceRepository()); } }
private ObservableCollection<SurveyListItem> _surveys;
public ObservableCollection<SurveyListItem> Surveys { get { return _surveys; } set { _surveys = value; NotifyPropertyChanged(); } }
public ModuleContentPageViewModel(ModuleHydratedDTO module, ModuleContentHydratedDTO moduleContent) : base()
{
_module = module;
_moduleContent = moduleContent;
_surveys = GetSurveys();
MessagingCenter.Subscribe<Stores.SurveyStore, SurveyDTO>(this, "UpdateSurveyInstanceCount", (sender, hydratedSurvey) =>
{
SurveyListItem survey = _surveys.FirstOrDefault(s => s.HydratedSurvey.SurveyId == hydratedSurvey.SurveyId);
if (survey != null)
{
survey.SurveyInstanceCount = UoW.SurveyInstances.GetCountForSurveyIdAsync(hydratedSurvey.MasterSurveyId ?? hydratedSurvey.SurveyId, ModuleContent.ModuleContentId).Result;
NotifyPropertyChanged();
}
});
}
private ObservableCollection<SurveyListItem> GetSurveys()
{
ObservableCollection<SurveyListItem> surveyList = new ObservableCollection<SurveyListItem>();
List<SurveyDTO> surveys = UoW.Surveys.GetHydratedSurveysForUser(_module.Module.ModuleId);
if (surveys.Count > 0)
{
foreach (var survey in surveys)
{
SurveyListItem item = new SurveyListItem();
item.HydratedSurvey = survey;
item.SurveyInstanceCount = UoW.SurveyInstances.GetCountForSurveyIdAsync(survey.MasterSurveyId.HasValue ? survey.MasterSurveyId.Value : survey.SurveyId, ModuleContent.ModuleContentId).Result;
surveyList.Add(item);
}
}
return surveyList;
}
private SurveyListItem _selectedSurvey;
public SurveyListItem SelectedSurvey
{
get { return _selectedSurvey; }
set
{
_selectedSurvey = value;
if (_selectedSurvey == null)
{
NotifyPropertyChanged();
return;
}
OnSurveySelected(_selectedSurvey);
_selectedSurvey = null;
NotifyPropertyChanged();
}
}
private void OnSurveySelected(SurveyListItem selectedSurvey)
{
NavigationService.PushAsync(new SurveyInstanceListVM(selectedSurvey.HydratedSurvey, _moduleContent.ModuleContentId), selectedSurvey.HydratedSurvey.Name);
}
}
}
Надежда, что помогает
Редактировать Edit-
Вот frankenstiened ViewModel/viewmodelbase сочетание.
using MyApp.DataModel.TransferObjects;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using MyApp.ViewModelFramework.Spooling;
namespace MyApp.ViewModel
{
public class SurveyListItem
{
public string Name { get { return "foo"; } set { var x = value; } }
private int _surveyInstanceCount;
public int SurveyInstanceCount { get { return _surveyInstanceCount; } set { _surveyInstanceCount = value; } }
}
public class ModuleContentPageViewModel
{
private ObservableCollection<SurveyListItem> _surveys;
public ObservableCollection<SurveyListItem> Surveys { get { return _surveys; } set { _surveys = value; NotifyPropertyChanged(); } }
public ModuleContentPageViewModel(ModuleHydratedDTO module, ModuleContentHydratedDTO moduleContent) : base()
{
_surveys = GetSurveys();
}
private ObservableCollection<SurveyListItem> GetSurveys()
{
ObservableCollection<SurveyListItem> surveyList = new ObservableCollection<SurveyListItem>();
surveyList.Add(new SurveyListItem());
return surveyList;
}
private SurveyListItem _selectedSurvey;
public SurveyListItem SelectedSurvey
{
get { return _selectedSurvey; }
set
{
_selectedSurvey = value;
if (_selectedSurvey == null)
{
NotifyPropertyChanged();
return;
}
_selectedSurvey = null;
NotifyPropertyChanged();
}
}
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
InvokeNotifyPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
public NotifyPropertyChangedSpooler NotificationSpooler = new NotifyPropertyChangedSpooler();
private NotificationPropertyChangedStateEnum _notificationPropertyChangedState = NotificationPropertyChangedStateEnum.Active;
// Gets and sets change notification spooling and blocking features.
public NotificationPropertyChangedStateEnum NotificationPropertyChangedState
{
get { return _notificationPropertyChangedState; }
set
{
_notificationPropertyChangedState = value;
if (_notificationPropertyChangedState == NotificationPropertyChangedStateEnum.Active && !NotificationSpooler.IsEmpty)
{
NotificationSpooler.Unwind();
}
NotifyPropertyChanged();
}
}
void InvokeNotifyPropertyChanged(string propertyName)
{
switch (NotificationPropertyChangedState)
{
case NotificationPropertyChangedStateEnum.Active:
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
break;
case NotificationPropertyChangedStateEnum.Spooling:
NotificationSpooler.Enqueue(this, propertyName);
break;
case NotificationPropertyChangedStateEnum.Inactive:
break;
}
}
}
public enum NotificationPropertyChangedStateEnum
{
Inactive = 0,
Active = 1,
Spooling = 3
}
}
Как вы узнаете о нас? Это просто список или ObservableCollection? Больше кода и xaml помогут. В идеале, если вы можете опубликовать полный код этой страницы и кода, за которым стоит модель, мы можем быстрее помочь. –
Это ObservableCollection. Я обновил страницу xaml, код позади и посмотрю модель –
Попытка ... Вы тестируете на устройстве или симуляторе? Какая версия iOS? –