I am trying to bind a few WPF controls to a trie. I have created a wrapper that exposes the methods in the trie as properties. I am trying to filter my listbox with the matches returned by the trie. When there are too many matches I don’t want to enumerate all the matches since that takes a few seconds, and is totally unnecessary. I have solved this declaratively by using an MultiBinding that takes both the lazy-loading Matches and then NumMatches which is much faster to calculate. The MultiBindingConverter first evaluates the NumMatches sent into the function. If this is smaller than a certain value, only then does it attempt to evaluate the Matches property.
The problem is that when Matches changes, so does NumMatches. My converter is thus called two times, once when Matches changes and one when NumMatches changes. This would have been fine, even though perhaps a bit bug-prone, if WPF didn’t (apparently) cache data, because even though both properties have changed at the time, the fresh value isn’t fetched but a cached one used. The converter will be called with mismatching arguments, count will be for example 100 (safe to enumerate) but the enumeration will cause a million or two values to be accessed, since the fresh Matches list is not fetched until the PropertyChanged for Matches is called (they are updated in the same wrapper property, remember) How should I solve this? I want to solve it declaratively.
My ideas are following:
1) Find a better way to solve the paging, not using MultiBinding
2) Disable caching
Which one of these are the most plausible? How much have I violated WPF plumbing here?
This is some of my XAML:
<ListBox SelectionChanged="ListBox_SelectionChanged" Height="250">
<ListBox.ItemsSource>
<MultiBinding>
<MultiBinding.Converter>
<mine:PagingMultiValue> </mine:PagingMultiValue>
</MultiBinding.Converter>
<Binding Path="Matches"></Binding>
<Binding Path="NumMatches"></Binding>
</MultiBinding>
</ListBox.ItemsSource>
</ListBox>
UPDATE
Here is some of my TrieWrapper:
public string Text
{
get { return text; }
set
{
if (string.Equals(text, value))
return;
text = value;
OnMatchesChanged();
OnNumMatchesChanged();
OnTextChanged();
}
}
Even though I have control over the source for my Trie datastructure, I would prefer not altering it by exposing the current matches through a property. This is why I have this “hack” instead. When OnMatchesChanged() is called, it is updated with the old value for NumMatches. Then directly afterwards it is updated with the correct value for both. If I change the order of the updates the only thing that changes is when exactly the wrong enumeration occurs, either when I add to the Text search string or remove from it.
Thanks!
Unless you have some good reason to do this another way, you should use the fact that ListBox has built in UI Virtualization and follow something along the lines of the following article in order to build the data virtualization.
http://blogs.microsoft.co.il/blogs/tomershamam/archive/2009/10/01/ui-virtualization-vs-data-virtualization-part-2.aspx
(UI Virtualization link from MSDN)
http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel.aspx
Your current solution is a data virtualization solution (being lazy evaluated) but it is not done in an way in which WPF can easily interface with it. By using a custom ICollectionView implementation (the article derives ListCollectionView for convenience) you don’t have to do anything to the ListBox except set the ItemsSource to be a CollectionViewSource object bound to your dataset. This keeps the XAML completely readable (compared to using multi bindings etc) and improves performance since it wont be using your converter to work out what elements are required to be rendered, WPF will use it’s own methods to determine what should be on screen or not.