ユーザーがフィルター文字列をテキストボックスに入力しているときにリストボックスがリアルタイムでフィルター処理される機能を実装すると、パフォーマンスに問題が発生します。私が作成しようとしている機能は、WP7の通話履歴検索に似ています。
これをテストするための簡単なプロジェクトを作成し、以下の重要な部分をコピーして貼り付けました。基本的に私はTextBoxを持っており、ユーザーはリストボックスにバインドされたデータをフィルタリングするために使用される文字列を書き込むことになっています。このフィルタリングは、フィルターボタンなどをタップした後ではなく、リアルタイムで実行する必要があります。
ListBoxは、ObservableCollectionをソースとして使用するCollectionViewSourceにバインドされています。テキストボックスに何かが入力されると、その値はビューモデルのプロパティに即座にデータバインドされます。ビューモデルプロパティのセッターは、CollectionViewSourceのフィルタリングを起動し、ListBoxのコンテンツを更新します。
私が行っている実際のプロジェクトでは、ListBoxに100個ほどのアイテムを含めることができます。
関連するXAMLは次のとおりです。
<TextBox TextChanged="TextBox_TextChanged" Text="{Binding FilterString, Mode=TwoWay, UpdateSourceTrigger=Explicit}"></TextBox>
<ListBox ItemsSource="{Binding ItemsListCVS.View, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Prop1, Mode=TwoWay}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModelへのインスタントバインディングをトリガーするためのコードビハインド-プロパティ:
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
var textBox = sender as TextBox;
// Update the binding source
BindingExpression bindingExpr = textBox.GetBindingExpression(TextBox.TextProperty);
bindingExpr.UpdateSource();
}
ViewModel:
private ObservableCollection<AnItem> _itemsList = new ObservableCollection<AnItem>();
private CollectionViewSource _itemsListCvs = new CollectionViewSource();
public ObservableCollection<AnItem> ItemsList
{
get
{
return _itemsList;
}
set
{
_itemsList = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemsListPropertyName);
}
}
public string FilterString
{
get
{
return _filterString;
}
set
{
if (_filterString == value)
{
return;
}
_filterString = value;
// Update bindings, no broadcast
RaisePropertyChanged(FilterStringPropertyName);
this.Filter();
}
}
public CollectionViewSource ItemsListCVS
{
get
{
return _itemsListCvs;
}
set
{
if (_itemsListCvs == value)
{
return;
}
_itemsListCvs = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemListPropertyName);
}
}
public MainViewModel()
{
var items = Builder<AnItem>.CreateListOfSize(100).Build();
this.ItemsList = new ObservableCollection<AnItem>(items);
this.ItemsListCVS.Source = this.ItemsList;
}
private void Filter()
{
this.ItemsListCVS.View.Filter = r =>
{
if (r == null) return true;
return ((AnItem)r).Prop1.ToString().ToLowerInvariant().Contains(FilterString);
};
}
リストにデータバインドされるAnItemクラス:
public class AnItem
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public string Prop4 { get; set; }
public string Prop5 { get; set; }
}
質問:
すべて正常に機能していますが、TextBoxへの書き込みとListBoxの更新の間には恐ろしい遅れがあります。
私は単にそれを間違っているのですか?もしそうなら、私は私のアプローチをどのように変えるべきですか?これは非常に一般的な要件だと思うので、おそらくそれに対するいくつかの素晴らしい解決策があります。