FileInformation.RenameAsync() разрывает привязку по сравнению с StorageFile.RenameAsync()

Что заставляет следующий код разрывать привязку FileInformation DataTemplate к ListView при переименовании выбранного элемента? И затем, как получить правильное уведомление о том, что представление FileInformation было переименовано?

Рассмотрим папку с 2 изображениями, 1.jpg и 2.jpg. Эта папка индексируется Windows 10 1909, а приложение UWP — версии 1903.

После того, как пользователь выбрал папку, мы создаем FileInformationFactory, извлекаем объект VirtualizedFiles и устанавливаем его в качестве источника CollectionViewSource. Представление последнего заполняет ICollectionView, который OneWay привязан к ItemsSource ListView.

Мы отображаем в списке каждый файл Name и файл FolderRelativeId из FileInformation. Кнопка переименования берет ListView.Selectedititem и переименовывает его в 1.jpg с параметром GenerateUniqueName.

Есть два метода под рукой.

1) Наиболее естественным является использование кода FileInformation.RenameAsync(), так как выбранный элемент уже является FileInformation.

2) Получите Storagefile из folder.GetFileAsync, где параметр name задается FileInformation.Name, а затем вызовите StorageFile.RenameAsync.

В обоих методах, когда мы переименовываем файл, список обновляется, как и ожидалось. Однако это длится недолго, так как я продолжаю переименовывать, даже если даю время для переименования файла. Действительно, в первом сценарии я могу переименовать файл, но, поскольку я продолжаю переименовывать, в какой-то неясный момент список кажется привязанным к предыдущему переименованному имени и не соответствует FolderRelativeId. Например, имя файла отображается как «1 (2).jpg», а FoldeRelativeId заканчивается на «1 (3).jpg.jpg». Представление списка не распознает элемент как часть ListView, поскольку я больше не могу его выбрать, а переименование вызывает перехваченное исключение.

Эта проблема не появляется в сценарии 2, почему? Как я могу использовать сценарий 1 (т.е. придерживаться FileInformation для переименования) и поддерживать уведомления, чтобы обновить пользовательский интерфейс без этой ошибки?

Дополнительный вопрос будет заключаться в том, как сохранить выбранный элемент таким же после переименования файла, потому что часто (не всегда!) выбранный элемент теряется (index=-1), вероятно, из-за того, что ICollectionview был сброшен из-за уведомлений от файловая система.

Чтобы увидеть ошибку, раскомментируйте строку в событии RenameButtonClick (и прокомментируйте вызов переименования из файла хранилища несколькими строками выше). await fileInformation.RenameAsync("1.jpg", NameCollisionOption.GenerateUniqueName);

Любая помощь приветствуется, так как я борюсь с этой проблемой уже несколько дней. Спасибо

public sealed partial class Scenario5 : Page, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
      => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    public Scenario5()
    {
        this.InitializeComponent();
    }

    private ICollectionView _fileCollectionView;
    private StorageFolder _folder;
    private QueryOptions _queryOptions;
    private StorageFileQueryResult _query;
    private FileInformationFactory _fileInformationFactory;

    public CollectionViewSource CollectionViewSource { get; set; } = new CollectionViewSource();
    public ICollectionView ItemCollectionView
    {
        get { return _fileCollectionView; }
        set
        {
            if (_fileCollectionView != value)
            {
                _fileCollectionView = value;
                OnPropertyChanged(nameof(ItemCollectionView));
            }
        }
    }
    public ObservableCollection<string> Information { get; private set; } = new ObservableCollection<string>();
    private async void FolderPickerButton_Click(object sender, RoutedEventArgs e)
    {
        var _pickedFolder = await PickFolderAsync();
        if (_pickedFolder == null)
        {
            return;
        }
        Information.Clear();
        _folder = _pickedFolder;
        _queryOptions = new QueryOptions
        {
            FolderDepth = FolderDepth.Deep,
            IndexerOption = IndexerOption.UseIndexerWhenAvailable,
        };

        _query = _folder.CreateFileQueryWithOptions(_queryOptions);

        _fileInformationFactory = new FileInformationFactory(_query, ThumbnailMode.SingleItem, 160,
            ThumbnailOptions.UseCurrentScale, delayLoad: false);

        var _vector = _fileInformationFactory.GetVirtualizedFilesVector();

        CollectionViewSource.Source = _vector;
        ItemCollectionView = CollectionViewSource.View;


    }

    private static async Task<StorageFolder> PickFolderAsync()
    {
        var folderPicker = new FolderPicker
        {
            SuggestedStartLocation = PickerLocationId.Desktop,
            ViewMode = PickerViewMode.Thumbnail
        };

        folderPicker.FileTypeFilter.Add("*");

        var _pickedFolder = await folderPicker.PickSingleFolderAsync();
        return _pickedFolder;
    }

    private void ListView_ItemClick(object sender, ItemClickEventArgs e)
    {
        if (e.ClickedItem is FileInformation fileInformation)
        {
            Information.Add($"Click {fileInformation.Name}\n{fileInformation.FolderRelativeId}");
        }
    }

    private async void RenameButton_Click(object sender, RoutedEventArgs e)
    {
        if (itemCollectionGridView.SelectedItem is FileInformation fileInformation)
        {
            Information.Add($"Selected item: {fileInformation.Name}\n{fileInformation.FolderRelativeId}");

            try
            {
                var storageFile = await _folder.GetFileAsync(fileInformation.Name);
                await storageFile.RenameAsync("1.jpg", NameCollisionOption.GenerateUniqueName);
                Information.Add($"Renamed storagefile {storageFile.Name}\n{storageFile.FolderRelativeId}");

                //await fileInformation.RenameAsync("1.jpg", NameCollisionOption.GenerateUniqueName);
                Information.Add($"Renamed FileInformation result {fileInformation.Name}\n{fileInformation.FolderRelativeId}");
            }
            catch (Exception ex)
            {
                Information.Add($"{ex.Message}\n" +
                    $"{fileInformation.Name}\n{fileInformation.FolderRelativeId}");
            }
        }
    }

    private void ClearButton_Click(object sender, RoutedEventArgs e)
    {
        Information.Clear();
    }
}

