UWP 将 IEnumerable 转换为 ObservableCollection (x:Bind)
UWP Convert IEnumerable to ObservableCollection (x:Bind)
好的,所以我有一个 IEnumerable
集合,我 x:Bind
变成了 ListView
。
当应用程序 运行 正在运行时,我希望每当这个 IEnumerable
列表发生变化时,我的 listview
也会更新。即使使用 INotifyPropertyChanged
也不是这种情况,所以我决定将 IEnumerable
转换为 ObservableCollection
。
我发现存在的演员表是:
myCollection = new ObservableCollection<object>(the_list);
这在不同的问题中会很好地工作,但在我的情况下我是 x:Bind 这个到 listview
并且每次我 运行 使用上面代码的方法一个新的引用已创建,绑定将不起作用(它不起作用)。那是对的吗 ?如果是,我该如何解决这个问题?如果不是,我做错了什么?谢谢。
目前我的代码如下所示:
public ObservableCollection<IMessage> MessageList;
private async Task SetMessages()
{
MessageList = new ObservableCollection<IMessage>(await channel.GetMessagesAsync(NumOfMessages).Flatten());
}
对我有用的:
完全感谢 Marian Dolinský 的回答,现在一切正常。我展示了一些我的代码以使其更清楚我所做的是:
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>
再次感谢您的帮助:)
由于 ObservableCollection
的工作方式,它没有更新 - 它仅通知集合内部的更改,但是当您在 SetMessages
方法中分配 MessageList
时,您创建 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
集合,您可以直接从代码中设置 ListView
的 ItemsSource
属性:
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
或为 MessageList
创建 DependencyProperty
然后将其绑定 Mode
设置为 OneWay
:
<ListView ItemsSource="{x:Bind MessageList, Mode=OneWay}">
我个人不推荐第四种选择。最好的选择是同步数据,因为 ListView
自动动画添加和删除 ListViewItems。
编辑:
我认为问题出在这两行:
messageList.Add(arg);
messageList.Remove(messageList[NumOfMessages - 1]);
由于新项目是在集合的末尾添加的,因此您将删除上次添加的项目。要删除最旧的项目,您应该使用 messageList.RemoveAt(0);
删除第一个位置的项目。
好的,所以我有一个 IEnumerable
集合,我 x:Bind
变成了 ListView
。
当应用程序 运行 正在运行时,我希望每当这个 IEnumerable
列表发生变化时,我的 listview
也会更新。即使使用 INotifyPropertyChanged
也不是这种情况,所以我决定将 IEnumerable
转换为 ObservableCollection
。
我发现存在的演员表是:
myCollection = new ObservableCollection<object>(the_list);
这在不同的问题中会很好地工作,但在我的情况下我是 x:Bind 这个到 listview
并且每次我 运行 使用上面代码的方法一个新的引用已创建,绑定将不起作用(它不起作用)。那是对的吗 ?如果是,我该如何解决这个问题?如果不是,我做错了什么?谢谢。
目前我的代码如下所示:
public ObservableCollection<IMessage> MessageList;
private async Task SetMessages()
{
MessageList = new ObservableCollection<IMessage>(await channel.GetMessagesAsync(NumOfMessages).Flatten());
}
对我有用的:
完全感谢 Marian Dolinský 的回答,现在一切正常。我展示了一些我的代码以使其更清楚我所做的是:
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>
再次感谢您的帮助:)
由于 ObservableCollection
的工作方式,它没有更新 - 它仅通知集合内部的更改,但是当您在 SetMessages
方法中分配 MessageList
时,您创建 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
集合,您可以直接从代码中设置 ListView
的 ItemsSource
属性:
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
或为 MessageList
创建 DependencyProperty
然后将其绑定 Mode
设置为 OneWay
:
<ListView ItemsSource="{x:Bind MessageList, Mode=OneWay}">
我个人不推荐第四种选择。最好的选择是同步数据,因为 ListView
自动动画添加和删除 ListViewItems。
编辑:
我认为问题出在这两行:
messageList.Add(arg);
messageList.Remove(messageList[NumOfMessages - 1]);
由于新项目是在集合的末尾添加的,因此您将删除上次添加的项目。要删除最旧的项目,您应该使用 messageList.RemoveAt(0);
删除第一个位置的项目。