Что заставляет следующий код нарушать привязку FileInformation
DataTemplate
к ListView
, когда выбранный элемент переименовывается? А затем, как получить правильное уведомление о том, что FileInformation
был переименован?
Рассмотрим папку с 2 изображениями, 1.jpg и 2.jpg. Эта папка индексируется Windows 10 1909, а приложение UWP - версия 1903.
После того, как пользователь выбрал папку, мы создаем FileInformationFactory
, извлекаем объект VirtualizedFiles
и устанавливаем его в качестве источника CollectionViewSource
. Представление последнего заполняет ICollectionView
, который OneWay связан с Источником Items ListView.
Мы отображаем в представлении списка каждый файл Name
и файл FolderRelativeId
из FileInformation
. Кнопка переименования берет ListView.Selecteditem и переименовывает его в 1.jpg с опцией GenerateUniqueName
.
. Есть два метода под рукой.
1) Самый естественный из приведенных код должен использовать FileInformation.RenameAsync()
, поскольку выбранный элемент уже является FileInformation
.
2) Получить файл хранилища из папки. GetFileAsyn c, где параметр имени задается в FileInformation.Name и затем вызовите StorageFile.RenameAsyn c.
. В обоих методах при переименовании файла представление списка обновляется, как и ожидалось. Однако это не длится долго, так как я продолжаю переименовывать, даже если я даю время для переименования файла. Действительно, в первом сценарии я могу переименовать файл, но, поскольку я продолжаю переименовывать, в какой-то непонятной точке список выглядит застрявшим в предыдущем переименованном имени и не соответствует FolderRelativeId
. Например, имя файла отображается как «1 (2) .jpg», а FoldeRelativeId оканчивается на «1 (3) .jpg.jpg». Представление списка не распознает элемент как часть ListView, так как я больше не могу его выбрать, и переименование вызывает перехваченное исключение.
Эта проблема, похоже, не возникает в сценарии 2, почему? Как я могу использовать сценарий 1 (т. Е. Придерживаться FileInformation
для переименования) и поддерживать уведомления в актуальном состоянии для обновления интерфейса без этой ошибки?
Еще один вопрос - как сохранить выбранный элемент таким же после файла был переименован, потому что часто (не всегда!) выбранный элемент теряется (индекс = -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>