WPF -绑定ObservableCollection到ListView创建闪烁当鼠标在一个项目上



我有一个ObservableCollection绑定到ListView约190项。每一项都是从一个websocket更新的,这意味着新的信息不断地到来。当我用鼠标浏览列表时,ListView闪烁,如果我用键盘滚动列表,它在每次更新后再次从第一个项目开始。我的假设是,每次更新来了,我创建了一个新的ObservableCollection,所以ListView从满到空(闪烁)再到满,但当我点击一个项目,它的焦点和焦点保持不闪烁,它按预期更新。是什么导致的呢?下面是我的代码:

ViewModel

public List<Tickers> ListTickers = new List<Tickers>();
private WSFuturesResponse _futuresResponse;
public WSFuturesResponse FuturesResponse
{
get
{
return _futuresResponse;
}
set
{
_futuresResponse = value;
OnPropertyChanged("FuturesResponse");
foreach (var future in ListTickers)
{
if (future.Market == FuturesResponse.market)
{
future.Price = FuturesResponse.data.last;
}
}
Tickers = new ObservableCollection<Tickers>(ListTickers);
}
}
private ObservableCollection<Tickers> _tickers;
public ObservableCollection<Tickers> Tickers
{
get
{
return _tickers;
}
set
{
_tickers = value;
OnPropertyChanged("Tickers");
}
}
// the method that fills the ListTickers
private async void StartConnection(Ticker wsApi, Client client, FtxRestApi api)
{
// get all futures data from the API
var futures = await api.GetAllFuturesAsync();
// parse the data
ApiFuturesResponse all_futures = JsonConvert.DeserializeObject<ApiFuturesResponse>(futures);
wsApi.OnWebSocketConnect += () =>
{
wsApi.SendCommand(FtxWebSocketRequestGenerator.GetAuthRequest(client));
foreach (ApiFuturesData future in all_futures.result)
{
if (future.name.Contains("PERP"))
{
ListTickers.Add(new Tickers(future.name));
wsApi.SendCommand(FtxWebSocketRequestGenerator.GetSubscribeRequest("ticker", future.name));
}
}
};
await wsApi.Connect();
}
// the event that updates the properties
public void WebsocketOnMessageReceive(object o, MessageReceivedEventArgs messageReceivedEventArgs)
{
FuturesResponse = JsonConvert.DeserializeObject<WSFuturesResponse>(messageReceivedEventArgs.Message);
}

XAML

<ListView Grid.Row="2"
Grid.Column="1"
ItemsSource="{Binding Tickers}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="TextBlockMarket" Grid.Column="0" Width="100" Text="{Binding Market}"/>
<TextBlock x:Name="TextBlockPrice" Grid.Column="1" Width="100" Text="{Binding Price}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
  1. 更换ListView的数据源导致的不良体验。将一个新实例分配给source属性总是会导致ItemsControl的完全重置。如果集合足够大或者ItemsControl不支持UI虚拟化,您将体验到严重的性能影响,如缓慢的UI甚至会冻结。
    使用ObservableCollection的原因是它允许修改源集合,而数据绑定引擎可以侦听更改以触发ItemsControl更新自己。如果您替换完整的集合实例,那么ObservableCollection实际上是冗余的。

  2. 此外,由于您已经在foreach中枚举了新的源集合ListTickers,因此将每个项直接添加到Tickers集合将进一步提高性能,因为在将集合传递给ObservableCollection的构造函数时消除了第二个枚举。因此,直接修改Tickers集合(在同一迭代中)将解决两个与性能相关的问题。

  3. 一个严重的bug来源是你的async方法StartConnection返回voidasync方法必须总是返回一个Task,这样它们才能被正确地等待。唯一的异常是事件处理程序,因为它们必须是void.
    因为您当前的实现返回void,您显然没有await它。但是,您必须始终等待async方法,以避免意外行为。

  4. 此外,保持属性不受长时间运行任务或枚举的影响。在您的示例中,请考虑将ListTickers集合的枚举和Tickers集合的更新移动到WebsocketOnMessageReceive事件处理程序。

  5. 你总是想保持项目的视觉树,即ItemTemplate简单,以提高渲染性能。当前定义了三列,但只使用了两列。考虑使用HorizontalAlignment代替。Grid也是一种昂贵的面板。DockPanel或StackPanel都比较便宜。还要考虑从TextBlock元素中删除元素名,因为它们强制隐式地创建字段。

  6. 不清楚您正在使用什么API发送请求。但它看起来像你在混合api。很难相信异步API要求您使用事件来获得结果。也许你该看看这个。等待Connect()应该取代OnWebSocketConnect事件。

  7. 使用。netJsonSerializer.DeserializeAsyncSystem.Text.Json命名空间通常会给你异步JSON处理:如何在。net中序列化和反序列化(封送和反封送)JSON

始终修改现有的ObservableCollection实例,而不是替换它:

public WSFuturesResponse FuturesResponse
{
get => _futuresResponse;
set
{
_futuresResponse = value;
// Use 'nameof' to avoid typos and to enable the usage of refactoring tools 
// e.g. to rename the property.
OnPropertyChanged(nameof(FuturesResponse));
// Clear the data source and reuse it
Tickers.Clear();
int preloadCount = 20;
// Take as much as possible, but not more than 'preloadCount'
AddTickers(ListTickers.Take(preloadCount));
// Defer adding the remaining items until the ItemsSource has been rendered.
Application.Current.Dispatcher.InvokeAsync(
() => AddTickers(ListTickers.Skip(preloadCount)), 
DispatcherPriority.Background);
}
private void AddTickers(IEnuemrable<Tickers> tickers)
{
foreach (var future in tickers)
{
if (future.Market == FuturesResponse.market)
{
future.Price = FuturesResponse.data.last;
}
Tickers.Add(future);
}
}
}

相关内容

  • 没有找到相关文章

最新更新