我正在尝试使用Microsoft.Windows.APICodePack.Shell.ShellContainer作为ListBox的ItemsSource,通过ListBox.ItemTemplate显示每个孩子(ShellObject(的缩略图和名称。当 ShellContainer 引用一个非常大的文件夹(比如一千多个文件(时,就会出现问题:如果我简单地声明
ShellContainer source=ShellObject.FromParsingName(@"C:MyFolder") as ShellContainer:
listBox1.ItemsSource=source.GetEnumerator();
它将UI冻结两到三分钟,然后一次显示ShellContainer的所有内容。我发现的最佳解决方法是创建一个这样的异步填充器类
class AsyncSourceFiller
{
private ObservableCollection<ShellObject> source;
private ShellContainer path;
private Control parent;
private ShellObject item;
public AsyncSourceFiller(ObservableCollection<ShellObject> source, ShellContainer path, Control parent)
{
this.source = source;
this.path = path;
this.parent = parent;
}
public void Fill()
{
foreach (ShellObject obj in path)
{
item = obj;
parent.Dispatcher.Invoke(new Action(Add));
Thread.Sleep(4);
}
}
private void Add()
{
source.Add(item);
}
}
然后通过以下方式调用它
ObservableCollection<ShellObject> source = new ObservableCollection<ShellObject>();
listBox1.ItemsSource = source;
ShellContainer path = ShellObject.FromParsingName(@"C:MyFolder"):
AsyncSourceFiller filler = new AsyncSourceFiller(source, path, this);
Thread th = new Thread(filler.Fill);
th.IsBackground = true;
th.Start();
这比以前的方式花费更多时间,但不会冻结 UI 并立即开始显示某些内容。有没有更好的方法来获得类似的行为,可能缩短总操作时间?
加载后台线程中的所有数据,完成后更新列表框的来源。 并且不要忘记在列表框中将虚拟化设置为 true。
耗时的操作是枚举您的ShellContainer
并创建数千个ShellObject
。 ListBox
不是问题。
当您将IEnumerable
设置为ItemControl
的源时,我认为它会在第一次显示时从枚举器中创建和内部列表,这就是为什么它在显示任何内容之前冻结两分钟的原因。
您在这里没有太多选择:
-
为自己创建一个
List<ShellObject>
并将其设置为我们ListBox
的来源。它不是更快,但至少您可以向用户显示"正在加载,请稍候"消息。 -
在另一个线程中加载列表(就像您一样(并在加载项目时显示项目。这有点奇怪,因为列表会随着时间的推移而"增长"。
-
找到一种方法将
ShellContainer
包装在实现IList
的类中。为此,您需要能够在ShellContainer
类中的给定索引处获取项目(我不知道"Windows API 代码包"(。如果您将其用作ListBox
源并启用了虚拟化,则只会加载显示的ShellObject
,并且它将快速流畅。