与 StorageFile.RenameAsync() 相比,FileInformation.RenameAsync() 打破了绑定
FileInformation.RenameAsync() breaks binding compared to StorageFile.RenameAsync()
当单击的项目被重命名时,是什么导致以下代码破坏 FileInformation
DataTemplate
和 ListView
的绑定?
然后,如何正确通知 FileInformation
已重命名的视图?
考虑一个包含 2 张图像的文件夹,1.jpg 和 2.jpg。此文件夹由 Windows 10 1909 索引,UWP 应用程序版本为 1903。
在用户 select 编辑文件夹后,我们创建一个 FileInformationFactory
并检索 VirtualizedFiles
对象并将其设置为 CollectionViewSource
的源。后者的视图填充了一个 ICollectionView
,它是单向绑定到 ListView 的 ItemsSource。
我们在列表视图中显示 FileInformation
的每个文件 Name
和文件 FolderRelativeId
。
重命名按钮采用 ListView.Selecteditem 并将其重命名为 1.jpg,并带有 GenerateUniqueName
.
选项
手边有两种方法。
1) 给定代码最自然的一种是使用
FileInformation.RenameAsync()
,因为 selected 项目已经是 FileInformation
。
2) 从folder.GetFileAsync获取Storagefile,其中name参数由FileInformation.Name给出,然后调用StorageFile.RenameAsync.
在这两种方法中,当我们重命名文件时,列表视图会按预期更新。然而,这不会持续很长时间,因为我一直在重命名,即使我给了文件重命名的时间。事实上,在第一种情况下
我可以重命名该文件,但是,当我不断重命名时,在某些不清楚的地方,列表视图似乎停留在以前重命名的名称上并且与 FolderRelativeId
不匹配。例如,文件名显示为“1 (2).jpg”,而 FolderRelativeId 以“1 (3).jpg.jpg”结尾。 listview 无法将项目识别为 ListView 的一部分,因为我不能再 select 并且重命名会引发捕获的异常。
这个问题在场景2中好像没有出现,为什么?我如何使用方案 1(即坚持 FileInformation
进行重命名)并保持通知有效以更新 UI 而没有此错误?
另一个问题是如何在文件重命名后保持 selected 项目不变,因为经常(不总是!)selected 项目丢失(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>
FileInformation.RenameAsync() breaks binding compared to StorageFile.RenameAsync()
这个问题看起来像一个错误,我可以重现这个问题,一般我们经常storageFile.RenameAsync
方法来重命名文件。请尝试使用 storageFile.RenameAsync 替换,您可以使用 windows 反馈中心应用程序报告此问题。
An additional question would be how to keep the selected item the same after the file has been renamed, because often (not always!) the selected item is lost (index=-1), probably because the ICollectionview has been reset owing to notifications from the file system.
为文件建立新索引需要一些时间然后通知 ICollectionview
,更好的方法是将文件夹添加到 FutureAccessList
,然后重新创建 [=11] =].
当单击的项目被重命名时,是什么导致以下代码破坏 FileInformation
DataTemplate
和 ListView
的绑定?
然后,如何正确通知 FileInformation
已重命名的视图?
考虑一个包含 2 张图像的文件夹,1.jpg 和 2.jpg。此文件夹由 Windows 10 1909 索引,UWP 应用程序版本为 1903。
在用户 select 编辑文件夹后,我们创建一个 FileInformationFactory
并检索 VirtualizedFiles
对象并将其设置为 CollectionViewSource
的源。后者的视图填充了一个 ICollectionView
,它是单向绑定到 ListView 的 ItemsSource。
我们在列表视图中显示 FileInformation
的每个文件 Name
和文件 FolderRelativeId
。
重命名按钮采用 ListView.Selecteditem 并将其重命名为 1.jpg,并带有 GenerateUniqueName
.
手边有两种方法。
1) 给定代码最自然的一种是使用
FileInformation.RenameAsync()
,因为 selected 项目已经是 FileInformation
。
2) 从folder.GetFileAsync获取Storagefile,其中name参数由FileInformation.Name给出,然后调用StorageFile.RenameAsync.
在这两种方法中,当我们重命名文件时,列表视图会按预期更新。然而,这不会持续很长时间,因为我一直在重命名,即使我给了文件重命名的时间。事实上,在第一种情况下
我可以重命名该文件,但是,当我不断重命名时,在某些不清楚的地方,列表视图似乎停留在以前重命名的名称上并且与 FolderRelativeId
不匹配。例如,文件名显示为“1 (2).jpg”,而 FolderRelativeId 以“1 (3).jpg.jpg”结尾。 listview 无法将项目识别为 ListView 的一部分,因为我不能再 select 并且重命名会引发捕获的异常。
这个问题在场景2中好像没有出现,为什么?我如何使用方案 1(即坚持 FileInformation
进行重命名)并保持通知有效以更新 UI 而没有此错误?
另一个问题是如何在文件重命名后保持 selected 项目不变,因为经常(不总是!)selected 项目丢失(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>
FileInformation.RenameAsync() breaks binding compared to StorageFile.RenameAsync()
这个问题看起来像一个错误,我可以重现这个问题,一般我们经常storageFile.RenameAsync
方法来重命名文件。请尝试使用 storageFile.RenameAsync 替换,您可以使用 windows 反馈中心应用程序报告此问题。
An additional question would be how to keep the selected item the same after the file has been renamed, because often (not always!) the selected item is lost (index=-1), probably because the ICollectionview has been reset owing to notifications from the file system.
为文件建立新索引需要一些时间然后通知 ICollectionview
,更好的方法是将文件夹添加到 FutureAccessList
,然后重新创建 [=11] =].