#c# #xaml #xamarin #xamarin.forms #syncfusion
#c# #xaml #xamarin #xamarin.forms #синхронизация
Вопрос:
Просто немного о том, что я пытаюсь сделать, у меня есть syncfusion listview
, который обновляет данные из базы данных с помощью вызова REST api. Я получаю еженедельные данные. В представлении есть еще одна кнопка, которая должна обновлять список ежемесячными данными.
За неделю у меня есть 1 запись в БД, и список заполняется данными. После нажатия кнопки «просмотреть все» БД выдает данные, а список получает 13 записей, но представление обновляет только одну из них. Похоже, что список ограничен отображением только одного. Вот код:
private ObservableCollection<TransactionInformationDto> listItems;
public ObservableCollection<TransactionChartData> ChartData { get; set; }
public ObservableCollection<TransactionInformationDto> TransactionList
{
get { return listItems; }
set { listItems = value; OnPropertyChanged(nameof(TransactionList)); }
}
public ICommand GetTransactions => new Command(async () =>
{
IsBusy = true;
TransactionList.Clear();
var data = await GetAllTransactions();
foreach(var item in data)
{
TransactionList.Add(item);
}
IsBusy = false;
});
public Command<object> ItemTappedCommand
{
get
{
return this.itemTappedCommand ?? (this.itemTappedCommand = new Command<object>(ShowTransactionInformation));
}
}
private void ShowTransactionInformation(object item)
{
var list = item as Syncfusion.ListView.XForms.ItemTappedEventArgs;
var transaction = (TransactionInformationDto)list.ItemData;
Navigation.PushAsync(new TransactionInfoPage(transaction));
}
#endregion
#region Constructor
public DashboardPageViewModel(INavigation navigation)
{
LoadTransactionDetails();
Navigation = navigation;
}
#endregion
#region Properties
public double TotalBalance
{
get
{
return totalBalance;
}
set
{
this.totalBalance = value;
this.OnPropertyChanged();
}
}
public INavigation Navigation { get; }
#endregion
#region Methods
private async Task<ObservableCollection<TransactionInformationDto>> GetAllTransactions()
{
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
return new ObservableCollection<TransactionInformationDto>(await DependencyService.Get<IGetInformation>().GetAllUserTransactions(retrievalInformation));
}
private void LoadTransactionDetails()
{
var userTransactions = new List<TransactionInformationDto>();
Task.Run(async () => {
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
var userBalance = await DependencyService.Get<IGetInformation>().GetUserBalanceInformation(retrievalInformation);
TotalBalance = userBalance.CurrentBalance;
userTransactions = await DependencyService.Get<IGetInformation>().GetTransactionData(retrievalInformation);
});
Thread.Sleep(1000);
WeekData(userTransactions);
}
private void WeekData(List<TransactionInformationDto> transactionInformation)
{
TransactionList = new ObservableCollection<TransactionInformationDto>();
var data = new ObservableCollection<TransactionInformationDto>(transactionInformation.OrderByDescending(x =>x.TimeStamp));
foreach(var item in data)
{
TransactionList.Add(item);
}
days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
ChartData = new ObservableCollection<TransactionChartData>();
UpdateChartData(days);
}
XAML
<Grid Grid.Row="1">
<Label Margin="16,26,16,16"
Text="TRANSACTIONS"
TextColor="{StaticResource Gray-800}"
FontSize="12"
LineHeight="{OnPlatform Android=1.5, Default=-1}"
HorizontalOptions="Start" />
<buttons:SfButton Margin="11,26,11,16"
BorderWidth="0"
TextColor="{StaticResource Gray-600}"
BackgroundColor="{StaticResource Transparent}"
WidthRequest="72"
HeightRequest="18"
Command="{Binding GetTransactions}"
CornerRadius="4"
HorizontalOptions="End">
<Label Text="VIEW ALL"
TextColor="{DynamicResource Link}"
FontSize="12"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineHeight="{OnPlatform Android=1.5, Default=-1}"
/>
</buttons:SfButton>
</Grid>
<listView:SfListView Grid.Row="2"
x:Name="_transactionList"
IsScrollBarVisible="False"
ItemSpacing="0"
ItemsSource="{Binding TransactionList}"
SelectionBackgroundColor="{StaticResource TappedBackgroundColor}"
TapCommand="{Binding ItemTappedCommand}"
AutoFitMode="Height"
BackgroundColor="White">
<listView:SfListView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--Profile pic-->
<border:SfBorder Grid.RowSpan="3"
Margin="16"
WidthRequest="40"
HeightRequest="40"
CornerRadius="20"
BorderWidth="0"
VerticalOptions="Center">
<Image Aspect="Fill"
Source="receipt">
</Image>
</border:SfBorder>
<!-- Name -->
<Label Grid.Column="1"
Margin="0,15,0,4"
HorizontalOptions="Start"
Text="{Binding ReceiverName}"
LineHeight="{OnPlatform Android=1.5, Default=-1}" />
<!-- Transaction Title -->
<Label Grid.Row="1"
Grid.Column="1"
Margin="0,0,0,16"
HorizontalOptions="Start"
Text="{Binding TransactionMessage}"
TextColor="{StaticResource Gray-700}"
FontSize="12"
LineHeight="{OnPlatform Android=1.5, Default=-1}" />
<!-- Amount -->
<Label Grid.Column="1"
Margin="0,16,16,4"
HorizontalOptions="End"
TextColor="{Binding IsReceived, Converter={x:StaticResource BooleanToColorConverter}, ConverterParameter=5}"
LineHeight="{OnPlatform Android=1.5, Default=-1}">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding IsReceived, Converter={StaticResource BooleanToStringConverter}, ConverterParameter=2}" />
<Span Text=" $" />
<Span Text="{Binding TransactionAmount}" />
</FormattedString>
</Label.FormattedText>
</Label>
<!-- Date -->
<Label Grid.Row="1"
Grid.Column="1"
Margin="0,0,16,16"
HorizontalOptions="End"
Text="{Binding TimeStamp, StringFormat='{}{0:dd MMM yyyy}'}"
TextColor="{StaticResource Gray-700}"
FontSize="12"
LineHeight="{OnPlatform Android=1.5, Default=-1}" />
<!-- Seperator -->
<BoxView Grid.Row="2" Grid.ColumnSpan="2" Style="{StaticResource SeparatorStyle}" />
</Grid>
</DataTemplate>
</listView:SfListView.ItemTemplate>
</listView:SfListView>
<controls:Popup Grid.Row="2" Grid.RowSpan="1" IsBusy="{Binding IsBusy}" IsEnabled="{Binding IsBusy}" LoadingMessage="Loading the list.." />
</Grid>`
Любая помощь будет оценена.
Комментарии:
1. вы уверены , что
TransactionList
это содержит 13 элементов?2. да, я отлаживал его. LoadTransactionDetails поместит 1 элемент в список. Тогда GetAllTransactions добавит 13 элементов в список. При использовании горячей перезагрузки, если я сохраняю xaml, он обновляет пользовательский интерфейс 13 элементами
Ответ №1:
- никогда не используйте Thread .Sleep() . Он блокирует пользовательский интерфейс (пользовательский интерфейс зависает / не отвечает), если он выполняется в потоке пользовательского интерфейса.
1.1. Пытаюсь что-то исправить, добавив поток.Sleep() и задача.Delay() — это еще один шаг к аду. Не вводите этот путь!
- Вам не нужно использовать ObservableCollection везде. Если вам не нужна наблюдаемость, используйте IList или IEnumerable или массив.
- Task.Run() создает новый поток. Обычно вам это не нужно. У вас проблемы с привязкой данных, потому что вам нужно убедиться, что привязка выполняется в потоке пользовательского интерфейса, чтобы иметь возможность обновлять компонент пользовательского интерфейса.
- Не загружайте данные в конструктор. Запускает событие загрузки и жизненного цикла, например, PageAppearing() (https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/app-lifecycle )
Обновленный код
private ObservableCollection<TransactionInformationDto> listItems;
public ObservableCollection<TransactionChartData> ChartData { get; set; }
public ObservableCollection<TransactionInformationDto> TransactionList
{
get { return listItems; }
set { listItems = value; OnPropertyChanged(nameof(TransactionList)); }
}
public ICommand GetTransactions => new Command(async () =>
{
IsBusy = true;
TransactionList.Clear();
var data = await GetAllTransactions();
TransactionList = new ObservableCollection<TransactionInformationDto>(data);
IsBusy = false;
});
public Command<object> ItemTappedCommand
{
get
{
return this.itemTappedCommand ?? (this.itemTappedCommand = new Command<object>(ShowTransactionInformation));
}
}
private void ShowTransactionInformation(object item)
{
var list = item as Syncfusion.ListView.XForms.ItemTappedEventArgs;
var transaction = (TransactionInformationDto)list.ItemData;
Navigation.PushAsync(new TransactionInfoPage(transaction));
}
#endregion
#region Constructor
public DashboardPageViewModel(INavigation navigation)
{
// LoadTransactionDetails(); // <<< see 4.
Navigation = navigation;
}
#endregion
#region Properties
public double TotalBalance
{
get
{
return totalBalance;
}
set
{
this.totalBalance = value;
this.OnPropertyChanged();
}
}
public INavigation Navigation { get; }
#endregion
#region Methods
private async Task<IEnumerable<TransactionInformationDto>> GetAllTransactions() // see 2.
{
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
return await DependencyService.Get<IGetInformation>().GetAllUserTransactions(retrievalInformation);
}
private async Task LoadTransactionDetails()
{
var userTransactions = new List<TransactionInformationDto>();
// see 3.
var retrievalInformation = await App.Database.GetUserRetrievalInformation();
var userBalance = await DependencyService.Get<IGetInformation>().GetUserBalanceInformation(retrievalInformation);
TotalBalance = userBalance.CurrentBalance;
userTransactions = await DependencyService.Get<IGetInformation>().GetTransactionData(retrievalInformation);
// see. 1. amp; 1.1.
WeekData(userTransactions);
}
private void WeekData(List<TransactionInformationDto> transactionInformation)
{
TransactionList = new ObservableCollection<TransactionInformationDto>(transactionInformation.OrderByDescending(x =>x.TimeStamp));
days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
ChartData = new ObservableCollection<TransactionChartData>();
UpdateChartData(days);
}
Комментарии:
1. Спасибо, Свен, я попробую и дам вам знать. Я уверен, что способ, которым я занимался, был немного хакерским, но это был единственный способ работать.
2. Большое спасибо, Свен, я решил проблему. Это было связано с 2 вещами. Первым был Task.Run(), который создавал другой поток, а не обновлял пользовательский интерфейс. Второй была привязка данных библиотеки syncfusion. Я изменил его на обычный ListView, и он работает. Еще раз спасибо
Ответ №2:
Задача.Метод Run будет выполняться в отдельном потоке, который пытается обновить пользовательский интерфейс. Макет будет обновляться при добавлении элементов в основной поток. Потоки пользовательского интерфейса прерываются, что ограничивает макеты в приложении.
https://docs.microsoft.com/en-us/xamarin/ios/user-interface/ios-ui/ui-thread#background-thread-example
https://forums.xamarin.com/discussion/comment/96756