UWP конвертировать IEnumerable в ObservableCollection (x:Bind)
Итак, у меня есть IEnumerable
Коллекция, которую я x:Bind
в ListView
,
В то время как приложение работает, я хочу всякий раз, когда это IEnumerable
список изменений, мой listview
быть обновленным также. Это не так даже при использовании INotifyPropertyChanged
поэтому я решил перейти к преобразованию IEnumerable
для ObservableCollection
,
Я узнал, что актерский состав, который существует:
myCollection = new ObservableCollection<object>(the_list);
Это будет работать нормально в другой проблеме, но в моем случае я х: привязать это к listview
и каждый раз, когда я запускаю метод с приведенным выше кодом, создается новая ссылка, и привязка не будет работать (она не работает). Это верно? Если да, как я могу обойти это? Если нет, что я делаю не так? Спасибо.
В настоящее время мой код выглядит так:
public ObservableCollection<IMessage> MessageList;
private async Task SetMessages()
{
MessageList = new ObservableCollection<IMessage>(await channel.GetMessagesAsync(NumOfMessages).Flatten());
}
Что сработало для меня:
Только благодаря ответу Мариана Долинского все работает. Я представляю некоторые из своего кода, чтобы сделать его более понятным. Я сделал следующее:
class ChatViewModel : INotifyPropertyChanged
{
//Number of messages to have in list at once.
private int NumOfMessages = 20;
//Called when a user clicks on a channel.
public async Task SelectChannel(object sender, ItemClickEventArgs e)
{
//Set the channel to clicked item
channel = (SocketTextChannel)e.ClickedItem;
//Receive the messages in the list.
IEnumerable<IMessage> data = await channel.GetMessagesAsync(NumOfMessages).Flatten();
//Clear the observablecollection from any possible previous messages (if for example you select another channel).
messageList.Clear();
//Loop and add all messages to the observablecollection
foreach (var item in data)
{
messageList.Add(item);
}
}
//The event handler when a message is received.
private async Task Message_Received(SocketMessage arg)
{
//Calls to add message only if the message received is of interest.
if (arg.Channel == channel)
{
//Sets the thread equal to the UI thread.
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//Adds the new message, removes the oldest to keep always 20.
messageList.Add(arg);
messageList.Remove(messageList[0]);
});
}
}
private ObservableCollection<IMessage> messageList = new ObservableCollection<IMessage>();
public ObservableCollection<IMessage> MessageList
{
get
{
return messageList;
}
}
}
А вот код в XAML:
<Page.DataContext>
<vm:ChatViewModel x:Name="ChatViewModel"/>
</Page.DataContext>
<ListView VerticalContentAlignment="Bottom" ItemsSource="{x:Bind ChatViewModel.MessageList, Mode=OneWay}" SelectionMode="None" ItemTemplate="{StaticResource MessageListDataTemplate}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0,0,5,5"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
В очередной раз благодарим за помощь:)
1 ответ
Это не обновляется из-за того, как ObservableCollection
работает - он уведомляет только об изменениях внутри коллекции, но при назначении MessageList
в вашем SetMessages
метод, вы создаете новый экземпляр ObservableCollection
и ListView.Source
указывает на исходный связанный экземпляр ObservableCollection
,
Кроме того, я вижу, что MessageList
это не свойство, а поле. Поля не работают с привязками.
У вас есть четыре варианта, как добиться обновления ListView
:
1) Вы можете синхронизировать существующие MessageList
с новыми данными:
private async Task SetMessages()
{
IEnumerable newData = await channel.GetMessagesAsync(20).Flatten();
// Assuming MessageList is not null here you will sync newData with MessageList
}
2) Если вы не используете MessageList
Коллекция где-либо еще, вы можете установить ItemsSource
собственностью ListView
прямо из кода:
private async Task SetMessages()
{
YourListView.ItemsSource = await channel.GetMessagesAsync(20).Flatten();
}
3) Так как вы используете x:Bind
Выражение, вы можете позвонить Bindings.Update();
Метод каждый раз, когда вы хотите обновить любую привязку на странице:
private async Task SetMessages()
{
MessageList = new ObservableCollection<IMessage>(await channel.GetMessagesAsync(20).Flatten());
Bindings.Update();
}
4) Вы могли бы реализовать INotifyPropertyChanged
на странице или создать DependencyProperty
для MessageList
а затем связать его с Mode
установлен в OneWay
:
<ListView ItemsSource="{x:Bind MessageList, Mode=OneWay}">
Лично я бы не рекомендовал четвертый вариант. Лучшим вариантом будет синхронизировать данные, так как ListView
автоматически оживляет добавление и удаление ListViewItems.
РЕДАКТИРОВАТЬ:
Я думаю, что проблема в этих двух строках:
messageList.Add(arg);
messageList.Remove(messageList[NumOfMessages - 1]);
так как новые элементы добавляются в конце коллекции, поэтому вы удаляете элемент, добавленный в прошлый раз. Для удаления самого старого элемента, вы должны использовать messageList.RemoveAt(0);
убрать предмет на первой позиции.