И XAML

<Page
x:Class="Virtualization.Scenario5"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Virtualization"
xmlns:ba="using:Windows.Storage.BulkAccess"    
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="48"/>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.ColumnSpan="1" Orientation="Horizontal"
                BorderBrush="Blue" BorderThickness="0,0,2,2">
        <AppBarButton Icon="Folder"
                      LabelPosition="Collapsed"
                      Click="FolderPickerButton_Click"/>
        <AppBarButton Icon="Rename"
                      LabelPosition="Collapsed"
                      Click="RenameButton_Click"/>
        <AppBarButton Icon="Clear"
                      LabelPosition="Collapsed" Label="Select Folder"
                      Click="ClearButton_Click"/>
    </StackPanel>
    <ListView x:Name="itemCollectionGridView" Grid.Row="1" Grid.Column="1"
              ItemsSource="{x:Bind ItemCollectionView, Mode=OneWay}" IsItemClickEnabled="True"
              ItemClick="ListView_ItemClick">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="ba:FileInformation">
                <StackPanel MinHeight="100">
                    <TextBlock Text="{Binding Name}" TextWrapping="WrapWholeWords"/>
                    <TextBlock Text="{Binding Path}" TextWrapping="WrapWholeWords"/>
                    <TextBlock Text="{Binding FolderRelativeId}" TextWrapping="WrapWholeWords"/>

                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <ListView Grid.Row="1" Grid.Column="2" ItemsSource="{x:Bind Information, Mode=OneWay}">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel ItemsUpdatingScrollMode="KeepLastItemInView" />
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

</Grid>


person Octopus    schedule 13.01.2020    source источник


Ответы (1)


FileInformation.RenameAsync() разрывает привязку по сравнению с StorageFile.RenameAsync()

Проблема выглядит как ошибка, и я мог воспроизвести эту проблему. В общем, мы часто используем storageFile.RenameAsync способ переименовать файл. Пожалуйста, попробуйте использовать storageFile.RenameAsync для замены, и вы можете сообщить об этом с помощью приложения концентратора обратной связи Windows.

Дополнительный вопрос будет заключаться в том, как сохранить выбранный элемент таким же после переименования файла, потому что часто (не всегда!) выбранный элемент теряется (index=-1), вероятно, из-за того, что ICollectionview был сброшен из-за уведомлений от файловая система.

Создание нового индекса для файла займет некоторое время, а затем уведомите ICollectionview, лучший способ — добавить папку в FutureAccessList и заново создайте файл ICollectionview.

person Nico Zhu - MSFT    schedule 14.01.2020
comment
Спасибо, я отправил отзыв через приложение-концентратор, но я не уверен, что это для разработчиков, оно больше подходит для пользователей Windows 10. Есть ли для этого репозиторий github, где я могу продолжить? Я попробую ваше предложение для будущего списка доступа. - person Octopus; 14.01.2020
comment
Пожалуйста, проверьте нижнюю часть документа , и у него есть Feedback порт, вы можете опубликовать там. - person Nico Zhu - MSFT; 14.01.2020
comment
Будущий список доступа не работает, потому что основная проблема заключается в том, что независимо от того, что вы делаете для создания ICollectionView, важным фактором является время его создания, и если вы создаете его слишком рано (до выполнения индексации), вы получаете ту же проблему (фокус потеряно из-за обновления представления). - person Octopus; 14.01.2020