最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Invoking ICollectionView.Refresh() from non-UI thread - Stack Overflow

programmeradmin3浏览0评论

I prefer using ICollectionView.Refresh() on an arbitrary collection instead of using ObservableCollection<T> as most of the time I know where the underlying collection changes and prefer to minimise notifications. This worked pretty well until I started to use async and noticed that it does not play nicely with ICollectionView.Refresh() if I set ConfigureAwait(false). With ConfigureAwait(true) it refreshes nicely, of course.

I understand why it does not work with ConfigureAwait(false): the synchronisation context is not restored and the refresh operation is called from a non-UI thread. I, however, should expect it to throw an exception but instead it fails silently and the UI is not updated.

What intrigues me is that the usual technique to marshal the call to the UI thread by Application.Current.Dispatcher.Invoke() also fails in this case. No exception occurs but the UI does not update. Why? How can I ensure that refreshing the ICollectionView works even where I fet to configure the synchronisation context properly?

MWE:

using System.ComponentModel;
using System.Windows;
using System.Windows.Data;

namespace ICollectionViewRefresh;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private readonly List<int> list = new List<int>();

    public ICollectionView List => CollectionViewSource.GetDefaultView(list);

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    private async void HandleButtonClick(object sender, RoutedEventArgs e)
    {
        list.Clear();
        await Task.Run(() => {
            for (int i = 0; i < 128; i++) {
                list.Add(Random.Shared.Next(1, 20));
            }
        }).ConfigureAwait(false);   // Only updates with .ConfigureAwait(true) even with MarshallingRefresh().
        // List.Refresh() silently fails with .ConfigureAwait(false) so let us try to marshal it to the UI thread:
        MarshallingRefresh(List);
    }

    private void MarshallingRefresh(ICollectionView collectionView) => Application.Current.Dispatcher.Invoke(() => collectionView.Refresh());
}

with the XAML

<Window x:Class="ICollectionViewRefresh.MainWindow"
        xmlns=";
        xmlns:x=";
        xmlns:d=";
        xmlns:mc=";
        xmlns:local="clr-namespace:ICollectionViewRefresh"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <Button Content="Fill list" Click="HandleButtonClick" />
        </StackPanel>
        <ListBox Grid.Row="1" ItemsSource="{Binding List}" />
    </Grid>
</Window>

Update: Upon @Sinatr 's advice, I replaced the method call MarshallingRefresh(List) with a direct lambda call Application.Current.Dispatcher.Invoke(() => List.Refresh()); and it does update the UI. But with a method name instead of a lambda, ie, Application.Current.Dispatcher.Invoke(List.Refresh);, it does not update.

发布评论

评论列表(0)

  1. 暂无评论